Part 1.结对同学
Part 2.具体分工
杨润秋:负责前端界面和网络接口调用代码书写及最后的整合及打包
韩洪威:负责后端AI算法部分代码书写、优化、调试
Part 3.PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
Estimate | · 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 3100 | 3400 |
Analysis | · 需求分析(包括学习新技术) | 500 | 600 |
Design Spec | · 生成设计文档 | 20 | 20 |
Design Review | · 设计复审 | 10 | 10 |
Coding Standard | · 代码规范(为开发制定合适的规范) | 30 | 30 |
Design | · 具体设计(用伪代码,流程图等方法来设计具体模块) | 20 | 30 |
Coding | · 具体编码 | 2400 | 2600 |
Code Review | · 代码复审 | 60 | 40 |
Test | 测试(自我测试,修改,提交修改) | 60 | 70 |
Reporting | 报告 | 60 | 60 |
Test Report | · 测试报告 | 30 | 30 |
Size Measurement | · 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | · 事后总结并提出过程改进计划 | 20 | 20 |
合计 | 3190 | 3490 |
Part 4. 解题思路描述与设计实现说明
思路说明(前端):
首先就是UI的设计,由于事先做过了原型设计,所以前端的UI设计就变得轻松许多。前端开发主要使用Android Studio进行开发安卓App。通过Android Studio设计UI界面可以使用拖拽的方法,所以对代码的要求不是很高。
完成了UI设计,接下来就是实现各个界面之间的通信功能,这几我采用的方法是每一个界面都设计一个Activity(实际上这样的设计方式比较繁琐,后续的优化可以改进为使用碎片化设计Fragment)。
与服务器的通信功能做的比较常规,就是正常的向服务器发送请求然后处理Response消息,例如登录和注册界面,只需要判断message是否为Success即可。需要注意的是如果属性变量等处理不当经常容易出现空指针异常而导致程序闪退的现象,想要做到处理好各种异常还有很多地方的代码需要进一步的优化。
思路说明(后端):
在十三水作业发布前就知道了隔壁班的这个作业,不过那时候也没有仔细去研究,作业发布后看着题目陷入了深思,因为一时也不知道算法该从何入手,后来慢慢研究后觉得一共也就十三张牌,不是很多,那可以使用枚举的方法,将所有的情况枚举出来,再进行比较选出认为比较大的牌型,而且对前墩、中墩枚举,剩下得到就是后墩,一共有C(13,3)xC(10,5)xC(5,5)一共就7万多种情况,时间复杂度上也符合要求,于是就顺着这个思路写下去了。
- 第一次也没什么思路,就憨憨的一个个按照十三水的规则算多少水,遍历下去取最大的,然后兴致冲冲拿去跑,结果很明显,因为不低于零的系统设置,所以没有负分,靠着运气偶尔有几十分,但从没有上过一百。
- 经过几天后,就将原来的代码删去,想了想于是选择使用权值比较的方法,将十种普通牌型赋予1-10的价值,通过比较价值来判断牌型,选择权值较大的牌型,然后又拿去跑,这次倒是跑到了更高一点的分数,不过也就仅此而已。
后来在观察历史对局详情时,突然发现了一种情况,比如“7 4 3,5 5 8 8 K,A 5 5 5 5”这种牌型如果按照这样“A K 7,5 5 8 8 4,3 5 5 5 5”,很明显后者会获得获得的收益更大,因为前墩是散牌的概率还是很大的,这样得分的可能性很明显更高,于是按照这个思路又改了一次算法,就得到了现在的算法
网络接口的使用:
网络接口的使用主要通过Retrofit2,可以把请求Request和返回的Response都封装为一个一个独立的类,Body的Key设置为类中的属性,这样发送和接收的时候就可以更加的方便。
以下是实现的部分代码:类图:
算法关键部分:
关键部分主要还是在枚举牌型及权值比较这一部分
关键部分流程图:
Part 5:关键代码解释
private static float balance(String[] str,int level){//权值细化值计算 float x=0; int max=0; int[] a=new int[13]; switch (level){ case 1: case 6: case 7: case 10: for(int i=0;i<str.length;i++){ if(max<map.get(str[i].charAt(1))){ max=map.get(str[i].charAt(1)); } } x=(float)(max-1)/13; break; case 2: case 3: case 4: for(int i=0;i<str.length;i++){ a[map.get(str[i].charAt(1))]++; } for(int i=0;i<=12;i++){ if(a[i]==2){ if(max<i)max=i; } } x=(float)(max-1)/13; break; case 5: case 8: for(int i=0;i<str.length;i++){ a[map.get(str[i].charAt(1))]++; } for(int i=0;i<=12;i++){ if(a[i]==3){ if(max<i)max=i; } } x=(float)(max-1)/13; break; case 9: for(int i=0;i<str.length;i++){ a[map.get(str[i].charAt(1))]++; } for(int i=0;i<=12;i++){ if(a[i]==4){ if(max<i)max=i; } } x=(float)(max-1)/13; break; } return x; } private static void calcuWater(String c1,String c2,String c3) throws IOException { //权值比较 int level1,level2,level3; c1=c1.substring(0,8); c2=c2.substring(0,14); c3=c3.substring(1,15); String[] str1=c1.split(" "); String[] str2=c2.split(" "); String[] str3=c3.split(" "); level1=judgeLevel(str1); level2=judgeLevel(str2); level3=judgeLevel(str3); if(compare(str1,str2,level1,level2,1,2)==1)return; if(compare(str2,str3,level2,level3,2,3)==1)return; if(level1+level2+level3>snum1+snum2+snum3){ replace(str1,level1,1); replace(str2,level2,2); replace(str3,level3,3); } else if(level1+level2+level3==snum1+snum2+snum3){ float g=balance(str1,level1)+balance(str2,level2)+balance(str3, level3)+(float)(level1-1)/10+(float)(level2-1)/10+(float)(level3-1)/10; float h= balance(s1,snum1)+balance(s2,snum2)+balance(s3,snum3)+(float)(snum1-1)/10+(float)(snum2-1)/10+(float)(snum3-1)/10; if(g>h){ replace(str1,level1,1); replace(str2,level2,2); replace(str3,level3,3); } } } private static void enumerate(String[] str)throws IOException{ //枚举所有牌型 String s=" "; for(int i=0;i<=12;i++){ s=s+str[i].substring(0,2); s+=" "; } int a1=0,a2=1,a3=2; int b1=0,b2=1,b3=2,b4=3,b5=4; while(a1<=10){ String ss=s; String c1=ss.substring(a1*3+1,(a1+1)*3+1)+ss.substring(a2*3+1, (a2+1)*3+1)+ss.substring(a3*3+1,(a3+1)*3+1); b1=0; b2=1; b3=2; b4=3; b5=4; while(b1<=5){ String sss=ss.substring(0,a1*3+1) +ss.substring((a1+1)*3+1, a2*3+1) +ss.substring((a2+1)*3+1,a3*3+1) +ss.substring((a3+1)*3+1,40); String c2= sss.substring(b1*3+1,(b1+1)*3+1) +sss.substring(b2*3+1,(b2+1)*3+1) +sss.substring(b3*3+1,(b3+1)*3+1) +sss.substring(b4*3+1,(b4+1)*3+1) +sss.substring(b5*3+1,(b5+1)*3+1); String c3=sss.substring(0,b1*3+1) +sss.substring((b1+1)*3+1,b2*3+1) +sss.substring((b2+1)*3+1,b3*3+1) +sss.substring((b3+1)*3+1,b4*3+1) +sss.substring((b4+1)*3+1,b5*3+1) +sss.substring((b5+1)*3+1,31); calcuWater(c1,c2,c3); //if(b1>=5)break; if(b5==9){ if(b4!=8){ b4++; b5=b4+1; } else{ if(b3!=7){ b3++; b4=b3+1; b5=b4+1; } else{ if(b2!=6){ b2++; b3=b2+1; b4=b3+1; b5=b4+1; } else{ b1++; b2=b1+1; b3=b2+1; b4=b3+1; b5=b4+1; } } } } else b5++; } if(a3==12){ if(a2!=11){ a2++; a3=a2+1; } else{ a1++; a2=a1+1; a3=a2+1; } } else a3++; } }
最重要的代码就是这部分的代码,分别是枚举牌型,权值比较,细化权值计算,通过遍历所有的牌型将认为最大的牌型选出来并赋值到对应变量中,返回到相应类中