https://github.com/374671984/tou_le_mei.git
Bilibili视频地址
小程序名称:骰了没
标题一、结对探索(4分)
1.1 队伍基本信息(1分)
结对编号:11;队伍名称:没名字与大忽悠;
学号 | 姓名 | 作业博客链接 | 具体分工 |
---|---|---|---|
102192109 | 李文龙 | http://t.csdn.cn/t7W3j | 前端原型设计,AI算法设计 |
102192111 | 陈佳亮 | 链接 | 前端界面的实现,后端功能的实现,代码的书写 |
1.2 描述结对的过程(1分)
宿舍两两结对
1.3 非摆拍的两人在讨论设计或结对编程过程的照片(2分)
二、原型设计(16分)
2.1 原型工具的选择(2分)
墨刀。
集成化,方便。
2.2 遇到的困难与解决办法(3分)
原型的选择:是否保持一致的风格,简约还是花哨,最后选择了Q版动漫风格
UI的设计:各种组件的摆放位置,如何摆放比较合理,让用户使用更舒服,更人性化。经过不断实验后选择了一些比较舒适的组件摆放位置。
墨刀比较难实现动画,最后只能使用静态图片,虽然带有交互性。
2.3 原型作品链接(5分)
2.4 原型界面图片展示(6分)
主页,点击开始游戏则弹出获取昵称头像的消息框
选择界面、点击规则出现弹窗。可滑动查看
本地对战、人机对战、在线对战创建房间
登录后选择创建房间或者加入房间
点击创建对局后,进入等待界面等待对方加入,对战结束后跳转到结算
三、编程实现(14分)
3.1 网络接口的使用(2分)
- 使用wx.request,使用小程序自带的静态网站
3.2 代码组织与内部实现设计(类图)(2分)
主界面到选择界面:cover -> modelselection
选择界面三个模式:self、ai、onlinelogin
在线对战: onlinelogin(登录) -> onlineselect(选择创建、加入)
创建:onlinecreateroom -> onlinecreateroomwaitting(等待加入)
加入:onlinejoinroom
内部实现:
player1:玩家1,调用所有相关函数实现功能
player2:玩家2,调用所有相关函数实现功能
touClick : 点击骰子,投骰子
put_tou : 放置骰子
remove : 消除同行中对方的相同点数位置
topCount 与 downCount : 计算得分
end : 结束
ai_Host1 : ai1(玩家1托管)的操作,调用所有相关函数实现功能
ai_Host2 : ai2(玩家2托管)的操作,调用所有相关函数实现功能
nextstep:AI逻辑,返回下一步的位置
ai_put_tou : AI放置骰子
ai_remove : AI消除同行中对方的相同点数位置
host_switch : 托管转换逻辑
top、down相关: 按钮事件,返回按钮值,即位置
3.3 说明算法的关键与关键实现部分流程图(2分)
AI算法思路:
3.4 贴出重要的/有价值的代码片段并解释(2分)
AI算法部分的实现,先消除对方棋盘上面和骰子点数相同的格子,没有就把骰子放在我方棋盘具有相同骰子点数的一行上面,如果都没有,就找一个空格放下。
//AI实现,输入当前轮次己方的棋盘情况、对方的棋盘情况和己方本轮次随机得到的骰子点数,再返回下一步要走哪里
nextStap(ownBoard=[],otherBoard=[],figure){
//用AI,计算ownBoard,otherBoard,返回下一步要走哪里
// 玩家2落点
if(centreState === 0 && downState === 1 && turn === 2 && hosted2 === 1){
for(var x = 0 ; x < 9 ; x++){
//对方棋盘中有和当前骰子点数相同的格子
if(otherBoard[x] === figure){
// 判定行数
// 第一行
if(0 < x && x < 3){
//如果己方第一行未满
for(var y = 0 ; y < 3 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
//己方第一行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 3 ; y < 9 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
}
// 第二行
if(3 < x && x < 6){
//如果己方第二行未满
for(var y = 3 ; y < 6 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
//己方第二行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 0 ; y < 9 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
}
// 第三行
if(6 < x && x < 9){
//如果己方第三行未满
for(var y = 6 ; y < 9 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
//己方第三行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 0 ; y < 6 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
}
}
}
//对方棋盘没有和己方棋盘相同的点数
for(var x = 0 ; x < 9 ; x++){
//判断己方棋盘有没有相同的点数,有就放在同一行
//己方有相同点数的格子
if(ownBoard[x] === figure){
// 判定行数
// 第一行
if(0 < x && x < 3){
//如果己方第一行未满
for(var y = 0 ; y < 3 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
//己方第一行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 3 ; y < 9 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
}
// 第二行
if(3 < x && x < 6){
//如果己方第二行未满
for(var y = 3 ; y < 6 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
//己方第二行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 0 ; y < 9 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
}
// 第三行
if(6 < x && x < 9){
//如果己方第三行未满
for(var y = 6 ; y < 9 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
//己方第三行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 0 ; y < 6 ; y++){
if(ownBoard[y] === 0){
console.log("玩家2落点" + y)
return y
}
}
}
}
}
//己方棋盘也没有相同点数,按照棋盘顺序放在空位置
for(var x = 0 ; x < 9 ; x++){
if(ownBoard[x] === 0){
console.log("玩家2落点" + y)
return x
}
}
}
// 玩家1落点
if(centreState === 0 && topState === 1 && turn === 1 && hosted1 === 1){
for(var x = 0 ; x < 9 ; x++){
//对方棋盘中有和当前骰子点数相同的格子
if(ownBoard[x] === figure){
// 判定行数
// 第一行
if(0 < x && x < 3){
//如果己方第一行未满
for(var y = 0 ; y < 3 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
//己方第一行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 3 ; y < 9 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
}
// 第二行
if(3 < x && x < 6){
//如果己方第二行未满
for(var y = 3 ; y < 6 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
//己方第二行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 0 ; y < 9 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
}
// 第三行
if(6 < x && x < 9){
//如果己方第三行未满
for(var y = 6 ; y < 9 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
//己方第三行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 0 ; y < 6 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
}
}
}
//对方棋盘没有和己方棋盘相同的点数
for(var x = 0 ; x < 9 ; x++){
//判断己方棋盘有没有相同的点数,有就放在同一行
//己方有相同点数的格子
if(otherBoard[x] === figure){
// 判定行数
// 第一行
if(0 < x && x < 3){
//如果己方第一行未满
for(var y = 0 ; y < 3 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
//己方第一行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 3 ; y < 9 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
}
// 第二行
if(3 < x && x < 6){
//如果己方第二行未满
for(var y = 3 ; y < 6 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
//己方第二行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 0 ; y < 9 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
}
// 第三行
if(6 < x && x < 9){
//如果己方第三行未满
for(var y = 6 ; y < 9 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
//己方第三行已满,就放在己方空的位置上
//可通过判定分数来改进落点
for(var y = 0 ; y < 6 ; y++){
if(otherBoard[y] === 0){
console.log("玩家1落点" + y)
return y
}
}
}
}
}
//己方棋盘也没有相同点数,按照棋盘顺序放在空位置
for(var x = 0 ; x < 9 ; x++){
if(otherBoard[x] === 0){
console.log("玩家1落点" + y)
return x
}
}
}
},
放置骰子和消除骰子的代码。
通过判断按钮返回的按钮值,来判断是哪一方,哪一个按钮按下,然后将这一个格子的图片替换成骰子点数对应的图片,并修改数组中的值。
消除骰子就是先区分是玩家1的回合还是玩家2的回合,然后通过按钮触发的值来判断是骰子被放在哪一行,然后消除对方对应行相同的骰子,并把数据更新到数组中。
//放置骰子,判断是哪个按钮按下了,改变ownBoard[]或otherBoard[]对应位置的值(骰子点数),并放置对应骰子点数的图片
put_tou(){
if(down_id === 1 && downState === 1 && turn ===2){
ownBoard[0] = figure
var image1 = this.fetchImgAddr(figure)
this.setData({
down11 : image1
})
console.log("ownBoard" + ownBoard)
}
else if(down_id === 2 && downState === 1 && turn ===2){
ownBoard[1] = figure
var image2 = this.fetchImgAddr(figure)
this.setData({
down12 : image2
})
console.log("ownBoard" + ownBoard)
}
else if(down_id === 3 && downState === 1 && turn ===2){
ownBoard[2] = figure
var image3 = this.fetchImgAddr(figure)
this.setData({
down13 : image3
})
console.log("ownBoard" + ownBoard)
}
else if(down_id === 4 && downState === 1 && turn ===2){
ownBoard[3] = figure
var image4 = this.fetchImgAddr(figure)
this.setData({
down21 : image4
})
console.log("ownBoard" + ownBoard)
}
else if(down_id === 5 && downState === 1 && turn ===2){
ownBoard[4] = figure
var image5 = this.fetchImgAddr(figure)
this.setData({
down22 : image5
})
console.log("ownBoard" + ownBoard)
}
else if(down_id === 6 && downState === 1 && turn ===2){
ownBoard[5] = figure
var image6 = this.fetchImgAddr(figure)
this.setData({
down23 : image6
})
console.log("ownBoard" + ownBoard)
}
else if(down_id === 7 && downState === 1 && turn ===2){
ownBoard[6] = figure
var image7 = this.fetchImgAddr(figure)
this.setData({
down31 : image7
})
console.log("ownBoard" + ownBoard)
}
else if(down_id === 8 && downState === 1 && turn ===2){
ownBoard[7] = figure
var image8 = this.fetchImgAddr(figure)
this.setData({
down32 : image8
})
console.log("ownBoard" + ownBoard)
}
else if(down_id === 9 && downState === 1 && turn ===2){
ownBoard[8] = figure
var image9 = this.fetchImgAddr(figure)
this.setData({
down33 : image9
})
console.log("ownBoard" + ownBoard)
}
else if(top_id === 1 && topState === 1 && turn ===1){
otherBoard[0] = figure
var image1 = this.fetchImgAddr(figure)
this.setData({
top11 : image1
})
console.log("otherBoard" + otherBoard)
}
else if(top_id === 2 && topState === 1 && turn ===1){
otherBoard[1] = figure
var image2 = this.fetchImgAddr(figure)
this.setData({
top12 : image2
})
console.log("otherBoard" + otherBoard)
}
else if(top_id === 3 && topState === 1 && turn ===1){
otherBoard[2] = figure
var image3 = this.fetchImgAddr(figure)
this.setData({
top13 : image3
})
console.log("otherBoard" + otherBoard)
}
else if(top_id === 4 && topState === 1 && turn ===1){
otherBoard[3] = figure
var image4 = this.fetchImgAddr(figure)
this.setData({
top21 : image4
})
console.log("otherBoard" + otherBoard)
}
else if(top_id === 5 && topState === 1 && turn ===1){
otherBoard[4] = figure
var image5 = this.fetchImgAddr(figure)
this.setData({
top22 : image5
})
console.log("otherBoard" + otherBoard)
}
else if(top_id === 6 && topState === 1 && turn ===1){
otherBoard[5] = figure
var image6 = this.fetchImgAddr(figure)
this.setData({
top23 : image6
})
console.log("otherBoard" + otherBoard)
}
else if(top_id === 7 && topState === 1 && turn ===1){
otherBoard[6] = figure
var image7 = this.fetchImgAddr(figure)
this.setData({
top31 : image7
})
console.log("otherBoard" + otherBoard)
}
else if(top_id === 8 && topState === 1 && turn ===1){
otherBoard[7] = figure
var image8 = this.fetchImgAddr(figure)
this.setData({
top32 : image8
})
console.log("otherBoard" + otherBoard)
}
else if(top_id === 9 && topState === 1 && turn ===1){
otherBoard[8] = figure
var image9 = this.fetchImgAddr(figure)
this.setData({
top33 : image9
})
console.log("otherBoard" + otherBoard)
}
},
// 判断行消除对方骰子
remove () {
//玩家1回合
if(turn === 1 && topState === 1){
//第一行
if(1 <= top_id && top_id <= 3){
for(var i = 0; i < 3 ; i++){
if(otherBoard[top_id - 1] === ownBoard[i]){
ownBoard[i] = 0
var image = this.fetchImgAddr(0)
if(ownBoard[0] === 0){
this.setData({
down11 : image
})
}
if(ownBoard[1] === 0){
this.setData({
down12 : image
})
}
if(ownBoard[2] === 0){
this.setData({
down13 : image
})
}
}
}
}
//第二行
if(4 <= top_id && top_id <= 6){
for(var i = 3; i < 6 ; i++){
if(otherBoard[top_id - 1] === ownBoard[i]){
ownBoard[i] = 0
var image = this.fetchImgAddr(0)
if(ownBoard[3] === 0){
this.setData({
down21 : image
})
}
if(ownBoard[4] === 0){
this.setData({
down22 : image
})
}
if(ownBoard[5] === 0){
this.setData({
down23 : image
})
}
}
}
}
//第三行
if(7 <= top_id && top_id <= 9){
for(var i = 6; i < 9 ; i++){
if(otherBoard[top_id - 1] === ownBoard[i]){
ownBoard[i] = 0
var image = this.fetchImgAddr(0)
if(ownBoard[6] === 0){
this.setData({
down31 : image
})
}
if(ownBoard[7] === 0){
this.setData({
down32 : image
})
}
if(ownBoard[8] === 0){
this.setData({
down33 : image
})
}
}
}
}
}
//玩家2回合
if(turn === 2 && downState === 1){
//第一行
if(1 <= down_id && down_id <= 3){
for(var i = 0; i < 3 ; i++){
if(ownBoard[down_id - 1] === otherBoard[i]){
otherBoard[i] = 0
var image = this.fetchImgAddr(0)
if(otherBoard[0] === 0){
this.setData({
top11 : image
})
}
if(otherBoard[1] === 0){
this.setData({
top12 : image
})
}
if(otherBoard[2] === 0){
this.setData({
top13 : image
})
}
}
}
}
//第二行
if(4 <= down_id && down_id <= 6){
for(var i = 3; i < 6 ; i++){
if(ownBoard[down_id - 1] === otherBoard[i]){
otherBoard[i] = 0
var image = this.fetchImgAddr(0)
if(otherBoard[3] === 0){
this.setData({
top21 : image
})
}
if(otherBoard[4] === 0){
this.setData({
top22 : image
})
}
if(otherBoard[5] === 0){
this.setData({
top23 : image
})
}
}
}
}
//第三行
if(7 <= down_id && down_id <= 9){
for(var i = 6; i < 9 ; i++){
if(ownBoard[down_id - 1] === otherBoard[i]){
otherBoard[i] = 0
var image = this.fetchImgAddr(0)
if(otherBoard[6] === 0){
this.setData({
top31 : image
})
}
if(otherBoard[7] === 0){
this.setData({
top32 : image
})
}
if(otherBoard[8] === 0){
this.setData({
to33 : image
})
}
}
}
}
}
},
其他诸如分数如何计算,页面如何绘制都是非常简单的东西,也就上述部分代码可能还有那么一点点思考的余地和价值,所以其他的就不放了。
3.5 性能分析与改进(2分)
体验 33
1 存在使用 css ‘:active’ 伪类来实现点击态
使用 css ‘:active’ 伪类来实现点击态,很容易触发,并且滚动或滑动时点击态不会消失,体验较差。建议使用小程序内置组件的 ‘hover-*’ 属性来实现
2 存在图片没有按原图宽高比例显示
图片若没有按原图宽高比例显示,可能导致图片歪曲,不美观,甚至导致用户识别困难。可根据情况设置 image 组件的 mode 属性,以保持原图宽高比。
3 文字颜色与背景色搭配较差,两者颜色过于接近会让用户无法或难以阅读
最佳实践 82
没通过
1 发现正在使用废弃接口
使用即将废弃或已废弃接口,可能导致小程序运行不正常。一般而言,接口不会立即去掉,但保险起见,建议不要使用,避免后续小程序突然运行异常。
组件/API modal
页面 pages/modelselection/modelselection
存在定时器未跟随页面回收
定时器是全局的,并不是跟页面绑定的,当小程序从一个页面路由到另一个页面之后,前一个页面定时器应注意手动回收
3.6 单元测试(2分)
3.7 贴出GitHub的代码签入记录,合理记录commit信息(2分)![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e466cac2b4fb94fceb0abe34c0322b85.png)
最前面的部分是在搞网络接口的时候,通过微信开发者工具签入的信息,当时基本完成了大部分界面的绘制。
四、总结反思(11分)
4.1 本次任务的PSP表格(2分)
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 45 |
· Estimate | · 估计这个任务需要多少时间 | 30 | 45 |
Development | 开发 | 2860 | 3300 |
· Analysis | · 需求分析 (包括学习新技术) | 840 | 1040 |
· Design Spec | · 生成设计文档 | 60 | 80 |
· Design Review | · 设计复审 | 100 | 100 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 | 80 |
· Design | · 具体设计 | 60 | 100 |
· Coding | · 具体编码 | 1500 | 1600 |
· Code Review | · 代码复审 | 120 | 180 |
· Test | · 测试(自我测试,修改代码,提交修改) | 120 | 120 |
Reporting | 报告 | 135 | 135 |
· Test Report | · 测试报告 | 60 | 60 |
· Size Measurement | · 计算工作量 | 15 | 15 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 60 |
· 合计 | 3025 | 3480 |
4.2 学习进度条(每周追加)(2分)
102192109李文龙:
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 5 | 5 | 原型设计主题确定,收集素材 |
2 | 0 | 0 | 20 | 25 | 制作初步原型设计 |
3 | 1000 | 1000 | 24 | 49 | 实现的原型设计,增加AI算法,学习微信小程序基本语言js,wxml,wxss |
102192111 陈佳亮:
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0 | 摆烂 |
2 | 0 | 0 | 20 | 20 | 持续摆烂、学习css、html、jss三件套和微信小程序 |
3 | 5000 | 5000 | 24 | 49 | 实现前端绘制,后端所有功能的实现,以实践促进成长 |
4.3 最初想象中的产品形态、原型设计作品、软件开发成果三者的差距如何?(2分)
最初就想着做一个小程序,因为可玩性比较高,但是后来在联机部分以及小程序上线的方面遇到了一些困难,所以只能先实现单机游戏。
原型设计比预期要满意一点。
软件开发成果基本满意。
4.4 评价你的队友(2分)
李文龙:
我的评价是:佳亮太强了!学习能力代码能力都很强!只能说太牛了!
值得学习的地方:学习及代码实践能力很强,喜欢钻研,沉浸式开发
需要改进:加强编程能力
陈佳亮:
我对队友的评价是:还不错,界面图片的设计深得我意,因为我是写代码的,界面这一块的设计就懒得管了,然后最多就是写页面的时候重新分配一下位置,看起来稍微舒服一些,队友找图片减少了我的工作量
4.5 结对编程作业心得体会(3分)
李文龙:
本次开发我的评价是困难。
本次任务又是一个全新的课题。需要用到之前没怎么学过的原型设计,前端以及后端。需要学习大量的新知识。
完成之后感觉轻松了许多,收获也挺多,更多的是又拓宽了自己的知识面,感觉又学到了新知识。
陈佳亮:
本次开发学习了前端三件套,学习 小程序开发,总的来说收获还行,主要是学习到了新的知识,至于代码能力的提升,没感觉到,反正就是该怎么写就怎么写吧,说实话我自己还有很多事情得干,搞个这个作业让我很头大,每天都是不想干,不得不干,还得干别的事情来回徘徊,而且前端三件套以前没学过,所以就不知道要怎么去结合前后端进行开发,略微畏难心理导致我就是看一眼这个作业就觉得烦,然后就是拖到周六的时候,只剩一周了,再不干干不完了。于是打开B站学三件套和小程序开发,花了两三天吧,简单入了一下门,然后开始写代码(参考别人的项目和代码迅速学习掌握各种功能和模块),前端写的比较顺,几乎没遇到问题,网络接口这部分遇到的问题比较大,不仅没搞出来还浪费了我很多时间,不搞这个也不至于要熬夜写代码,心累,其他的AI部分实现自动一直运作还有待debug,还有托管只能手动一步步来,有待改善,总之,这是一个让我很心累的小项目,这种不想做,没时间做,又不得不做的东西属性难顶,最大的收获也就是三天速通前端三件套了,踩了无数坑,希望能有一点帮助吧。