结对编程作业
https://github.com/Jimase/Software_Engineering_Team2
我的博客链接
队友的博客链接
姓名 | 分工 |
---|---|
翁敏 | ui,js版本代码,原型、代码测试、代码格式规范 |
苏伟煌 | 算法,主体框架构建,博客,uml类图设计,Github撰写、源码管理、外卖搬运、端茶倒水 |
一、原型设计
1、链接与开发工具说明
- 原型链接
- pigTail采用的原型开发工具是:Axure,ps
2、原型展示
1、 初步原型由以下几个页面构成:
- 登陆页面
- 对局选择页面
- 模式选择页面
- 人机对战难度选择页面
- 在线对战操作选择页面
- 加载与等待页面
2、登录页面
- 有输入账号密码,密码为掩码构成,此外细节较为丰富,拥有“记住我”,“忘记密码”,音乐,设置,提示功能。输入正确账号密码后跳转到游戏模式选择界面。交互逻辑困难主要靠参考。
3、人机对战选择页面
4、人机对战难度选择页面
- 若选择在线对战,则进入在线对战模块,该页面基本细节均实现了,有创建对局,加入对局等功能(在线对战原型设计确实走心了)
- 加入房间页面提供了目前空闲的uuid,用户可以点击加入房间,也可以选择查找uuid,若uuid存在,则显示出房间供用户选择加入与否。此外还有该原型界面加入了一些细节,如刷新信息,随机加入,回到主页等功能
- 玩家在选择模式后,即将进入房间对局页面,期间会有3秒的动画等待过渡,期间可以选择退出等待,释放对局。(因为实际的接口并没有给予我们释放对局的功能,此处仅做展示)
3、后期原型优化。
- 针对原型设计中的不足和美观缺陷做了一定的优化🌟🌟🌟
- 卡牌选取特效
- 隔空嘲讽特效(可惜接口不允许,只能本地隔空对线)
- 圆框登录界面看起来会更舒服一些
- 多多沟通:最大的问难还是设计原型必须有可行性,前后端相互商诠。
- 审美的缺陷只能靠模仿了。
- Axure不会用不知道算不算困难,总之只能百度了,也算解决了问题吧。
二、原型设计实现
0、后期原型优化:
-
首先是主页面和选择页面:按钮更加圆滑了。😑
-
卡牌做了显示特效
-
隔空对线功能(当然,接口没有给予我们对话功能😤😤,真是太可惜了😂😂😂,静音啥的以及倒咖啡、对线是游戏的灵魂,怎么能没有呢❗❗❗❗)
-
1、代码实现思路
(1)网络接口的使用:
我们将测试组的同学们提供的接口文档细致阅读后,对于游戏的基本接口信息交互有了充分认知后,设计了一个类用于对每种接口的核心信息做了分类,以及接口信息返回做了分类,具体如下、对于异常状况做了相应的异常处理、否则可能会出现由于少量的网络波动和非理想状况(如:玩家登陆失败、网络波动导致程序终止、因此必须考虑所有可能的返回参数)
接口简称 | 接口参数需求 | 可能的返回状况 |
---|---|---|
登录连接 | student_id 、password |
登陆成功、登录失败(含网络超时) |
登录连接 | student_id 、password |
登陆成功、登录失败(含网络超时) |
加入对局 | token 、uuid |
加入成功、失败(对局不存在、无法加入(人满)、网络超时) |
创建对局 | token |
创建成功、创建失败 |
获取上步信息 | token 、uuid |
获取成功、网络超时 |
执行玩家操作 | token 、uuid |
执行成功、失败 |
获取上步操作 | token 、uuid |
获取成功、失败 |
- 游戏运行过程中的调用:
- 为了确保对于所有状况的应对便于处理、我们对接口函数要求其返回整个返回信息字典、而非几个参数、举例来说、以获取上部操作为例、我们初步的做法是:
import requests
import json
def get_last(token, uuid):
url = 'http://172.17.173.97:9000/api/game/' + uuid + '/last'
header = {'Authorization': token, 'Content-Type': 'application/json'}
r = requests.get(url, headers=header,timeout=5)
user_dict = json.loads(r.text)
# print(user_dict)
return user_dict
- 这样做的好处是显而易见的:get_last接口需要应对的返回结果很多、如果只使用
user_dict['data']['last_code']
某次偶发性的网络波动导致user_dict['data']
根本不存在、没能正确处理而导致了程序的异常终止是非常糟糕的。而坏处也很明显将庞大的返回信息的处理交给了后续的程序、也加重了工作量。
(2)代码组织与内部实现设计(类图)
- 总体UML类图框架:
- 实际上有一些组件可以实现python自动生成类图(当然这肯定不是让我们写完代码再画类图,这里也放出来展示下、以玩家类和game的交互为例子,pygame是我安装的外部库,然而pyreverse也能逆向工程生成类图,这一小段可以与上图的结构做对比、说明类图没有画错)
参考
(3)算法的关键与关键实现部分流程图
- AI算法的核心部分在于我们使用了一个矩阵来储存玩家的手牌状态
- 即实现了搜索树的便利,也提供了一定的潜在训练可行性
(4)重要的/有价值的代码片段
- 在AI的开发过程中,注意到:当某一方玩家的牌数对比对手方少到一直翻牌也不会输时(即该玩家的牌数+1+放置区+(卡组区-1)*2<对手手牌-卡组区)、搜索结果并不能返回一直翻牌的决策、反而更容易输。人为加入特判后、测试了30局,21:9胜率远高于原本的版本:
def solusion(center_card,card_ai,card_people,ans):
#略去部分代码
lc_show = 'U' #中心排队的花色
la = len(card_ai) #AI的手牌
lp = len(card_people)#对手的手牌
lc = len(center_card)#放置区的手牌数
#具体特判
if la == 0 :
ans[0] = '0'
ans[1] = ''
return 0
elif la + 1 + (lo-1)*2 < lp - lo + 1 :
ans[0] = '0'
ans[1] = ''
return 0
else:
pass
#ans数组是反会的具体操作ans[0]='0'表示翻牌操作。
(5)性能分析与改进
- 以本地的人人对战的性能分析为例子
- Coverage(覆盖率):
- Threading Graph(进程效率测试,整体运行状态平稳):
- 性能分析:
可以看到,大部分的调用还是集中在画面的渲染,毕竟一张图片的大小可以比肩上千万行代码。
(6)具体的改进的思路
- 具体来说还是有限优化画面渲染部分,详细阅读了pygame的文档后,引入了设置模块
# -*- coding: utf-8 -*-
# @Time : 2021/10/22 23:29
# @Author : Jimase
# @File : setting.py
from pygame import display
class Settings:
def __init__(self):
self.screen_width = 1800
self.screen_height = 900
display.set_caption("PiG TAiL--")
self.fps = 90 //多次测试,这是最适合我自己电脑的fps
self.hand_card_distance = 70
(7)性能分析图和程序中消耗最大的函数
- cProfile是Python标准库中内置的性能分析模块,我们也选择了这个工具对本地和联网对战时候的游戏运行进程做了测试。
- cProfile也能生成对应的可视化图,具体如下
- 进一步验证了,性能中占比最多的是画面渲染
从性能分析图来看,时间消耗最多的模块莫过于游戏的的渲染功能,这也是选用pygame的缺点,换取了编程的便利的同时也带来了更大的难以弥补的遗憾😓😓😓😓也诚挚地向我的队友道歉,擅长js的他向我做了妥协,导致他的工作陷入了窘迫的局面。
(8)部分单元测试代码,说明测试的函数,构造测试数据的思路
- 为了编写单元测试,需要引入了Python自带的unittest模块,这里以网络接口部分的单元测试为例。测试接口的具体步骤:
- 获得接口的 URL。
- 向接口发送请求。
- 检查响应的 HTTP 状态码、返回的数据等是否符合预期。
# filename="./tests/test_api.py
import unittest
from datetime import datetime
from django.core.cache import cache
from ..utils import Highlighter, UpdatedAtKeyBit
class PostViewSetTestCase(APITestCase):
def test_list_post_create_game(self):
def test_list_post_join_game(self):
def test_list_post_getlast_game(self,token):
'''
:param token:
:param uuid:
:return:
'''
#这里以获取上步操作为例:本以为这个接口不会有太大的问题,因为很偶然的错误没能加入了一场已经人满的对局
#在测试过程中才发现,没有考虑到人没齐的时候,返回的报文不存在last_code字段,看了错误堆栈才明白问题所在。
url = 'http://172.17.173.97:9000/api/game/' + uuid + '/last'
header = {'Authorization': token, 'Content-Type': 'application/json'}
r = requests.get(url, headers=header)
user_dict = json.loads(r.text)
response = self.client.get(url, {"token": token, "uuid":uuid})
self.assertEqual(response.status_code, status.HTTP_200_OK)
#可能返回的有状态码"200"、"403"、"404"
serializer = PostListSerializer(instance=[self.post2, self.post1], many=True)
self.assertEqual(response.data["results"], serializer.data)
result = field.to_representation(document)
self.assertEqual(user_dict_result, expected)
return user_dict_result
def test_list_put_Do_card(self):
def test_list_get_result(self):
def test_list_get_list(self):
2、Github的代码签入记录
-
这里向大家推荐一个蛮有意思的项目gitmoji,这看起来是一个很无厘头的项目,将表情包用于Github commmit 信息的签入。😙😙😙
-
然而事实上,像在GitHub上优秀的许多项目一样、Github社区不但对于commit 提交信息有严格规范,对于commit。信息中的表情包也有一样严格的要求😣😣😣😣😣😣😣太糟心了,明明deadline已经要到了,却又看到了这么萌的功能,实在是忍不住想去弄一下,花了好大的心思才让自己的gitBush可以直接看到可爱的表情包。
-
我们使用了社区比较常见的
Angular
: -
部分签入记录如下:(包括bug修复在内一共五个分支,删除了两个)
branch:main
branch:dev
-
徽章
-
.gitignore
3、遇到的代码模块异常或结对困难及解决方法。
- 这里写在最前面、向我的队友致歉、由于自己命名的的过程中使用了和方法一样的名字json。给他造成了很大的困扰。
- 具体代码展示:(原本的jsonT字段明明是json、最可笑的是、我其实想过并且遇到了这个困扰,后来不需要竟然没把名字改回去,python比起C++不报错既是也是缺点啊。要学会看报错栈🤔🤔🤔)
def Create_Game(token, isprivate): # isprivate表示该游戏是否隐私 Ture/False
jsonT = {'private': isprivate}
header = {'Authorization': token, 'Content-Type': 'application/json'}
r = requests.post('http://172.17.173.97:9000/api/game', headers=header, json=jsonT)
print("Create_Game_response:", r.text)
user_dict = json.loads(r.text)
uuid = user_dict['data']['uuid']
print("success create game,uuid:", uuid)
return user_dict
- 代码模块异常主要是前文提到的接口部分,没有考虑登录接受的所有可能的status。后来经过测试加以完善也就解决了😴😴😴。
4、队友评价
队长(Su)对队友(Weng)的评价
- 值得学习的地方🤔
对新事物的接受能力强,不像我,还是总想着接着用python做。
对我保持了极大的宽容,照顾了我的小情绪。现在看来我的决策几乎全都是错的、从用python做开始、一错再错。
- 待改进的地方:😞😞😞😞
宽容度是一把双刃剑😱😒😒为啥就不能狠狠地说别用python呢😒😒
deadline期间都蛮紧张的,不过这也正常。几乎我认识所有🤔🤔的柯老师的学生我感觉这段时间情绪大家都不是很好。也希望大家能互相理解。
队友(Weng)对队长(Su)的评价
- 队长是队里的主力,我主要是给他端茶倒水,擦汗扇风,找找bug,写写边角料的代码和文字工作。队长虽然能力强,但是作风武断,恃强凌弱,不顾及我的感受把我原本用js写的web端猪尾巴程序给否定掉了(毕竟正经人谁用pygame写这种程序啊)。但是毕竟他是巨佬,抱条大腿不容易,最后作业也绝大部分都做出来了,我也学到很多东西,只能心怀感恩了。
- 值得学习的地方
能力极强,效率极高,身体素质极强,熬夜肝代码太顶了。😱😱😱😱
- 需要改进的地方
乖乖的,这种游戏下次我们用js写吗,虽然python我也承认也强,但是ui组件太难写了。
合作期间发生几次小分歧,不过都无伤大雅,更加增进了合作的默契性,领会到脾气平静平静再平静,至少应该做到说的比做的多。
5、此次结对作业的PSP和学习进度条(周更)
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 72 |
· Estimate | · 估计这个任务需要多少时间 | 1200 | 1600 |
Development | 开发 | 700 | 700 |
· Analysis | · 需求分析 (包括学习新技术) | 5 | 55 |
· Design Spec | · 生成设计文档 | 5 | 55 |
· Design Review | · 设计复审 | 5 | 55 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 30 | 55 |
· Design | · 具体设计 | 120 | 120 |
· Coding | · 具体编码 | 360 | 720 |
· Code Review | · 代码复审 | 50 | 50 |
· Test | · 测试(自我测试,修改代码,提交修改) | 30 | 50 |
Reporting | 报告 | 90 | 180 |
· Test Repor | · 测试报告 | 30 | 60 |
· Size Measurement | · 计算工作量 | 10 | 20 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 10 | 100 |
· 合计 | 1200 | 1800 |
- Su:
第N周 | 新增代码(行) | 累计代码(行) | 周学习耗时(小时) | 累计学习耗时(小时) | 重要成长与任务进展 |
---|---|---|---|---|---|
1 | 506 | 506 | 15 | 15 | 学习了JS的初步知识、尝试修改自己的博客样式、测试了接口的通用性、学会了gitmoji、gitcommit规范 |
2 | 1006 | 1512 | 12 | 27 | 使用JS完成了基本的登陆界面、寻找了相关素材、对柯老师的感恩之情深入心府💛💙💜❤️💚 |
3 | 200 | 1712 | 19 | 46 | JS解决跨域问题纯前端方法太困难了、很痛苦地换了方案、开始学习python的pygame |
4 | 1700 | 3412 | 10 | 66 | pygame写完了最基本的人人对战,人机对战,基本的登陆逻辑、对柯老师课堂上的淳淳😉😉教导更加深有体会、后悔没有早点遇到这样的好老师 |
5 | 1000 | 4412 | 20 | 86 | 完成了通过手动输入uuid进行在线人人对战的代码、进行了部分网络测试 |
6 | 1200 | 5612 | 30 | 116 | 最终检查、AI实现、完善代码结构 |
三、心得
我的心得(Su)
1、戒骄戒躁
很多事情应该有大局观、过渡的焦虑和紧张并不能促进项目的进展。情绪波动过大,也感谢队友的极大包容和谅解。
2、规划不够明确
初期规划的时候选择了随大流的web端实现。我却因为因为一个小小的跨域问题未能解决焦虑不已,非常草率的换了python解决(python的requrest模块可以替我解决跨域问题),后来由于草率地换了python实现本项目,UI界面实现加入对局极度困难,而测试组的同学又从后,端替我们解决了跨题😖😖😖进退两难之余没能稳定自己的情绪。我不是一个很好的牵头者😞😞😞😞。
3、缺乏和队友足够的沟通。
导致在项目开始阶段做了非常错误的决定,使用python的pygame来实现项目,到后面基本上只剩我自己完成在线对战(很遗憾没能实现一个很完美的加入对局功能,需要手动在命令行输入uuid和查询uuid),算是给自己的本部优秀的项目上留下了个瑕疵。也就是我的队友能供容纳我的小脾气,甚至做了两手准备,依然实现了web端的人人对战,确保我无后顾之忧,泪目了😭😭😭😭😭
4、写给妈妈
那段时间因为紧逼的DDL,负面情绪不知道为啥往她身上撒。母亲学历有限,也许只是抖音刷到一个《考研多重要》的视频,很多时候只是想和我聊一下考研规划。又或者是听到各种百万彩礼的新闻想和我谈一下人生大事。再或者是忙碌之余想关心下千里之外的孩子又惹了什么祸。我若不是逃避、就是一肚子苦水往她身上吐槽。😔😔😔很多事情并没有我说的那么糟糕,但在她看来我像是承受了多少苦难一样。我都二十来岁了,她还是像小学回家时耐心认真地听着我分享生活点滴、唯一的区别是我这么大了、我的委屈在她看来从一笑了之变成了彻夜未眠的原因。下次和母亲倒苦水的时候还是应该说清楚,我只是写不完作业,不是毕不了业。🙄🙄🙄
5、神才是永远的柯老师。
队友心得(Weng)
1、感谢互联网
这份作业学习到了很多知识,大多数知识来源途径来自于网络,查询了很多优秀的文档以及翻看了github上游戏编者的代码,受益非强,从刚才是的配置环境都会出错的菜鸟慢慢成长了,至少有所进步。
2、对这份作业的评价
这份作业难度适中,可能在ai算法那部分比较难吧,由于本人厌恶算法,队长临危受命承担算法部分,感恩戴德。这次作业也是我第一次接触到数据接口处理,python中的request模块雀食功能强大,模块齐全,不得不说,python,及时行乐。另外也接触了很多ui框架,原生js,pygame和pyqt,不必重新造轮子。
3、提高认知
好像自己也没有那么菜(虽然好像确实挺菜的)但是一些功能,多花点时间,找找资料,熬夜写一写代码,逼一逼自己似乎也能写出不错的功能。另外这份作业也改变了我的作息,以前都没熬过那么晚的夜,增强了我熬夜写代码的能力。提前为日后的996做铺垫。
4、吹捧队长(Su)
代码写完了,肯定要夸队长啊(我实在太了解他了,他这个人就喜欢别人夸他),不夸队长下次不和我一起写代码。一个人写?这辈子都不可能一个写的,数学很差,代码能力又不强,就是靠抱队长大腿才能写得出代码这个样子的。和队长一些写代码感觉比自己一个人写好多啦,一个人写很无聊,和队长一起写灵感迸发,码力无限。另外队长人又聪明,说话又好听,我超喜欢和他一起写代码的。