GitHub:https://github.com/same-dust/roll_game
B站:https://b23.tv/utRdWUA
一、结对探索
1.1 队伍基本信息
结对编号: 队伍名称;
学号 | 姓名 | 作业博客链接 | 具体分工 |
---|---|---|---|
102101315 | 王辉凡 | UI设计、算法设计、UI素材寻找、博客撰写 | |
102101322 | 吴文景 | https://bbs.csdn.net/topics/617401533 | 前端、算法设计、AI算法、项目代码规划、页面交互 |
1.2 描述结对的过程
一个宿舍的,自然而然组队了
1.3 非摆拍的两人在讨论设计或结对编程过程的照片
二、原型设计
2.1 原型工具的选择
墨刀。首先最重要的一点是墨刀免费且方便使用。其次,墨刀的免费版本自带部件、交互、功能,基本上能满足网页端产品原型的设计要求,其控件的拖拉、大小的调整,都会自然的去匹配相应的母版大小。因此在里面可以首先选择相应的设备布局,减少了不少工作环节。然后是素材库比较丰富,墨刀内置了很多的素材库,做这类原型的时候会方便很多,易用性也强很多。另外他的学习成本也比较低:一般大概半天就能掌握所有基础操作。所以几个原型工具体验下来后选择了墨刀。
2.2 遇到的困难与解决办法
困难:
1、素材查找:网上的资源很多但也很杂,想要找到心仪的素材需要花费一定的时间。
解决办法:先找到喜欢的游戏页面布局进行模仿,通过ProKnockout 进行抠图和素材编辑,Procreate自行绘画素材,小图标从阿里图标库下载。
2、初次使用: 对于初次使用墨刀的我来说,掌握软件的操作和功能可能需要一定时间和学习。这导致设计进度较慢或出现一些技术性错误。
解决办法: 积极地进行墨刀相关的学习和培训,阅读文档和教程,以快速掌握软件的使用技巧。同时,我还会与其他使用过的朋友进行交流和分享经验,以便更好地利用墨刀进行原型设计。
收获:最直接的收获就是学会了原型设计以及墨刀的使用;遇到的一些困难,经历,这些经验将有助于我们
2.3 原型作品链接
https://modao.cc/proto/aumZ1OBys21pgjMVAtBEaD/sharing?view_mode=device&screen=rbpTrnOlM5rWOCtrE&canvasId=rcTrnUyd1KS9DrVg #孤注一掷-分享
2.4 原型界面图片展示
页面一:
欢迎界面,选择游戏模式后跳页面二。
页面二:选择游戏局数后跳转页面三
页面三:开始游戏。
页面四:选择赔率
页面五:结算页面
三、编程实现
3.1 网络接口的使用
网络编程的实现主要是使用到了socket.io来实现两个客户端的同步通讯
3.2 代码组织与内部实现设计
就是尽量解一下嵌套吧,因为游戏玩家做一个动作就需要更新相应的数据,数据容易混在一起,所以就让每个玩家都都独立的数据。内部实现设计就基本是采用封装函数的形式,让代码模块化,可复用性高一点
3.3 说明算法的关键与关键实现部分流程图
算法的关键就是如何使用全局变量控制程序在用户‘做一件事情’后,程序该调用哪种函数(也就是程序如何响应用户的交互)
3.4 贴出重要的/有价值的代码片段并解释
这个代码用来重置页面,并刷新回合数,局数等,我认为这个比较重要是因为,游戏的多次游玩并不能单靠循环实现,有些地方要resset,而有些地方是update,所以,当一轮游戏结束后,如何重新渲染页面就显得比较重要.
function settlement(){
// 隐藏网页中的一些元素
document.querySelector('.desktop').style.display = 'none'; // 隐藏名为"desktop"的元素
document.querySelector('.odds_select').style.visibility='hidden'; // 隐藏名为"odds_select"的元素的可见性
// 从网页中获取双方的骰子点数值并存入两个数组arr1和arr2
for(let i=1;i<=5;i++){
arr1[i-1]=document.querySelector(`.player-area.left`).querySelector(`.dice${i}`).dataset.value; // 获取左侧玩家区域中骰子点数值
arr2[i-1]=document.querySelector(`.player-area.right`).querySelector(`.dice${i}`).dataset.value; // 获取右侧玩家区域中骰子点数值
}
score(); // 调用score()函数,计算得分
// 延迟2秒后执行以下操作,等待得分计算完成并进行一些重置操作
setTimeout(function(){
reset(); // 调用reset()函数,对需要重置的地方进行重置,确保上一轮的操作完全清除,不会影响下一轮
current_round++; // 增加当前回合数
document.querySelector('h1').innerText=`Round:${current_round}`; // 更新网页中显示的当前回合数
document.querySelector('.Rn-rounds').innerText=`剩余回合:${round-current_round}`; // 更新网页中显示的剩余回合数
document.querySelector('.desktop').style.display = 'block'; // 显示名为"desktop"的元素,恢复显示网页内容
},2000); // 设置延迟时间为2秒
}
首先隐藏了一些网页中的元素,然后从网页中获取双方骰子的点数值,并存入两个数组中。接下来调用score()函数来计算得分。之后,通过延迟2秒的操作,在重置一些状态后,增加当前回合数并更新网页中的相关显示内容。最后,显示之前隐藏的网页元素,恢复显示网页内容。
function score(){
let base1=0; // 玩家1的基础得分
let base2=0; // 玩家2的基础得分
let judge1=[0,0,0,0,0,0]; // 玩家1每个点数出现的次数
let judge2=[0,0,0,0,0,0]; // 玩家2每个点数出现的次数
let bonus1=0; // 玩家1的额外得分
let bonus2=0; // 玩家2的额外得分
let double1=0; // 玩家1的双对数量
let double2=0; // 玩家2的双对数量
let triple1=0; // 玩家1的三连数量
let triple2=0; // 玩家2的三连数量
let straight1=0; // 玩家1的顺子数量
let straight2=0; // 玩家2的顺子数量
// 计算基础得分和点数出现次数
for(let i=0;i<=4;i++){
base1+=Number(arr1[i]); // 累加玩家1骰子点数
base2+=Number(arr2[i]); // 累加玩家2骰子点数
judge1[Number(arr1[i])-1]++; // 对应的点数出现次数加1
judge2[Number(arr2[i])-1]++; // 对应的点数出现次数加1
}
// 计算玩家1的额外得分
for(let i=0;i<=5;i++){
if(judge1[i] === 1){
straight1++; // 记录顺子出现的点数数量
}
if(judge1[i] === 2){
double1++; // 记录双对出现的点数数量
}
if(judge1[i] === 3){
triple1++; // 记录三连出现的点数数量
}
if(judge1[i] === 4){
bonus1+=40; // 四连,额外得40分
break; // 只要出现了四连,不需要再判断其他情况
}
if(judge1[i] === 5){
bonus1+=100; // 五连,额外得100分
break; // 只要出现了五连,不需要再判断其他情况
}
}
// 根据额外得分情况,给玩家1添加额外得分
if(double1 === 1 && triple1 === 1){
bonus1+=20; // 葫芦,额外得20分
}
else if(double1 === 2 && triple1 === 0){
bonus1+=10; // 双对,额外得10分
}
else if(double1 === 0 && triple1 === 1){
bonus1+=10; // 三连,额外得10分
}
// 判断玩家1是否出现顺子,并给予相应的额外得分
if(straight1 === 5){
if(judge1[0] === 0){
if(arr1[0] === '2' && arr1[1] === '3' && arr1[2] === '4' && arr1[3] === '5' && arr1[4] === '6'){
bonus1+=60; // 大顺子,额外得60分
}
}
else if(judge1[5] === 0){
if(arr1[0] === '1' && arr1[1] === '2' && arr1[2] === '3' && arr1[3] === '4' && arr1[4] === '5'){
bonus1+=60; // 大顺子,额外得60分
}
}
else if(judge1[1] === 0){
if(arr1[0] === '1' && arr1[1] === '3' && arr1[2] === '4' && arr1[3] === '5' && arr1[4] === '6'){
bonus1+=30; // 小顺子,额外得30分
}
}
else if(judge1[2] === 0){
if(arr1[0] === '1' && arr1[1] === '2' && arr1[2] === '4' && arr1[3] === '5' && arr1[4] === '6'){
bonus1+=30; // 小顺子,额外得30分
}
}
else if(judge1[3] === 0){
if(arr1[0] === '1' && arr1[1] === '2' && arr1[2] === '3' && arr1[3] === '5' && arr1[4] === '6'){
bonus1+=30; // 小顺子,额外得30分
}
}
else if(judge1[4] === 0){
if(arr1[0] === '1' && arr1[1] === '2' && arr1[2] === '3' && arr1[3] === '4' && arr1[4] === '6'){
bonus1+=30; // 小顺子,额外得30分
}
}
}
// 计算玩家2的额外得分
for(let i=0;i<=5;i++){
if(judge2[i] === 1){
straight2++; // 记录顺子出现的点数数量
}
if(judge2[i] === 5){
bonus2+=100; // 五连,额外得100分
break; // 只要出现了五连,不需要再判断其他情况
}
if(judge2[i] === 4){
bonus2+=40; // 四连,额外得40分
break; // 只要出现了四连,不需要再判断其他情况
}
if(judge2[i] === 3){
triple2++; // 记录三连出现的点数数量
}
if(judge2[i] === 2){
double2++; // 记录双对出现的点数数量
}
}
// 根据额外得分情况,给玩家2添加额外得分
if(double2 === 1 && triple2 === 1){
bonus2+=20; // 葫芦,额外得20分
}
else if(double2 === 2 && triple2 === 0){
bonus2+=10; // 双对,额外得10分
}
else if(double2 === 0 && triple2 === 1){
bonus2+=10; // 三连,额外得10分
}
// 判断玩家2是否出现顺子,并给予相应的额外得分
if(straight2 === 5){
if(judge2[0] === 0){
if(arr2[0] === '2' && arr2[1] === '3' && arr2[2] === '4' && arr2[3] === '5' && arr2[4] === '6'){
bonus2+=60; // 大顺子,额外得60分
}
}
else if(judge2[5] === 0){
if(arr2[0] === '1' && arr2[1] === '2' && arr2[2] === '3' && arr2[3] === '4' && arr2[4] === '5'){
bonus2+=60; // 大顺子,额外得60分
}
}
else if(judge2[1] === 0){
if(arr2[0] === '1' && arr2[1] === '3' && arr2[2] === '4' && arr2[3] === '5' && arr2[4] === '6'){
bonus2+=30; // 小顺子,额外得30分
}
}
else if(judge2[2] === 0){
if(arr2[0] === '1' && arr2[1] === '2' && arr2[2] === '4' && arr2[3] === '5' && arr2[4] === '6'){
bonus2+=30; // 小顺子,额外得30分
}
}
else if(judge2[3] === 0){
if(arr2[0] === '1' && arr2[1] === '2' && arr2[2] === '3' && arr2[3] === '5' && arr2[4] === '6'){
bonus2+=30; // 小顺子,额外得30分
}
}
else if(judge2[4] === 0){
if(arr2[0] === '1' && arr2[1] === '2' && arr2[2] === '3' && arr2[3] === '4' && arr2[4] === '6'){
bonus2+=30; // 小顺子,额外得30分
}
}
}
const final1 = base1 + bonus1; // 玩家1的最终得分
const final2 = base2 + bonus2; // 玩家2的最终得分
let str = document.querySelector('.Total-odds').innerText; // 获取网页中显示的赔率字符串
let str1 = document.querySelector('.chip.left').innerText; // 获取网页中显示的玩家1筹码字符串
let str2 = document.querySelector('.chip.right').innerText; // 获取网页中显示的玩家2筹码字符串
const odd = Number(str.slice(4)); // 解析赔率的数值部分
let chip1 = Number(str1.slice(3)); // 解析玩家1的筹码数量
let chip2 = Number(str2.slice(3)); // 解析玩家2的筹码数量
let final; // 最后的得分差额
if(final1 > final2){
final = (final1 - final2) * odd; // 计算得分差额
chip1 += final; // 玩家1获得得分差额的筹码
chip2 -= final; // 玩家2失去得分差额的筹码
}
else{
final = (final2 - final1) * odd; // 计算得分差额
chip1 -= final; // 玩家1失去得分差额的筹码
chip2 += final; // 玩家2获得得分差额的筹码
}
// 在网页中更新最新的筹码数量
document.querySelector('.chip.left').innerText = `筹码:${chip1}`;
document.querySelector('.chip.right').innerText = `筹码:${chip2}`;
// 打印最终得分和玩家筹码数量,用于调试
console.log(final1);
console.log(final2);
console.log(chip1);
console.log(chip2);
}
首先,我定义了一系列的变量用于存储得分和各种情况的统计数量。然后,通过循环遍历玩家的骰子点数,累加基础得分并统计每个点数出现的次数。接下来,根据点数出现的次数和规则,计算额外得分,包括连续的顺子和相同点数的组合(如双对、三连等)。之后,根据赔率和得分差额,更新玩家的筹码数量。最后,通过修改网页中相应的元素,更新显示最新的筹码数量。
3.5 贴出GitHub的代码签入记录,合理记录commit信息
四、总结反思
4.1 本次任务的PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | |
Estimate | 估计这个任务需要多少时间 | 1800 | 2000 |
Development | 开发 | 1500 | 1800 |
Analysis | 需求分析 (包括学习新技术) | 200 | 150 |
Design Spec | 生成设计文档 | 20 | 30 |
Design Review | 设计复审 | 10 | 10 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 15 |
Design | 具体设计 | 15 | 20 |
Coding | 具体编码 | 500 | 600 |
Code Review | 代码复审 | 20 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 20 | 30 |
Reporting | 报告 | 200 | 300 |
Test Repor | 测试报告 | 25 | 25 |
Size Measurement | 计算工作量 | 30 | 25 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 30 | 50 |
合计 | 4410 | 5120 |
4.2 学习进度条(每周追加)
吴文景
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 780 | 780 | 31 | 31 | 学习前端知识,html |
2 | 895 | 1675 | 32 | 63 | 学习前端,css,js |
3 | 945 | 2618 | 32 | 100 | 交互式设计,socket.js |
4 | 547 | 3k+ | 32 | 132 | websocket,node.js后端 |
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 540 | 540 | 31 | 31 | 学习后端知识,springboot |
2 | 987 | 1527 | 32 | 63 | 学习后端,java stream流 |
3 | 991 | 2518 | 32 | 100 | 交互式设计,websocket |
4 | 668 | 3186 | 28 | 128 | ui,原型设计 |
4.3 最初想象中的产品形态、原型设计作品、软件开发成果三者的差距如何
最初想法比较丰富,比如背景音乐和多人随机匹配啥的,但是理想很丰满,现实很骨感。知识储备和编程能力始终是一个大坑,其中web编程困扰了我们许多天,后端服务器搭建起来到最后也没太搞明白,只能舍弃掉这种想法。最后只实现了两个人联机对战。其次是原型设计,两个大老粗的艺术细胞都接近负数,不会ps也不会设计,最后在网上找来找去找了几张图贯穿全局。总的来说,我们基本都是从零开始学起,包括前端后端和原型设计,花费了许多时间在上面,最后也算勉强完成作业。
4.4 评价你的队友
102101315 王辉凡:
队友的技术能力十分突出。他不仅对编程语言和开发工具的掌握十分熟练,而且有着解决复杂问题的能力,在前端页面开发和大部分js的编写完成的很好。在面临困难和挑战时,他总是能迅速找到解决方案,为我们的作业的完成提供重要支持。他的思维方很全面。
102101322吴文景:
我的队友是一个非常勤奋和有毅力的人。在这次作业中,他始终保持积极的态度,全身心投入到后端开发和原型设计中。即使遇到困难和挫折,他也从不轻言放弃,而是坚持不懈地寻求解决方案。
4.5 结对编程作业心得体会
102101315王辉凡:
- 首先,这是我第一次完整的开发一个小游戏,给我带来了许多经验和挑战。对于没有项目经验的小白来说,这次作业让我系统性的运用所学的知识,以及不断地学习新的知识,包括后端框架,原型设计,stream数据流的学习与使用。都让我拥有了一次深刻的体验。
- 其次,这也是我第一次结对完成任务。结对编程的精髓在于团队的协作和沟通。每个人都有自己的长处和短处,只有通过互相学习,互相补充,才能达到最好的效果。在我们的项目中,我主要负责了页面的设计和交互,而我的队友则主要负责了逻辑处理以及页面实现。我们的工作虽然各有侧重,但是在整个开发过程中,我们始终保持密切的交流,不断地分享我们的想法和见解。
+最后,在编程过程中,我们难免会遇到各种预料之外的问题。但是,正是通过解决这些问题,我们才能更好地理解编程的实际操作。摇骰子的逻辑看似简单,但在实际操作中却遇到了各种困难,如如何设计摇骰子的算法,如何处理用户的不正当操作等。通过不断地试错和学习,我们最终成功地完成了这个项目。
通过这次结对编程的作业,我不仅提高了自己的编程技能,也深刻理解了团队协作的重要性。
102101322吴文景:
首先为了完成这次作业,我深入地学习和了解了前端三件套(js,html,css)的知识,以及websocket网络编程的知识,让我的技能点多了许多,我的编程能力也得到了一定的提高。
其次,这是我头一次与他人结对编程完成作业,这给我带来了许多收获和启发。它让我明白了协作和沟通的重要性,因为与队友密切合作是成功的关键,即使遇到挑战,通过共同讨论和解决问题,我学到了更多,获得了更好的提升。在解决代码模块异常时,及时的代码审查和互相提供反馈是关键。最重要的是,结对编程让我认识到在软件开发中团队协作的价值。它也启发了我更多关于不同编程方法和技术的学习,以改进我的技能。通过互相学习,我觉得更有信心应对未来的编程项目。
最后,能够完成这份作业让我感到十分的愉悦,满满的成就感是前所未有的。