在正文第一行给出GitHub项目地址(本行除链接外无任何其他文本内容)
Bilibili
一、结对探索(4分)
1.1 队伍基本信息(1分)
结对编号:12;队伍名称:异世相遇,尽享美味;
学号 | 姓名 | 博客链接 | 具体分工 |
---|---|---|---|
032002223 | 欧希贤 | 前端界面实现、JS的交互 | |
032002225 | 苏文伟 | UI设计、人机对战 |
1.2 描述结对的过程(1分)
希贤:异世相遇
文伟:尽享美味
(原,都可以原)(“旅行者”结伴前行)
1.3 非摆拍的两人在讨论设计或结对编程过程的照片(2分)
二、原型设计(16分)
2.1 原型工具的选择(2分)
先是看了同学推荐的那几个原型设计工具,感觉都不是很趁手,但是在查找原型工具的资料的时候,发现了Pixso这个工具,不用下载软件,
直接在网页上面设计,不仅附带了自动保存的功能,而且还能够导入几个比较流行的原型工具设计软件的文件。
2.2 遇到的困难与解决办法(3分)
我觉得原型设计刚开始最主要的问题是对工具使用的不熟练,但是熟能生巧嘛,多建几个画板就熟悉了。
然后就是素材的选择了,这一点主要看各人的审美还有对题目内容是用什么角度来解读的:
我是最开始听到这个题目的时候就觉得整个游戏风格可以偏向于“黑暗风”,感觉更加的cool一些。
后来听到舍友说这个游戏和“咩咩启示录”里面的一个小游戏很像,然后我去看了一下,它的UI设计一下子
就击中了我的“心巴”,更加坚定了我做“暗设计”的想法。后面更是很多素材都是从“咩咩启示录”里面抠出来
的,我感觉还行,虽然最后的结果和我想象的还是有一定的差距。
2.3 原型作品链接(5分)
https://pixso.cn/app/editor/3WAD0D3Loz_g5cR5eES0Xw
ps:按右上角那个演示就可以通过点击来与原型作品进行交互。(点“开始投掷”什么的)
2.4 原型界面图片展示(6分)
特色创意元素:(倒数第二张图片是PVE结束后的界面,左下角的白色区域会跳转到的是第三张图片的彩蛋模式,彩蛋模式人方可以无限的摇骰子,直到摇出自己想要的点数(对面AI,狠狠地欺负它,哼╭(╯^╰)╮))
- 开始界面
- 点击PVP或者点击PVE进入的游戏界面
- “彩蛋”游戏界面
- 规则界面
- “非彩蛋”的游戏结束弹窗
- “彩蛋”的游戏结束弹窗
三、编程实现(14分)
3.1 网络接口的使用(2分)
没做出来(T^T)
3.2 代码组织与内部实现设计(类图)(2分)
3.3 说明算法的关键与关键实现部分流程图(2分)
- AI算法:
3.4 贴出重要的/有价值的代码片段并解释(2分)
- AI代码
function aiplay() //电脑的逻辑
{
var local = 0;//要返回的位置,给人提示的作用。
for(var k = 0;k <= 2;k++)//第一种情况,其中一行666,正好我摇出了6
{
if(wins1[k*3] == wins1[k*3 + 1] && wins1[k*3] == wins1[k*3 + 2] && wins1[k*3] == y && y > 1)//炸胡,因为要等于y,所以不用判断是否有等于0(ps:1太小了)
{
if(wins2[k*3] == 0)
{
wins2[k*3] = y;
local = k*3;
// count1--;
return local;//它的落点一旦确定就return结束这个函数。
}
else if(wins2[k*3 + 1] == 0)
{
wins2[k*3 + 1] = y;
local = k*3 + 1;
// count1--;
return local;
}
else if(wins2[k*3 + 2] == 0)//一定要有这个判断,不然会把原本就有的给替换掉。
{
wins2[k*3 + 2] = y;
local = k*3 + 2;
//count1--;
return local;
}
}
}
for(var k = 0;k <= 2;k++)//221||212||122摇出了2,上一个for函数已经排除了三个相等的情况
{
//如果出现了两个1是没必要改它的
if(y > 1 && (wins1[k*3] == wins1[k*3+1] && y == wins1[k*3])||(wins1[k*3] == wins1[k*3+2] && y == wins1[k*3])||(wins1[k*3+2] == wins1[k*3+1] && y == wins1[k*3+1]))
{
if(wins2[k*3] == 0)
{
wins2[k*3] = y;
local = k*3;
// count1--;
return local;
}
else if(wins2[k*3 + 1] == 0)
{
wins2[k*3 + 1] = y;
local = k*3 + 1;
// count1--;
return local;
}
else if(wins2[k*3 + 2] == 0)
{
wins2[k*3 + 2] = y;
local = k*3 + 2;
// count1--;
return local;
}
}
}
//从这里开始要优先考虑自己的棋盘
for(var j =0 ;j < 3;j++)
{
if((y == wins2[j*3] || y == wins2[j*3 + 1] || y == wins2[j*3 + 1]) && y > 1)//凑两个或者三个都行,y要大于1
{
if(wins2[j * 3] == 0)
{
local = wins2[j * 3];
wins2[j * 3] = y;
return local;
}
else if(wins2[j*3 + 1] == 0)
{
local = wins2[j * 3 + 1];
wins2[j * 3 + 1] = y;
return local;
}
else if(wins2[j*3 + 2] == 0)
{
local = wins2[j * 3 + 2];
wins2[j * 3 + 2] = y;
return local;
}
}
}
//对面重复的太小我不能消它
var k1 = -1;var k2 = -1;var k3 = -1;
for(var k = 0 ;k < 2;k++)
{
if((wins1[k*3] == wins1[k*3 + 1] && wins1[k*3] == wins1[k*3 + 2] && wins1[k*3] == y && y <= 1)||(y <= 1 && (wins1[k*3] == wins1[k*3+1] && y == wins1[k*3])||(wins1[k*3] == wins1[k*3+2] && y == wins1[k*3])||(wins1[k*3+2] == wins1[k*3+1] && y == wins1[k*3+1])))
{
if(k == 0)
k1 = 0;
if(k == 1)
k2 = 0;
if(k == 2)
k3 = 0;//标记行,先不下。
}
}
if(k1 == 0||k2 == 0||k3 == 0)//有某几行的确存在太小的连对现象
{
var randomm = 50;//循环50次,找不到空位就算了.
while(randomm != 0)
{
var tion = Math.floor((Math.random() * 10) + 1) % 9;
var c = tion/3;//c==0 || c==1 || c==2
if(c == 0 && k1 != 0 && wins2[tion] == 0)
{
wins2[tion] = y;
local = tion;
// count1--;
return local;
}
else if(c == 1 && k2 != 0 && wins2[tion] == 0)
{
wins2[tion] = y;
local = tion;
// count1--;
return local;
}
else if(c == 2 && k3 != 0 && wins2[tion] == 0)
{
wins2[tion] = y;
local = tion;
// count1--;
return local;
}
randomm--;
}
}
//
for(var k = 0;k <= 2;k++)
{
//完全没办法就随机
while(1){
var tion = Math.floor((Math.random() * 10) + 1) % 9;
if(wins2[tion] == 0)
{
wins2[tion] = y;
local = tion;
// count1--;
return local;
}
}
}
- 放图代码
function refresh() //遍历两个数组,若元素不等于0则放置骰子图片,若元素等于0则放置原始图片
{
count1 = 0; //用于记录左边放置的骰子数,由于每次操作都要遍历,故先清零
count2 = 0; //用于记录右边放置的骰子数
for(var j=0;j<9;j++) //遍历左边的数组
{
if(wins1[j]!=0) //非零元素则放置对应骰子图片
{
layImg("leftbox"+(j+1),wins1[j]);
count1++;
}
else //零元素则放置原始图片
{
document.getElementById("leftbox"+(j+1)).src = diceArrays[0];
// count1--;
}
}
//以上是左边的遍历更新操作
judgeend(count1); //遍历结束后便判断是否结束,因为每次refresh都会有一边的图片增多,所以每一边都要单独判断一次。
for(var k=0;k<9;k++)
{
if(wins2[k]!=0)
{
layImg("rightbox"+(k+1),wins2[k]);
count2++;
}
else
{
document.getElementById("rightbox"+(k+1)).src = diceArrays[0];
// count2--;
}
judgeend(count2);
}
}
我们的js的逻辑是先用数组分别记录两个棋盘内的数字有无变化,再将两个数组分别遍历,获取其数值,若数值为0,则将对应位置的图片放置为初始图片,若不为0,则放置为对应的骰子图片。
3.5 性能分析与改进(2分)
消耗最大的是那个AI代码,但是我们觉得那段AI代码也是执行的很快,所以只对规则判断进行优化。
然后让优化过的代码和之前没优化的代码进行1V1PK,统计胜率。我们总共是写了三版的AI代码。
- 第二版代码比较第一版代码是100%胜率(他们两版都是同种思路,都是先看看对面有没有可消的,然后看看自己有没有可堆的,比较贴合人的思维)。
- 第三版代码是利用计算分别在三行放棋子的时候让自己的收益值达到最大(收益值=消掉对方的分数+自己增加的分数)(按道理这种方法应该是最容易赢的思维,但是实际结果是他对比第二版的代码只有大约25%的胜率,这可能是我们还没有把事件的情况考虑全面的因素)。
所以现在代码中正常人机部分放的是第二版的代码,而彩蛋人机部分带的是第一版的代码。
3.6 单元测试(2分)
//AI部分代码
if(k1 == 0||k2 == 0||k3 == 0)/*有某几行的确存在太小的连对现象
(在刚开始的时候忘记加入这个判断条件导致可能会出现在不符合条件的情况也要跑50次,
纯纯是资源浪费了。)*/
{
var randomm = 50;//循环50次,找不到空位就算了.
while(randomm != 0)
{
var tion = Math.floor((Math.random() * 10) + 1) % 9;
var c = tion/3;//c==0 || c==1 || c==2
if(c == 0 && k1 != 0 && wins2[tion] == 0)
{
wins2[tion] = y;
local = tion;
// count1--;
return local;
}
else if(c == 1 && k2 != 0 && wins2[tion] == 0)
{
wins2[tion] = y;
local = tion;
// count1--;
return local;
}
else if(c == 2 && k3 != 0 && wins2[tion] == 0)
{
wins2[tion] = y;
local = tion;
// count1--;
return local;
}
randomm--;
}
}
//
for(var k = 0;k <= 2;k++)
{
//完全没办法就随机
while(1){
var tion = Math.floor((Math.random() * 10) + 1) % 9;
if(wins2[tion] == 0)
{
wins2[tion] = y;
local = tion;
// count1--;
return local;
}
}
function judgeend(count) //判断是否结束
{
// document.getElementById('background').style.display="block";
if(count==9) //某边的骰子数量达到九,游戏结束
{
allScore();//重新统计一次分数才不会出错,否则会出现游戏结果是最后一次放置之前的
document.getElementById('background').style.display="block"; //将弹窗设置为可见
if(wins[0]>wins[1]) //左边总分大于右边
{
document.getElementById("whowin").innerHTML = "左方赢了!";
return;
}
else if(wins[0]==wins[1]) //两边分数一致
{
document.getElementById("whowin").innerHTML = "平局!";
return;
}
else if(wins1[0]<wins[1]) //左边总分小于右边
{
document.getElementById("whowin").innerHTML = "右方赢了!";
return;
}
}
}
若不重新统计分数则会出现计分失误的现象。
3.7 贴出GitHub的代码签入记录,合理记录commit信息(2分)
四、总结反思(11分)
4.1 本次任务的PSP表格(2分)
一.psp表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
· Estimate | · 估计这个任务需要多少时间 | 30 | 60 |
Development | 开发 | ||
· Analysis | · 需求分析 (包括学习新技术) | 900 | 1000 |
· Design Spec | ·生成设计文档 | 30 | 60 |
· Design Review | · 设计复审 | 120 | 150 |
· Coding Standard | · 代码规范(为目前的开发制定合适的规范 ) | 120 | 180 |
· Design | · 具体设计 | 100 | 300 |
· Coding | · 具体编码 | 150 | 1500 |
· Code Review | 代码复审 | 30 | 180 |
· Test | · 测试(自我测试,修改代码,提交修改) | 100 | 180 |
Reporting | 报告 | ||
· Test Repor | · 测试报告 | 20 | 20 |
· Size Measurement | 计算工作量 | 25 | 30 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 50 | 60 |
· 合计 | 1675 | 3720 |
4.2 学习进度条(每周追加)(2分)
- 欧希贤
第N周 | 新增代码(行) | 累计代码(行) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|
第一周 | 50 | 50 | 5 | 学习了HTML基础 |
第二周 | 102 | 152 | 20 | 学习了HTML(续)和CSS基础,能够打出网页的基本框架。 |
第三周 | 1500+ | 2000+ | 40 | 学习了JavaScript基础,扩展了一些HTML与CSS知识,能够实现网页的一些交互。 |
- 苏文伟
第N周 | 新增代码(行) | 累计代码(行) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|
第一周 | 0 | 0 | 4 | 学习了UI工具的使用 |
第二周 | 50+ | 50+ | 10 | 学习了关于这个软件实现的HTML语法,还有一些JavaScript的语法规则。 |
第三周 | 200+ | 700+ | 20 | 对UI设计更加的熟悉,优化UI设计;对AI算法的实现,考虑了多种情况和方案,结合之前学习的JavaScript对AI算法对前端进行适配。 |
4.3 最初想象中的产品形态、原型设计作品、软件开发成果三者的差距如何?(2分)
-其实一开始咱俩想做的是小程序,第一版UI做的原型就是小程序,但最后因为小程序的实现太难了(对现在的我们来说),
且考虑到时间因素,通过UI设计结合自己能够做到的地步做出了现在的UI原型(采用了咩咩启示录的素材),
然后做到现在的web,跟第二版的UI设计作品是比较贴近的。(不会做接口,寄了,不得不摆了,最后一天了|__|)
4.4 评价你的队友(2分)
- 欧希贤
文伟很细心,和他结对编程找bug的速度嘎嘎快。
- 苏文伟
希贤对前端的上手很快,我只负责UI的设计还有Ai代码的编写,属实是偷懒了(doge)。
4.5 结对编程作业心得体会(3分)
- 欧希贤
因为之前没有学过前端,所以感觉就特别难特别难,每天睁眼就是先吐血然后去读书。一开始真的什么都不会,就跟着教程学,学一点打一点学一点打一点,一开始就只能打一些很基础的东西,完全不知道作业的要求怎么完成,也不知道学的有没有用,后面把基础的学全了才感觉好像大概知道需要什么了,再后面才慢慢慢慢开始有自己的想法。真正感受到了从无到有的难度。每一个不曾熬大夜的日子,都是对ddl的辜负(麻了)。后面完成了也蛮有成就感吧,同时也很累,一边觉得从无到有再到成品感觉也算可以了一边又觉得我现在只想躺到世界末日可是明天还要早起要不明天就世界末日吧。然后就是遇到了很多困难吧,但最初的困难过去之后感觉就还好,后面最大的困难就是找bug,就是这个东西,总有一些意想不到的诡异的bug,我们甚至保留了一个当彩蛋。。。找bug这个事情我感觉我可能一个人就比较慢,但结对的话就感觉,效率很高。最后就感觉吧,还是记住万事开头难,如果感到艰难的话,还是希望自己之后可以坚持,挺过这段时间就会好起来。
- 苏文伟
1、评价作业难度:
对我们来说还是有挑战性的(不过无困难不进步嘛,难度算是适中吧)
2、完成后的感受:
我们做的是纯前端,对我们来说难点在于上线(放弃小程序也是这个原因),到最 后我们的登录系统也没做,所以也没有联机系统,算是一个遗憾了。其他方面我感觉还行(感觉我们的AI比距骨骰强(bushi),但是也不是太强(太强了人完全打不过怎么办)(bushi))
3、代码模块异常:
这部分就多了(doge),但是有一个印象深刻的:就是在人把骰子放进最后一个空位的时候,这个时候按道理游戏要马上结束才对,但是我们的代码刚开始的话会出现机器继续放骰子,然后又刚刚好可以消除人的那方的骰子的现象,后来在人放完骰子那个地方加上了一个判断语句,还给“摇骰子”,“点击人方的空格”,“机器摇骰子并放置”加了一个flag的锁(有点操作系统的味道)。
4、对之后开发的启发:
刚开始的时候就要明确自己能够做出什么,最好能够列出自己的技能树,然后再列出这个开发项目需要的技能树,结合时间、资源等因素,对所需技能树做出适当的删减,然后按部就班的去安排时间进行开发。我觉得这样可以让开发过程减少一些额外时间开销。