https://github.com/Yummy-y/Kex-Bonesdice.git
https://www.bilibili.com/video/BV1Pg411a7cR?share_source=copy_web
一、结对探索
1.1 队伍基本信息
结对编号:20;队伍名称:饿鬼;
学号 | 姓名 | 作业博客链接 | 分工 |
---|---|---|---|
032002231 | 谢以盛 | https://github.com/Yummy-y/Kex-Bonesdice.git | 后端 |
032002235 | 杨明 | https://github.com/Yummy-y/Kex-Bonesdice.git | 前端 |
1.2 描述结对的过程
Yummy:搞?
Eson:搞!
1.3 非摆拍的两人在讨论设计或结对编程过程的照片
二、原型设计
2.1 原型工具的选择
选择的原型工具是墨刀,墨刀是一款打通产设研团队,实现原型,设计,流程,思维导图一体化的在线协同工具。它方便使用,利于协同开发,故选择其为原型工具。
2.2 遇到的困难与解决办法(3分)
- 困难一:初次使用原型开发工具,缺乏经验。
- 困难二:手头没有合适美观的现成素材。
解决尝试:通过文档和视频学习,逐步上手实现原型设计。通过各个软件和搜索引擎的搜索查找,找到美观且风格接近的设计素材。
是否解决:是。
收获:世上无难事,只怕有心人。
2.3 原型作品链接
https://modao.cc/app/IrTCOyU9rjod98yMagrDLb
(共两页)
2.4 原型界面图片展示(6分)
- 主页:背景是明丽鲜艳的古风风格。项目名称《逍遥骰》以及文案“拂去尘烟,一念逍遥。”在正中间展示。往下三个卷轴风格的选项:龙争虎斗(pvp),投石拔距(pve),人外有人(在线对战)。
创新点:左上角三个小功能,打开bgm、切换主页背景、查看游戏规则(音效音乐功能展示还请移步github项目或bilibili视频查看)
- 对局页面:左下和右上分别是玩家A、B的头像和骰子区,中间是双方的九宫格棋盘。
创新点: - 左上角增加三个小功能分别是打开bgm、切换背景和返回主页
- 轮到某一方的回合时,该玩家的头像呼吸灯闪烁。
- 九宫格两旁统计每行得分,并以大写中文展示。
- 点数重复以及消除对方点数会有字母特效(共五种)并伴有音效。
三、编程实现
3.1 网络接口的使用
- 前端部分:
main.js文件:
//导入 axios 使我们可以发起axios请求
import axios from 'axios'
Vue.prototype.$axios = axios
axios.withCredentials = true
// 配置请求的跟路径 由后端提供 这样我们只要输入根路径后面的地址就OK (例如/login /home
axios.defaults.baseURL = 'http://127.0.0.1:5000'
调用接口页面文件Pve.vue(定义接口函数,发起post请求,通过this.getBChangeRow()即可调用):
//api
async getBChangeRow() {
const result = await this.$axios({
method: "post",
url: "/pve",
data: {
dataA: this.playerBData,
dataB: this.playerAData,
point: this.randb,
},
});
this.AIRow = res.data;
return res.data;
},
- 后端部分:
import re
from flask import Flask,request
from flask import jsonify
from flask_cors import CORS
import AIcontrol
app = Flask(__name__)
CORS(app,resources=r'/*',supports_credentials=True)
@app.route('/pve',methods=['POST'])
def aAI():
data=request.get_json()
print(data)
next_step=AIcontrol.AI_decide(data['dataA'], data['dataB'], data['point'])
print(next_step)
next_step={
'next_step': next_step
}
return jsonify(next_step)
3.2 代码组织与内部实现设计(类图)
- 前端部分:
- 后端部分:
3.3 说明算法的关键与关键实现部分流程图
-
前端部分:
游戏对局流程控制流程图如下:
-
后端部分:
-
后端的模块主要是提供交互接口和ai决策;
-
后端部分简单的交互接口使用flask库建立本地接口;
-
ai决策过程则是以贪心算法为主的简单策略:我们希望每一列都能得到最大分数,所以做决策时尽可能贴近最大的分数,同时对对手进行一定简单干扰,即消除决策。
3.4 贴出重要的/有价值的代码片段并解释
- 前端部分:
data部分:
//用对象包数组方式表示九宫格,对象的KEX三元素表示数组的三行
data() {
return {
playerAData: {
K: [],
E: [],
X: [],
},
playerBData: {
K: [],
E: [],
X: [],
},
}
用数组方法push()实现放置骰子的点数:
//this.randa为我们掷得的点数
pushAE() {
this.playerAData.E.push(this.randa);
this.changeRow = "E";
this.removeB();//remove方法即消除方法
},
用数组方法splice()实现点数的消除:
removeA() {
var row = this.changeRow;
for (let i = 0; i < this.playerAData[row].length; i++) {
if (this.playerAData[row][i] == this.randb) {
var removed = this.playerAData[row].splice(i, 1);
console.log("remove the " + removed + "!");
}
}
for (let i = 0; i < this.playerAData[row].length; i++) {
if (this.playerAData[row][i] == this.randb) {
var removed = this.playerAData[row].splice(i, 1);
console.log("remove the " + removed + "!");
}
}
至此,实现游戏的基本也是核心功能,点数的放置和消除,其他功能(流程控制、消除特效、得分统计、图形界面转化等)在此基础上通过增加判断条件变量,监听和获取九宫格数据,即可逐一实现(实现逻辑思路大于算法设计),具体各个判断变量的设置和使用以及九宫格监听思路,本博客篇幅有限还请移步github项目查看。
ps:为便于后端开发,九宫格数据重构为以下格式(数组本质是特殊的对象,重构后不影响已实现的任何功能)
playerAData: [[], [], []],
playerBData: [[], [], []],
- 后端部分:
后端的AI由简单策略组成:
获取每列未来预期最大能够获取的分数:比如列状态为[1],那么它未来填满时最大得分的状态为[1,6,6],分数为25,而状态[1,5]的未来最大得分为[1,5,5],分数为21。这些状态我们枚举保存。
def exp_score1(board,list):
maxn = 0
k = 1
for i in range(1, 7):
list.append(i)
score = exp_score2(board, list)
list.pop()
if (score > maxn):
maxn = score
k = i
board[str(list)] = score
return score
def exp_score2(board,list):
maxn=0
k=1
for i in range(1, 7):
list.append(i)
score = exp_score3(board, list)
list.pop()
if(score>maxn):
maxn=score
k=i
board[str(list)] = maxn
return maxn
def exp_score3(board,list):
score=0
num=[0,0,0,0,0,0]
for i in range(0,3):
num[list[i]-1]+=1
for i in range(0,6):
score+=num[i]*num[i]*(i+1)
board[str(list)]=score
return score
def exp_socre():
dict={}
list=[]
dict['[]']=-54
for x1 in range(1,7):
list.append(x1)
exp_score1(dict,list)
for x2 in range(1, 7):
list.append(x2)
exp_score2(dict, list)
for x3 in range(1, 7):
list.append(x3)
exp_score3(dict,list)
list.pop()
list.pop()
list.pop()
return dict
#嵌套获取每一种情况下的未来最大得分
将点数填入每列中并获取它未来的最大得分,将其与现在的未来最大得分相减,得到权重以比较,抉择接下来放在哪一列。这里我们希望它们的差值越小越好。
得分模块外,消除对手的干扰行为同样重要,但是消除对手的骰子有可能会减少分数收入,所以我们设定一定限制,不难发现,1,2这样的点数一般来说是不值得消除的。综合观察的情况,对程序予以一定阈值以操控消除决策。
以下是简单决策的环节:
for i in range(0,3):
if(list_count(my_data[i])==3):
print(i,":max!")
else:
my_max=exp_dict[str(my_data[i])]
his_max=exp_dict[str(his_data[i])]
my_data[i].append(point)
my_exp = exp_dict[str(my_data[i])]
my_data[i].pop()
ans=0
while point in his_data[i]:
his_data[i].remove(point)
ans+=1
his_exp = exp_dict[str(his_data[i])]
while ans:
ans-=1
his_data[i].append(point)
pd=my_exp-his_exp
pd2=my_max-his_exp
pd3=pd-pd2
score=Exp_add(my_data[i],point)+Exp_lost(his_data[i],point)#当前步收益
print(i,":",my_max - my_exp)
nt[i]=my_max
if score>=12:
set=i
situ=0
if situ==2:
if my_max - my_exp < change:
change = my_max - my_exp
set = i
elif my_max - my_exp == change:
if my_max>nt[set]:
set = i
3.5 性能分析与改进
因为是简单的贪心策略,后端的AI算法基本上只是 O(1)级别的时间复杂度,所以没什么需要优化的。
(描述你改进的思路,展示性能分析图和程序中消耗最大的函数)
3.6 单元测试
- 模拟前端的游戏流程,实现PVE以测试AI流程
3.7 贴出GitHub的代码签入记录,合理记录commit信息
四、总结反思
4.1 本次任务的PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | ||
Estimate | ·估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | ||
Analysis | ·需求分析 (包括学习新技术) | 300 | 250 |
Design Spec | ·生成设计文档 | 60 | 60 |
Design Review | ·设计复审 | 15 | 20 |
Coding Standard | ·代码规范 (为目前的开发制定合适的规范) | 30 | 15 |
Design | ·具体设计 | 60 | 60 |
Coding | ·具体编码 | 1000 | 800 |
Code Review | ·代码复审 | 120 | 120 |
Test | ·测试(自我测试,修改代码,提交修改) | 50 | 150 |
Reporting | 报告 | ||
Test Repor | ·测试报告 | 20 | 15 |
Size Measurement | ·计算工作量 | 20 | 15 |
Postmortem & Process Improvement Plan | ·事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1725 | 1605 |
4.2 学习进度条(每周追加)(2分)
第N周 | 新增代码(行) | 累计代码(行)) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 700 | 700 | 8 | 8 | 熟练用vue框架搭建项目 |
2 | · 800 | 1500 | 10 | 18 | 学会动态修改vue背景图,熟练使用vue动画,掌握后端接口书写的基本流程 |
3 | 700 | 2200 | 10 | 28 | 熟练使用flask库实现后端接口开发 |
4.3 最初想象中的产品形态、原型设计作品、软件开发成果三者的差距如何?
产品形态和预期的有些差别,最初本来想做个很酷的小游戏。有丰富的音效和动画效果,交互感强,功能丰富,页面信息清晰明了又面面俱到,最好是小程序。但是实际开发过程中考虑到技术栈(前端动画效果的实现,页面各类高质量素材获取,后端数据库调用,对跨域的处理等等。)以及完成度(学习成本大,可分配时间有限。)很多想法都被pass掉了。原型设计上我们根据自身能力对预期实现功能进行了考量,原型设计作品和软件开发成果很接近,基本还原了原型设计,还额外补充了一些功能。这点还算是比较满意。
4.4 评价你的队友
- 评价Eson:
谢以盛是一个很好的队友。积极配合我们两人小队完成本次作业,积极和我进行前后端对接,互相调试找出错误。不断优化自己的算法。他一丝不苟,热情配合的精神值得我学习。需要改进的地方:第二周尾正式进行后端的开发学习,这个时间可以再提早一些,可以为后续的对接配合可以提供更多弹性空间和容错。 - 评价Yummy:
杨明是一个一个很优秀的队友啊(喜)。他对于本次作业非常上心,在前端工作上投入了大量时间,对于自己工作的热情这一点很值得我学习。需要改进的地方:队友在一些细节上会钻牛角尖而导致自己进入一些耗费很大的状态,希望能够自我调整一下。
4.5 结对编程作业心得体会
心得体会:
- Yummy:这次作业比较有挑战性,项目比较完整,开发过程中基于产品的完成度要做很多考虑,例如要满足基本功能、要提升用户交互感、游戏对局的流程控制要符合逻辑等等。在前端开发上还尝试了不少此前没有尝试的功能开发。还是有不少收获的,但是也看到自己能力有限,很多想法也会因为自己的技术水平而被局限,很多算法和布局以及功能实现并没有尽善尽美,不过遗憾也会是自己继续奋斗学习的动力!继续加油!
- Eson:预期做这次作业时没有什么概念,上手了才发现问题多多。在我负责后端处理接口时,因为不了解前端数据结构,给队友带来一些意义不明的错误,在我们实现前后端实现对接之间沟通不顺利;另外就是能力局限导致不能完成预期项目,没能成功抽象作业的游戏流程,对深度学习的算法也不够熟悉,只能手动写策略。在这次作业完成过程中,对所学的知识是一次很好的检验,今后也要再接再厉,继续努力。