第二次结对编程作业
一、链接
2、本作业博客的链接
3、Github地址
二、具体分工
杨欢:前端qt界面,以及网络接口
梅恒权:后端AI出牌算法
三、PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(min) | 实际耗时(min) |
---|---|---|---|
Planning | 计划 | 60 | 60 |
Estimate | 估计这个任务需要多少时间 | 60 | 60 |
Development | 开发 | 1200 | 1500 |
Analysis | 需求分析(包括学习新技术) | 400 | 600 |
Design Spec | 生成设计文档 | 20 | 10 |
Design Review | 设计复审 | 30 | 60 |
Coding Standard | 代码规范(为开发制定合适的规范) | 30 | 40 |
Design | 具体设计 | 400 | 600 |
Coding | 具体编码 | 1200 | 1500 |
Code Review | 代码复审 | 60 | 120 |
Test | 测试(自我测试,修改,提交修改) | 60 | 240 |
Reporting | 报告 | 60 | 60 |
Test Report | 测试报告 | 60 | 60 |
Size Measurement | 计算工作量 | 20 | 10 |
Postmortem & Process Improvement Plan | 事后总结并提出过程改进计划 | 60 | 60 |
Total | 合计 | 3720 | 4980 |
四、解题思路描述与设计实现说明
4.1、网络接口的使用
4.1.1、注册接口
def signIn(self,account):#注册接口
headers = {
"Content-Type": 'application/json',
"User-Agent": 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Mobile Safari/537.36'
}
response = requests.post('http://api.revth.com/auth/register2', json.dumps(account), headers=headers)
reData = json.loads(response.text)
4.1.2、接入登录接口的代码
def login(self,account): #登陆
# account=self.account
headers = {
"Content-Type": 'application/json',
"User-Agent": 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Mobile Safari/537.36'
}
response = requests.post('http://api.revth.com/auth/login', json.dumps(account), headers=headers)
reData = json.loads(response.text)
4.1.3、接受到牌的接口
def openGame(self,token):
headers = {
"X-Auth-Token": '',
"User-Agent": 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Mobile Safari/537.36'
}
headers["X-Auth-Token"] = token
response = requests.post('http://api.revth.com/game/open', headers=headers)
reData=json.loads(response.text)
4.1.4、排行榜的接口
url = 'http://api.revth.com/rank'
re = requests.get(url)
self.rank_json = json.loads(re.text)
4.1.5、历史记录的接口
headers={
"X-Auth-Token":self.token,
}
data={
"player_id":id,
"limit":20,
"page":self.page
}
response=requests.get("http://api.revth.com/history",data=data,headers=headers)
self.listData=json.loads(response.text)["data"]
4.2、代码组织与内部实现设计(类图)
4.3、算法的关键与关键实现部分流程图
算法使用了回溯搜索,关键在于减枝和分数矩阵评分的算法设计
关键实现部分流程图如下:
五、关键代码解释
搜索回溯
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];
}
}
}
}
权重矩阵
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]
];
六、性能分析与改进
6.1、改进的思路
改进的时候就是看时间是否满足调节,通过算法的优化减小时间的耗费。
从暴搜到简单搜索到牌型贪心组合。枚举13张牌可能组成的牌型,从单张(junks)到顺子(straights),每种牌型从大到小排序,再从中枚举组合出后中前墩的可能出牌模式,最后枚举判断是否合法即可。
6.2、性能分析图
可见CPU运行达到峰值,在出牌的AI算法上
6.3、消耗最大函数
由以上可知,由于是回溯搜索,所以时间主要在三墩牌的组合上。
七、单元测试
7.1、单元测试代码
function test(arr) {
for (var i = 0; i < arr.length; ++i ) {
var mypos = Math.floor(Math.random() * (arr.length - i));
var item = arr[mypos];
arr[mypos] = array[arr.length - 1 - i];
arr[arr.length - 1 - i] = item;
}
return arr;
}
console.time("running time");
for (var i =0 ; i<=1000 ;++i )
{
tmp=optimize_hand(test([...Array(52).keys()]));
}
console.timeEnd("running time");
7.2、测试函数说明
随机测试出牌情况,输出运行时间。
7.3、构造测试数据的思路
随机数据生成,保证算法的鲁棒性,随机生成出1000次牌局,尽可能争取达到各种各样的牌型与决策。
八、Github的代码签入记录
九、遇到的代码模块异常或结对困难及解决方法
问题描述
结对没有尽早开始写代码,拖延得比较后面。每周都在deadline各种赶(linux,实验,比赛等等)
做过哪些尝试
意识到来不及了,就赶快写了,连续熬夜一两天战斗了
是否解决
已解决
有何收获
队友是一个好队友。
在deadline前效率是最高,但过了之后,真的很伤身体
十、评价你的队友
值得学习的地方
强者风范,不紧不满,胸有成竹
需要改进的地方
好像没有什么需要改进的,总的来说,感谢我的队友,考虑到我的各种原因,包揽
了大部分的工作。平心而论,如果这次是个人作业,我可能就完成不了了,或者说只会做出一个非常难受的UI界面。
十一、学习进度条
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 10 | 10 | 学会markdown写博客 |
2 | 500 | 500 | 26 | 36 | 学会json格式使用 使用request库调用API |
3 | 0 | 0 | 21 | 57 | 使用Axure进行原型设计 设计出征战十三水原型 |
4 | 600 | 1100 | 16 | 73 | 使用Pyqt进行UI设计 设计出征战十三水UI |
… | … | … | … | … | … |