一、博客链接
周华博客:https://www.cnblogs.com/fzuzh/p/11768398.html
吕瑞峰博客:https://www.cnblogs.com/ruifeng1/p/11768443.html
Fork的同名仓库的Github项目地址:https://github.com/ruifeng-1/031702533
二、具体分工
吕瑞峰:后端代码,AI设计
周华:前端代码,UI设计
三、PSP表格
过程 | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|
计划 | 15 | 15 |
估计任务时间 | 15 | 15 |
开发 | 1000 | 1200 |
需求分析 (包括学习新技术) | 500 | 600 |
生成设计文档 | 30 | 60 |
设计复审 | 50 | 50 |
代码规范 (为目前的开发制定合适的规范) | 20 | 30 |
具体设计 | 50 | 50 |
具体编码 | 80 | 100 |
代码复审 | 20 | 20 |
测试(自我测试,修改代码,提交修改) | 100 | 150 |
报告 | 30 | 60 |
测试报告 | 10 | 20 |
计算工作量 | 10 | 10 |
事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1960 | 2410 |
四、解题思路描述与设计实现说明
- 解题方案
题目要求从十三张牌中选出最优牌组,一开始我感觉无从下手,于是从刚开始借鉴其他人的博客,然后与某位大佬进行深入的讨论,最后确定了解题方案(使用语言:c++):
1.定义一个pair<char,int>型作为牌组变量,将十三张牌存入牌组中,然后枚举十三张牌的所有情况,枚举的过程是先枚举5张牌作为后墩,再枚举3张作为前墩,最后5张作为中墩(不要问我为什么不枚举553而是535),因此枚举次数是c5,13*c3,8,而非13!。
2.对于任意情况,如何选出最优牌组?我们对每种牌型和牌面设置了权重,然后据此计算每种情况的权值(先算出每墩的权值再相加),选出权值最高的情况作为最优牌组。
3.对于牌组的单调性(即后墩>=中墩>=前墩),我们在计算每墩的权值后会进行判断,不符合单调性则直接丢弃。 - 网络接口的使用
1.登录
Json::Value login(const string& username, const string& password) {
Json::Value post;
post["username"] = username;
post["password"] = password;
int re = http.ope("/auth/login", "status", "POST", "Content-Type: application/json
", post.toStyledString());
if (re) {
cout << re;
post["status"] = 1;
return post;
}
Json::Value ret;
Json::Reader reader;
reader.parse(dt.getStatus(), ret);
return ret;
}
2.注册绑定
Json::Value regAndBind(const string& username, const string& password, const string& student_number, const string& student_password) {
Json::Value post;
post["username"] = username;
post["password"] = password;
post["student_number"] = student_number;
post["student_password"] = student_password;
int re = http.ope("/auth/register2", "status", "POST", "Content-Type: application/json
", post.toStyledString());
if (re) {
cout << re;
post["status"] = 1;
return post;
}
Json::Value ret;
Json::Reader reader;
reader.parse(dt.getStatus(), ret);
return ret;
}
3.注销
Json::Value logout() {
Json::Value ret;
int re = http.ope("/auth/logout", "status", "POST", "X-Auth-Token: "" + user.token + ""
", "");
if (re) {
cout << re;
ret["status"] = 1;
return ret;
}
Json::Reader reader;
reader.parse(dt.getStatus(), ret);
return ret;
}
4.开始战局
Json::Value start() {
Json::Value ret;
int re = http.ope("/game/open", "status", "POST", "X-Auth-Token: " + user.token + "
", "");
if (re) {
cout << re;
ret["status"] = 1;
return ret;
}
Json::Reader reader;
reader.parse(dt.getStatus(), ret);
return ret;
}
5.出牌
Json::Value submit(const Json::Value& post) {
Json::Value ret;
int re = http.ope("/game/submit", "status", "POST", "X-Auth-Token: " + user.token + ",Content-Type: application/json
", post.toStyledString());
if (re) {
cout << re;
ret["status"] = 1;
return ret;
}
Json::Reader reader;
reader.parse(dt.getStatus(), ret);
return ret;
}
-
代码组织与内部实现设计
-
算法的关键与关键实现部分流程图
算法的关键在于两处:牌组的枚举,权值的计算。
五、关键代码解释
登录界面:
<div id="particles-js">
<div class="login">
<div class="login-top">
登录
</div>
<div class="login-center clearfix">
<div class="login-center-img"><img src="img/name.png"/></div>
<div class="login-center-input">
<input type="text" name="" value="" placeholder="请输入您的用户名" onfocus="this.placeholder=''" onblur="this.placeholder='请输入您的用户名'"/>
<div class="login-center-input-text">用户名</div>
</div>
</div>
<div class="login-center clearfix">
<div class="login-center-img"><img src="img/password.png"/></div>
<div class="login-center-input">
<input type="password" name=""value="" placeholder="请输入您的密码" onfocus="this.placeholder=''" onblur="this.placeholder='请输入您的密码'"/>
<div class="login-center-input-text">密码</div>
</div>
</div>
<div class="login-button">
登录
</div>
</div>
<div class="sk-rotating-plane"></div>
</div>
枚举:
Enume(枚举)类中的部分函数,主要用于枚举13张牌并分别放入三墩中
void Enume::clear(vector<pair<char, int> >& _a) {
swap(poker, _a);
max_weight = 0;
vector<int> p;
for (int i = 1; i <= 13; i++)
p.push_back(i);
dfs1(p, 5, 13);
}
void Enume::dfs1(vector<int>& p, int r, int n) {
for (int s = (1 << r) - 1; s < 1 << n;) {
choose.clear();
vector<int> cur = p;
for (int j = n - 1; j >= 0; j--) {
if (((s >> j) & 1)) choose.push_back(poker[p[j]]), cur.erase(cur.begin() + j, cur.begin() + j + 1);
}
back.init(choose);//计算权值
dfs2(cur, 5, 8);
int x = s & -s;
int y = s + x;
s = ((s & ~y) / x >> 1) | y;
}
}
void Enume::dfs2(vector<int> & p, int r, int n) {
for (int s = (1 << r) - 1; s < 1 << n;) {
choose.clear();
vector<int> cur = p;
for (int j = n - 1; j >= 0; j--) {
if (((s >> j) & 1)) choose.push_back(poker[p[j]]), cur.erase(cur.begin() + j, cur.begin() + j + 1);
}
mid.init(choose);//计算权值
int x = s & -s;
int y = s + x;
s = ((s & ~y) / x >> 1) | y;
if (mid.weight > back.weight) continue;
dfs3(cur, 3, 3);
}
}
void Enume::dfs3(vector<int> & p, int r, int n) {
choose.clear();
for (int j = 0; j < n; j++) {
choose.push_back(poker[p[j]]);
}
front.init(choose);//计算权值
if (front.weight > mid.weight) return;
best_choose();//放入最优牌组
}
六、性能分析与改进
-
改进的思路
原来的算法是计算三墩的权值后,再比较三墩的牌型大小,如果不符合单调性则丢弃,后来觉得实在是有点膈应人,于是作了剪枝的优化处理:枚举到中墩时,如果中墩的牌型大于后墩的牌型,则直接丢弃,重新进行枚举。 -
性能分析图和程序中消耗最大的函数
七、单元测试
八、贴出Github的代码签入记录
九、遇到的代码模块异常或结对困难及解决方法
-
问题描述
此次依然使用c++写后端代码,出现了许多问题,但最关键也最严重的是找不到相应的函数!!例如http请求和json的处理,c++相对java和python来说就非常麻烦。
-
做过哪些尝试
在网上寻找第三方库,找了cjson,jsoncpp,libcurl一系列库等,在编译第三方库这一部分经历了一段很长的阵痛。
-
是否解决
解决了,最后确定了jsoncpp和自带的wininet.h库来处理json和http请求。
-
有何收获
熟悉了jsoncpp库的使用,比起cjson库使用起来更加方便。
十、评价你的队友
-
值得学习的地方
圆满地完成困难的AI部分,非常认真地对待这次作业。 -
需要改进的地方
没有,太强了
十一、学习进度条
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 10 | 10 | 学习用Axure RP 8设计原型模型 |
2 | 500 | 500 | 30 | 30 | 设计AI算法,掌握一些c++第三方库的使用 |
3 | 100 | 100 | 20 | 20 | 完善算法,学习了一点html的知识 |