GitHub Link: GitHub
Bili Bili Link: 哔哩哔哩
一、结对探索
1.1 队伍基本信息
结对编号:40;队伍名称:知天易逆天男;
学号 | 姓名 | 作业博客链接 | 具体分工 |
---|---|---|---|
032002542 | 朱智浩 | 这是一个链接 | 游戏原型实现 |
032002539 | 张凯昕 | https://bbs.csdn.net/topics/608559866 | ui设计,原型设计,前后端编程实现,算法设计 |
1.2 描述结对的过程
1.3 非摆拍的两人在讨论设计或结对编程过程的照片
二、原型设计
2.1 原型工具的选择
原型工具选择了墨刀。选择墨刀的原因是刚开始用axure pr8,但是后来发现这个工具巨无敌难使用,然后询问同学就使用了墨刀。
2.2 遇到的困难与解决办法
第一次做原型,csdn查了点资料 ,上面说一般Axure和墨刀使用的比较多,但是大多在踩一捧一(Axure更XXX,墨刀怎么XXX)。于是使用Axure PR8。去哔哩哔哩搜索Axure使用教程,学完后发现很多功能都没有,很多实用的工具居然都要自己制作,大为震惊!!(很多功能制作不出来,且极为耗时与不好用)果断放弃,发现同学大多使用墨刀,上手后发现真香,甚至不用学,摸索一下就会用。强推!!!!!Axure滚啊。收获:一定要调研清楚,实用便捷最重要!!!!
2.3 原型作品链接
原型链接:这是一个原型链接捏
2.4 原型界面图片展示
注册界面捏,点击转提交进入登陆界面
登陆界面,点击提交进入欢迎界面
点击对战可选择“本机对战”“匹配对战”“人机对战”
点击“游戏规则”进入可进入“新手教学”
有排行榜界面,可以看看你滴排名
有“认输”以及“托管”功能
亲亲输了呢,这边建议再来一把哦😗😗
三、编程实现
3.1 网络接口的使用
使用了ajax与websocket,websocket主要用于匹配对决的实现,通过websocket来不断发送地图信息以及对手的落子,ajax用于除游戏外一些不经常发送的请求,例如登陆注册等。
3.2 代码组织与内部实现设计
前端部分
后端部分
backend部分
matchingsystem部分
3.3 说明算法的关键与关键实现部分流程图
· 由于我不会机器学习,所以只能用普通的算法实现
· 算法的实现主要是贪心,每次下子时遍历一遍,找出分差最有利的落子位置
· 若有复数位置,先落于行空格最多位置,避免将所有位置堵死,使得原本可以消除对方的落子没得落子
· 再判断点数与对方行空格最少的位置
· 使点数较小的位置落于对方行空格最大的地方,被对方消除时损失较小
· 使点数较大的位置落于对方行空格最小的地方,不容易被消除
3.4 贴出重要的/有价值的代码片段并解释
package com.example.matchingsystem.service.impl.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
@Component
public class MatchingPool extends Thread{
private static List<Player> players = new ArrayList<>();
private ReentrantLock lock = new ReentrantLock();
private static RestTemplate restTemplate;
private final static String startGameUrl = "http://localhost:3000/pk/start/game/";
@Autowired
public void setRestTemplate(RestTemplate restTemplate){
MatchingPool.restTemplate = restTemplate;
}
public void addPlayer(Integer userId,Integer rating){
lock.lock();
try {
for (Player player:players){
if (player.getUserId().equals(userId)){
return;
}
}
players.add(new Player(userId,rating,0));
}finally {
lock.unlock();
}
}
public void removePlayer(Integer useId){
lock.lock();
try {
List<Player> newPlayers = new ArrayList<>();
for (Player player:players){
if (!player.getUserId().equals(useId)){
newPlayers.add(player);
}
}
players = newPlayers;
}finally {
lock.unlock();
}
}
private void increaseWaitingTime(){//所有玩家等待时间加一
for (Player player:players){
player.setWaitingTime(player.getWaitingTime() + 1);
}
}
private boolean checkMatched(Player a,Player b){
int ratingDelta = Math.abs(a.getRating() - b.getRating());
int waitingTime = Math.min(a.getWaitingTime(),b.getWaitingTime());
return ratingDelta <= waitingTime *10;
}
private void sendResult(Player a,Player b ){
MultiValueMap<String,String> data = new LinkedMultiValueMap<>();
data.add("a_id", a.getUserId().toString());
data.add("b_id",b.getUserId().toString());
restTemplate.postForObject(startGameUrl,data,String.class);
}
private void matchPlayers(){
System.out.println("match players: " + players.toString());
boolean[] used = new boolean[players.size()];
for (int i = 0; i < players.size(); i ++){
if (used[i]) continue;
for (int j = i + 1;j < players.size(); j++){
if (used[j]) continue;
Player a = players.get(i),b = players.get(j);
if (checkMatched(a , b)){
used[i] = used[j] = true;
sendResult(a , b);
break;
}
}
}
List<Player> newPlayer = new ArrayList<>();
for (int i = 0; i < players.size(); i ++){
if (!used[i]){
newPlayer.add(players.get(i));
}
}
players = newPlayer;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
lock.lock();
try {
increaseWaitingTime();
matchPlayers();
}finally {
lock.unlock();
}
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
}
这部分代码是用于匹配池的实现
restTemplate用于于另一模块backend交互
addPlayer 当前端发送请求时,程序先遍历匹配池,发现没有该用户,再把用户加入匹配池中,
防止用户自己匹配到自己。
removePlayer当前端发送请求或者匹配到对象时,将玩家移除匹配池
increaseWaitingTime,checkMatched,matchPlayers分别用于当玩家等待时间增加时,checkMatched中允许的分差也会逐渐增大,当增大一定程度,两个对象就会匹配,并更新匹配池
sendResult是在当匹配成功时将匹配信息发送给backend
run则是在每秒扫描一次匹配池,执行程序
由于该程序是异步程序,所以需要lock函数对函数进行上锁。
3.5 性能分析与改进
占用资源最多的函数是tomcat,张凯昕的优化的思路是将一些可以在前端实现的功能直接在前端实现减少前后端的交互。
3.6 单元测试
利用apipost测试接口,用sout以及assert进行单元测试
3.7 贴出GitHub的代码签入记录,合理记录commit信息
四、总结反思
4.1 本次任务的PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 5 | 5 |
· Estimate | · 估计这个任务需要多少时间 | 5 | 5 |
Development | 开发 | 5113 | 5796 |
· Analysis | · 需求分析 (包括学习新技术) | 2700 | 2800 |
· Design Spec | · 生成设计文档 | 15 | 15 |
· Design Review | · 设计复审 | 5 | 3 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 3 | 3 |
· Design | · 具体设计 | 30 | 15 |
· Coding | · 具体编码 | 2000 | 2500 |
· Code Review | · 代码复审 | 60 | 60 |
· Test | · 测试(自我测试,修改代码,提交修改) | 300 | 400 |
Reporting | 报告 | 85 | 50 |
· Test Report | · 测试报告 | 15 | 15 |
· Size Measurement | · 计算工作量 | 10 | 5 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 60 | 30 |
· 合计 | 5203 | 5851 |
4.2 学习进度条
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 2 | 2 | 初步了解本次作业内容,发现这些技术一个都不会 |
2 | 0 | 0 | 15 | 17 | 学习Axure,并用它做一些简单的原型设计,发现Axure真的一点都不好用,果断放弃(过于折磨) |
3 | 0 | 0 | 12 | 29 | 制作完游戏原型,额,至少会用了,还学了点html+css |
4.3 最初想象中的产品形态、原型设计作品、软件开发成果三者的差距如何?
游戏原型和设计出来的结果无法完全相同,制作的过程中有可能加入一些奇思妙想,这是设计时无法完全预测的,开发成果往往优于设计作品
4.4 评价你的队友
· 张凯昕
值得学习的地方:临危不惧,步步为营
需要改进的地方:过于临危不惧
· 朱智浩
值得学习的地方:作业开始第一天就火力全开,一周多一点点就全搞定了,可靠捏
需要改进的地方:需要多沟通,不然有时候不太清楚需求
4.5 结对编程作业心得体会
· 张凯昕
作业难度**没做前:**好难好难好难好难好难好难 做的时候:好难好难好难好难好难好难好难好难好难好难好难好难 做完后:就这?能不能再难点?
完成感受
心理:
如释重负,身心愉悦
身体:
不堪重负,面色憔悴 我终于不用天天背那笔记本去教室打代码了,太重了 代码模块 遇到问题,百度一下,百度不行,问问同学,同学不会,问问佛祖,然后Debug一整天,所幸最后还是顺利解决
学习启发
一开始的学习都是在模仿,知识其实并没有太多的吸收,真正吸收知识的时候,其实是模仿着模仿着,模仿出bug的时候,为了debug,必须将代码的逻辑捋清楚,才可以改好bug
之后想学点动画,倒也不用特别深入,够用就行,看别人的作业的骰子会动,很是羡慕,我也想写。更多学习心得请移步 第一次独立完成项目的经验之谈
· 朱智浩
作业难度
偏高,有很多方面的知识完全不太了解。加入从无到有实现一个能看得过去的产品,实在有些难
完成后感受
真不错,我也可以去当一个产品经理了(
对之后学习或软件开发的启发
刚入门时搜集资料很重要,盲人摸象事倍功半,还有就是早点开始写作业