博客和github链接
丁枢桐的博客链接
https://www.cnblogs.com/wadx2019/p/11675166.html
本作业的博客链接
https://www.cnblogs.com/zhanglingxin/p/11679788.html
我所fork的同学的仓库地址
https://github.com/wadx2019/secret_job
具体分工
丁枢桐同学 负责AI代码
张凌昕同学 负责前端UI的设计和实现,前端设计
这里是部分的前端界面,因为我在设计页面时,大部分采用动态背景,所以展示出来的效果可能不是很明显
PSP表
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
·Planning | 计划 | 90 | 110 |
·Estimate | 估计这个任务需要多少时间 | 15 | 18 |
·Development | 开发 | 700 | 1050 |
·Analysis | 需求分析 (包括学习新技术) | 200 | 220 |
·Design Spec | 生成设计文档 | 60 | 75 |
·Design Review | 设计复审 | 40 | 55 |
·Coding Standard | 代码规范(为开发制定合适的规范) | 25 | 30 |
·Design | 具体设计 | 100 | 230 |
· Coding | 具体编码 | 200 | 460 |
·Code Review | · 代码复审 | 30 | 100 |
· Test | 测试(自我测试,修改代码,提交修改) | 120 | 220 |
·Reporting | 报告 | 40 | 50 |
· Test Repor | · 测试报告 | 10 | 15 |
·Size Measurement | 计算工作量 | 20 | 25 |
·Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 20 | 30 |
. | · 合计 | 1670 | 2688 |
接口分析
我根据永福给的几个前端的接口,还有get,post参数,写了接永福服务器的接口写了接口,然后进行数据的发送和获取数据并加以展示,将他的所给的牌将牌给储存下来。再和后端交接,把后端所给的字符串交给永福的服务器。我选择我连的一部分接口来详细描述。
注册接口
接入注册接口的代码,根据用户所填写的用户名,学号,密码,教务处 ,密码,将所填写的内容传递到服务器,并且根据服务器返回的内容,来提醒用户。
function register() {
var reg_data = ($('#reg_form').serializeJson());
if (reg_data.username == undefined || reg_data.password == undefined) {
alert("用户名或密码不能为空");
return;
}
$.ajax({
type: "POST",
dataType: "json",
url: "https://api.shisanshui.rtxux.xyz/auth/register",
data: JSON.stringify(reg_data), //提交的数据
contentType: "application/json;charset-UTF-8",
success: function (result) { //todo
console.log(result); //打印服务端返回的数据(调试用)
if (result.status == 0) {
alert("注册成功");
window.location.href = '登入.html'
};
},
error: function (jqXHR, textStatus, errorThrownt) {
var responseText = jQuery.parseJSON(jqXHR.responseText);
if (responseText.status == 1001) {
alert("用户名已被使用");
};
if (responseText.status == 1002) {
alert("学号已绑定");
};
if (responseText.status == 1003) {
alert("教务处认证失败");
}
}
});
}
###接入登录接口的代码
function begin() {
var login_data = ($('#login_form').serializeJson());
if (login_data.username == undefined || login_data.password == undefined) {
alert("用户名或密码不能为空");
return;
}
$.ajax({
type: "POST",
dataType: "json",
url: "https://api.shisanshui.rtxux.xyz/auth/login",
data: JSON.stringify(login_data), //提交的数据
contentType: "application/json;charset-UTF-8",
success: function (result) { //todo
console.log(result); //打印服务端返回的数据(调试用)
if (result.status == 0) {
token=result.data.token;
user_id=result.data.user_id;
username=login_data.username
console.log(token);
localStorage.setItem("token1",token);
localStorage.setItem("user_id1",user_id);
localStorage.setItem("username1",username);
alert("登录成功");
window.location.href = '游戏大厅.html'
};
},
error: function (res) {
// $("#login_form").reset();
alert("用户名或密码错误");
}
});
}
function logout(){
token=localStorage.getItem("temp");
$.ajax({
type: "POST",
url: "https://api.shisanshui.rtxux.xyz/auth/logout",
beforeSend: function(xhr) {
xhr.setRequestHeader("X-Auth-Token",token);
},
success: function (result) { //todo
console.log(result);
console.log(token);
alert("注销成功");
window.location.href = '点入.html'
},
error: function (res) {
alert("注销失败");
}
});
}
接收到牌的接口
接收到牌的时候,用一个字符串数组储存起来,并且交给AI,AI根据所穿的字符串数组来给出牌来交给服务器。
排行榜的接口
在排行榜的接口中, 我先接入服务器的接口使用的是get 方式,并根据服务器所返回的数据现场生成一个表格。
接口分析的小结
这次连接接口的过程中,学习到了ajax,并且了解了get和post两种方式的不同之处。GET - 从指定的资源请求数据。POST - 向指定的资源提交要被处理的数据
解题思路描述与设计实现说明
网络接口的使用
使用ajax进行http请求操作,样例如下:
代码组织与内部实现设计(类图)
说明算法的关键与关键实现部分流程图
算法关键在于三墩回溯中剪枝函数的设计以及分数矩阵评分算法的设计
流程图如下:
关键代码解释
重要代码片段
1.分数矩阵
var pattern_points = [
// Tr FH Qu SF RF
[1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1], // front
[1, 1, 1, 1, 1, 1, 1, 2, 8, 10, 20], // center
[1, 1, 1, 1, 1, 1, 1, 1, 4, 5, 10], // back
];
var junk_value = [
//2 3 4 5 6 7 8 9 T J Q K A
[ 0, 0, 0, 0, 0, 1, 1, 2, 2, 4, 7, 15, 34],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
];
var junk_value0 = [
//2 3 4 5 6 7 8 9 T J Q K A
[ 0, 20, 20, 21, 21, 22, 23, 24, 26, 30, 34, 42, 0], //A?
[ 0, 9, 9, 10, 10, 11, 11, 12, 13, 15, 18, 0, 0], //K?
[ 0, 5, 5, 5, 6, 6, 7, 7, 8, 8, 0, 0, 0] //Q?
];
var one_pair_value = [
//2 3 4 5 6 7 8 9 T J Q K A
[45, 47, 49, 51, 53, 56, 60, 64, 68, 73, 81, 89, 97],
[ 2, 3, 3, 4, 5, 6, 8, 10, 12, 15, 18, 24, 33],
[ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3]
];
var one_pair_value0 = [
//2 3 4 5 6 7 8 9 T J Q K A
[96, 96, 96, 96, 96, 96, 97, 97, 97, 97, 98, 98, 0], //A?
[86, 86, 86, 86, 86, 87, 87, 88, 89, 89, 90, 0, 91], //K?
[76, 76, 77, 77, 77, 78, 78, 78, 79, 80, 0, 82, 83] //Q?
];
var one_pair_value1 = [
//2 3 4 5 6 7 8 9 T J Q K A
[ 0, 0, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 0], //A?
[ 0, 0, 23, 23, 23, 23, 23, 24, 25, 25, 26, 0, 27], //K?
[ 0, 0, 17, 17, 18, 18, 18, 19, 19, 20, 0, 21, 22] //Q?
];
var two_pair_value = [
//2 3 4 5 6 7 8 9 T J Q K A
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 37, 37, 39, 41, 43, 46, 49, 54, 58, 62, 64, 64],
[ 0, 3, 3, 4, 4, 5, 7, 8, 10, 11, 13, 14, 14]
];
var continuous_value = [
//2 3 4 5 6 7 8 9 T J Q K A
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 37, 37, 39, 41, 43, 46, 49, 54, 58, 62, 64, 64],
[ 0, 3, 3, 4, 4, 5, 7, 8, 10, 11, 13, 14, 14]
];
var triple_value = [
//2 3 4 5 6 7 8 9 T J Q K A
[99, 99, 99, 99, 99,100,100,100,100,100,100,100,100],
[63, 66, 69, 71, 72, 72, 74, 74, 74, 75, 75, 75, 76],
[12, 13, 13, 15, 16, 16, 16, 16, 16, 14, 15, 15, 15]
];
var straight_value = [
//2 3 4 5 6 7 8 9 T J Q K A
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 77, 79, 81, 83, 85, 87, 88, 89, 91, 92],
[ 0, 0, 0, 16, 18, 20, 22, 24, 26, 28, 31, 34, 37]
];
var flush_value = [
//2 3 4 5 6 7 8 9 T J Q K A
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 93, 92, 92, 93, 94, 95, 97, 98],
[ 0, 0, 0, 0, 0, 35, 37, 38, 38, 40, 44, 50, 61]
];
var flush_value2 = [
//2 3 4 5 6 7 8 9 T J Q K A
[ 0, 0, 0, 0, 53, 54, 55, 56, 57, 59, 62, 65, 0], //A?
[ 0, 0, 0, 44, 44, 45, 46, 47, 48, 49, 52, 0, 0], //K?
[ 0, 0, 0, 41, 41, 41, 42, 42, 44, 45, 0, 0, 0] //Q?
];
var full_value = [
//2 3 4 5 6 7 8 9 T J Q K A
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[98, 99, 99, 99, 99, 99, 99, 99,100,100,100,100,100],
[65, 66, 69, 71, 73, 75, 78, 80, 82, 85, 88, 91, 94],
];
var quadruple_value = [
//2 3 4 5 6 7 8 9 T J Q K A
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[100,100,100,100,100,100,100,100,100,100,100,100,100],
[ 94, 94, 95, 95, 95, 96, 97, 97, 97, 97, 98, 98, 98]
];
三墩回溯
optimize_back() {
for (var b1 = 0; b1 < 9; ++b1) {
for (var b2 = b1 + 1; b2 < 10; ++b2) {
for (var b3 = b2 + 1; b3 < 11; ++b3) {
for (var b4 = b3 + 1; b4 < 12; ++b4) {
for (var b5 = b4 + 1; b5 < 13; ++b5) {
this.back = new WaveEvaluator(
[this.hand[b1], this.hand[b2], this.hand[b3],
this.hand[b4], this.hand[b5]], Back);
if (this.back.pattern == Junk) continue;
if (200 + this.back.value < this.threshold) continue;
var bits = (1<<b1) + (1<<b2) + (1<<b3) + (1<<b4) + (1<<b5);
this.optimize_center(bits);
}
}
}
}
}
}
optimize_center(bits) {
out:
for (var c1 = 0; c1 < 9; ++c1) {
if ((1 << c1) & bits) continue;
for (var c2 = c1 + 1; c2 < 10; ++c2) {
if ((1 << c2) & bits) continue;
for (var c3 = c2 + 1; c3 < 11; ++c3) {
if ((1 << c3) & bits) continue;
for (var c4 = c3 + 1; c4 < 12; ++c4) {
if ((1 << c4) & bits) continue;
for (var c5 = c4 + 1; c5 < 13; ++c5) {
if ((1 << c5) & bits) continue;
this.center = new WaveEvaluator(
[this.hand[c1], this.hand[c2], this.hand[c3],
this.hand[c4], this.hand[c5]], Center);
if (this.back.is_smaller_than(this.center)) break out;
if (100 + this.center.value + this.back.value < this.threshold)
continue;
var center_bits = (1<<c1) + (1<<c2) + (1<<c3) + (1<<c4) + (1<<c5);
this.optimize_front(bits + center_bits);
}
}
}
}
}
}
optimize_front(bits) {
out:
for (var f1 = 0; f1 < 11; ++f1) {
if ((1 << f1) & bits) continue;
for (var f2 = f1 + 1; f2 < 12; ++f2) {
if ((1 << f2) & bits) continue;
for (var f3 = f2 + 1; f3 < 13; ++f3) {
if ((1 << f3) & bits) continue;
this.front = new WaveEvaluator(
[this.hand[f1], this.hand[f2], this.hand[f3]], Front);
if (this.center.is_smaller_than(this.front)) break out;
var sum_value = expected_points(this.front, this.center, this.back);
var duplicate = false;
for (var pos = 0; pos < this.top_waves.length; ++pos) {
if (sum_value < this.top_waves[pos][0]) break;
if (sum_value == this.top_waves[pos][0]) {
duplicate = true;
break;
}
}
if (duplicate) continue;
if (pos == 0 && this.top_waves.length == this.handicap + 1) continue;
this.top_waves.splice(pos, 0, [sum_value].concat(
this.front.wave, this.center.wave, this.back.wave));
if (this.top_waves.length > this.handicap + 1)
this.top_waves.shift();
this.threshold = this.top_waves[0][0];
}
}
}
}
optimize_back() {
for (var b1 = 0; b1 < 9; ++b1) {
for (var b2 = b1 + 1; b2 < 10; ++b2) {
for (var b3 = b2 + 1; b3 < 11; ++b3) {
for (var b4 = b3 + 1; b4 < 12; ++b4) {
for (var b5 = b4 + 1; b5 < 13; ++b5) {
this.back = new WaveEvaluator(
[this.hand[b1], this.hand[b2], this.hand[b3],
this.hand[b4], this.hand[b5]], Back);
if (this.back.pattern == Junk) continue;
if (200 + this.back.value < this.threshold) continue;
var bits = (1<<b1) + (1<<b2) + (1<<b3) + (1<<b4) + (1<<b5);
this.optimize_center(bits);
}
}
}
}
}
}
optimize_center(bits) {
out:
for (var c1 = 0; c1 < 9; ++c1) {
if ((1 << c1) & bits) continue;
for (var c2 = c1 + 1; c2 < 10; ++c2) {
if ((1 << c2) & bits) continue;
for (var c3 = c2 + 1; c3 < 11; ++c3) {
if ((1 << c3) & bits) continue;
for (var c4 = c3 + 1; c4 < 12; ++c4) {
if ((1 << c4) & bits) continue;
for (var c5 = c4 + 1; c5 < 13; ++c5) {
if ((1 << c5) & bits) continue;
this.center = new WaveEvaluator(
[this.hand[c1], this.hand[c2], this.hand[c3],
this.hand[c4], this.hand[c5]], Center);
if (this.back.is_smaller_than(this.center)) break out;
if (100 + this.center.value + this.back.value < this.threshold)
continue;
var center_bits = (1<<c1) + (1<<c2) + (1<<c3) + (1<<c4) + (1<<c5);
this.optimize_front(bits + center_bits);
}
}
}
}
}
}
optimize_front(bits) {
out:
for (var f1 = 0; f1 < 11; ++f1) {
if ((1 << f1) & bits) continue;
for (var f2 = f1 + 1; f2 < 12; ++f2) {
if ((1 << f2) & bits) continue;
for (var f3 = f2 + 1; f3 < 13; ++f3) {
if ((1 << f3) & bits) continue;
this.front = new WaveEvaluator(
[this.hand[f1], this.hand[f2], this.hand[f3]], Front);
if (this.center.is_smaller_than(this.front)) break out;
var sum_value = expected_points(this.front, this.center, this.back);
var duplicate = false;
for (var pos = 0; pos < this.top_waves.length; ++pos) {
if (sum_value < this.top_waves[pos][0]) break;
if (sum_value == this.top_waves[pos][0]) {
duplicate = true;
break;
}
}
if (duplicate) continue;
if (pos == 0 && this.top_waves.length == this.handicap + 1) continue;
this.top_waves.splice(pos, 0, [sum_value].concat(
this.front.wave, this.center.wave, this.back.wave));
if (this.top_waves.length > this.handicap + 1)
this.top_waves.shift();
this.threshold = this.top_waves[0][0];
}
}
}
}
性能分析与改进
改进思路
使用评分矩阵进行评分,并且依据评分进行剪枝,减少回溯的时间。
性能分析图
消耗最大函数
本AI算法主要是用回溯法找到最优解,所以时间主要花在三墩回溯上。
单元测试
单元测试代码
function shuffle(array) {
for (var i = 0; i < array.length; ++i) {
var pos = Math.floor(Math.random() * (array.length - i));
var item = array[pos];
array[pos] = array[array.length - 1 - i];
array[array.length - 1 - i] = item;
}
return array;
}
console.time("run time");
for (var i =0 ; i<=1000 ;++i )
{
tmp=optimize_hand(shuffle([...Array(52).keys()]));
}
console.timeEnd("run time");
测试代码说明
随机构造一千种出牌情况,在nodejs上进行测试,输出运行时间。
构造测试数据的思路
生成随机数组,进行随机评测。
Github代码签入记录
遇到的代码模块异常或结对困难及解决方法
遇到的问题
在这次写前端的过程中,因为之前从来没有接触过前端,所以一开始很多地方都不懂,并且很多东西都不知道是什么,有一次一个很简单的东西,我学了两天。。。。就很难受,这个知识点特别特别特别的简单,但是居然花了两天的时间真的是特别无语。并且对前端的很多东西不是太了解,有些可以很快实现的东西拖了比较久的时间。并且在前端实现过程中一些排版的问题有些难受。并且在前端设计的时候,有些特效并不能做到,于是我看了很多教程。并且看了很多的前端设计的资料。
做过哪些尝试
在这次问题频出的前端实现中,很多遇到的问题都通过看网络上的一些博客还有一些教程解决了,还有的实在不行的就找学长还有学弟们请教。虽然过程比较艰辛但是都解决了
有何收获
在这次的结对编程作业中,我学到了前端的很多知识,接触了以前从来没有接触过的东西,并且也做出来了。感觉收获还是很大的。
评价你的队友
丁枢桐同学值得学习的地方
丁枢桐同学,做事情认真,严谨。他的代码能力很强,并且对任何事情要求完美,在这样的队友的带领下,对每一个页面的建议特别多,虽然很辛苦,但是得到了很多的锻炼,自身的能力也提高了不少。合作过程还是比较愉快的,谢谢丁枢桐同学给我很多的锻炼机会。
丁枢桐同学需要改进的地方
丁枢桐同学,提的一些意见的可行性希望能更高一点。
学习进度条
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 4.5 | 4.5 | 学会了Axure rp 还有墨刀的使用方法 |
2 | 100 | 100 | 30 | 34.5 | 大致学会了html,css,js |
3 | 290 | 390 | 40 | 74.5 | 运用了html,css,js并写出了很多页面 |
4 | 350 | 740 | 45 | 119.5 | 学会了ajax,以及运用Js,css写特效 |