一、结对探索(4分)
1.1 队伍基本信息(1分)(阿拉伯数字序号为二级标题,下同)
结对编号:21;队伍名称:拼尽全力不挂科队;
学号 | 姓名 | 博客链接 | 具体分工 |
---|---|---|---|
102102158 | 杨桥东 | http://t.csdnimg.cn/nFqoP | 代码设计、AI算法、数据计算 |
102102159 | 李璐璐 | http://t.csdnimg.cn/54Zpe | 原型设计、视频制作、页面布局优化 |
1.2 描述结对的过程(1分)
两人是同班同学,很自然就组一队了。
1.3 非摆拍的两人在讨论设计或结对编程过程的照片(2分)
二、原型设计(16分)
2.1 原型工具的选择(2分)
选择墨刀,对于小白来说比较容易上手,里面也有比较多的素材(使用体验还不错,可以满足本次任务的需求)、PS(抠图抠得人发狂)。
2.2 遇到的困难与解决办法(3分)
困难描述:
- 1、游戏界面布局:很少玩游戏,对游戏界面的美观及布局不是很能把握住。
- 2、素材查找:此时恨不得自己不是个画手,因为网上的资源很多但也很杂,而且想要找到合适且满足需求能够搭配一起的素材很少,好不容易找到还需要充钱、VIP,主要很贵。
- 3、动态组件的实现:之前没有使用过墨刀,了解了一下基础操作就开始原型设计了,动态组件需要添加不同状态和不同事件,刚开始也不是很能理解它们之间的联系,所以在刚开始一直不能实现想要的效果,只能退而求其次。
解决办法:
- 所以先去找一些关于骰子的游戏,还有微信小程序上一些小游戏去看它们界面布局、功能实现然后再开始尝试自己的。通过在网上的查找想到了将骰子和小丑结合一起,红色蓝色黄色为主色去搭配制作原型。因为找到合适的素材有限,所以有些就需要自己抠图去搭配,使用PS,技术不到位,所以有些图就不太自然,但尽力做到最好了。
- 刚开始寸步难行,后来做的多了就会有一些感悟,也知道该怎么解决页面中组件的联系,还有怎样添加动态组件,更好的实现效果,这就叫熟能生巧把。
收获:
- 会使用墨刀做些简单动态的原型设计,从而对原型设计有一定的了解,而且原型设计的相关逻辑功能,可以很好地在实现程序前理清编程思路,提高编程效率;还会使用PS进行抠图换背景。
2.3 原型作品链接(5分)(静态原型作品得2分,交互性强的原型作品得5分)
2.4 原型界面图片展示(6分)
通过.gif动态展示原型界面
- 游戏欢迎界面:
- 这是游戏欢迎界面,通过点击按钮PLAY进入游戏首页,游戏首页有排行榜、规则等图标以及三种游戏模式
- 游戏规则介绍:
- 这是游戏首页点击规则图标进入规则详情页,有三页即分别游戏规则、奖励分示例、结算规则
- 游戏模式选择:
-
以本地对战为例:点击游戏首页本地对战按钮,进入到加载页面,之后到选择局数和个人筹码页面,然后开始游戏,有托管图标,可以进行托管,点击退出直接到结算页面,从结算页面也可直接进入排行榜或者直接退出。
-
本地对战
-
在线对战
- 人机对战
- 对战积分排行榜
- 点击游戏首页排行榜图标可以进入到排行榜页面
三、编程实现(14分)
3.1 网络接口的使用(2分)
未能成功使用
3.2 代码组织与内部实现设计(类图)(2分)
3.3 说明算法的关键与关键实现部分流程图(2分)
具体流程如下:
- 初始化游戏的总轮数、当前轮数和两个玩家的筹码数量;
- 创建两个玩家对象菏泽一个玩家一个AI,并按照顺序进行每一轮游戏;
- 进入游戏循环,直到达到总局数或者有玩家的筹码数为0。
- 每一轮开始时,重置玩家状态并显示当前轮数和剩余局数。
- 轮到每个玩家投掷骰子。
- 如果是人类玩家,要求输入锁定的骰子和倍率;如果是AI玩家,随机选择锁定的骰子和倍率。
- 根据锁定的骰子和倍率计算得分,并更新玩家的筹码数和倍率。 显示本轮得分和当前总分。
- 对于非最后一个玩家,等待其回合结束后才进行下一位玩家的操作。
- 每轮结束后,显示当前各玩家的筹码数。 游戏结束后,确定获胜者并显示最终得分。
- 显示各玩家的最终得分。
算法思想主要是通过循环和条件判断来控制游戏的流程,同时利用随机选择和用户输入来模拟玩家的操作。游戏的胜负判断基于筹码数的比较。
3.4 贴出重要的/有价值的代码片段并解释(2分)
def check_score(dice):
freq = count_dice_frequency(dice)
total_score = sum(dice) # 基础得分为骰子点数的总和
bonus_score = 0 # 初始化奖励分为0
if len(freq) == 2 and any(v == 2 for v in freq.values()):
# 双对
bonus_score = 10
elif len(freq) == 3 and max(freq.values()) == 3: # 三连
bonus_score = 10
elif len(freq) == 2 and any(v == 3 for v in freq.values()):
# 葫芦
bonus_score = 20
elif len(freq) == 1 and dice.count(dice[0]) == 4: # 四连
bonus_score = 40
elif len(freq) == 1 and dice.count(dice[0]) == 5: # 五连
bonus_score = 100
elif sorted(dice) in [[1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6]]:
# 小顺子
bonus_score = 30
elif sorted(dice) in [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6]]:
# 大顺子
bonus_score = 60
- 这段代码实现了骰子得分的计算函数check_score。给定一组骰子点数,函数首先统计每个点数的频率,然后根据特定的规则判断得分。
import random
from game_rules import check_score
def roll_dice(num_dice):
dice = []
for _ in range(num_dice):
dice.append(random.randint(1, 6))
return dice
class Player:
def __init__(self, name, chips=100):
self.name = name # 玩家姓名
self.chips = chips # 筹码数量
self.dice = [] # 投掷的骰子
self.locked_dice = [] # 锁定的骰子
self.multiplier = 1 # 倍率
self.total_score = 0 # 总得分
def reset(self):
self.dice.clear()
self.locked_dice.clear()
self.multiplier = 1
def roll_dice(self, num_dice):
self.dice = random.choices(range(1, 7), k=num_dice)
def choose_locked_dice(self, roll, dice_indexes=[]):
if not dice_indexes:
self.locked_dice.clear() # 清除上轮锁定的骰子
self.locked_dice.extend([self.dice[i - 1] for i in dice_indexes])
def choose_multiplier(self, roll, multiplier):
self.multiplier = multiplier
def calculate_total_score(self):
self.total_score += self.chips
class HumanPlayer(Player):
def __init__(self, name, chips):
super().__init__(name, chips)
def choose_multiplier(self, multiplier):
print(f"选择倍率:{multiplier}")
self.multiplier = multiplier
score = check_score(self.dice + self.locked_dice)
print(f"本轮得分:{score * self.multiplier}")
def choose_locked_dice(self, indexes):
print(f"选择锁定的骰子索引:{indexes}")
self.locked_dice = [self.dice[index - 1] for index in indexes]
num_unlocked_dice = 5 - len(self.locked_dice)
self.dice = roll_dice(num_unlocked_dice)
print(f"重新投掷骰子结果:{self.dice}")
class AIPlayer(Player):
def play(self, dice_results):
print("AI投掷骰子...")
print("AI骰子结果:{}".format(dice_results))
scores = [] # 存储每种选择的得分
# 遍历所有可能的选择
for locked_indices in range(len(dice_results) + 1):
# 投掷未锁定的骰子
new_dice_results = [random.randint(1, 6)
if i not in locked_indices else dice_results[i]
for i in range(len(dice_results))]
# 计算当前选择的得分
score = check_score(new_dice_results)
scores.append(score)
# 选择得分最高的选择
best_choice = max(range(len(scores)), key=lambda x: scores[x])
print("锁定骰子数:{},锁定骰子结果:{},倍率:{}".format(best_choice,
[dice_results[i] for i in range(best_choice)], self.multiplier))
# 根据选择更新状态
if best_choice == 0:
self.multiplier = random.choice([1, 2, 3, 4, 5, 6])
self.locked_dice.clear()
else:
self.multiplier = 1
self.locked_dice = [dice_results[i]
for i in range(best_choice)]
print("重新掷骰子结果:{}".format([new_dice_results[i]
for i in range(len(new_dice_results))
if i not in self.locked_dice]))
self.update_score(new_dice_results) # 更新玩家分数
print("AI本轮得分:{},当前总分:{}".format(scores[best_choice],
self.score))
print()
def choose_multiplier(self, multiplier):
if multiplier == 0:
# 随机选择一个非零倍率
multiplier = random.choice([1, 2, 3, 4, 5, 6])
self.multiplier = multiplier
- 这段代码实现了一个骰子游戏的玩家类和AI玩家类。其中,玩家类包括了玩家的姓名、筹码数量、投掷的骰子、锁定的骰子、倍率和总得分等属性和方法。AI玩家类继承了玩家类,并且实现了自己的play方法和choose_multiplier方法。
- 在AI玩家的play方法中,首先打印出AI投掷骰子的信息。然后,遍历所有可能的选择,包括不锁定任何骰子到锁定所有骰子。对于每种选择,都重新生成未锁定的骰子结果,并计算得分。将得分存储在列表中。
- 之后,通过比较得分列表,选择得分最高的选择作为最佳选择。根据最佳选择的不同情况,更新AI玩家的状态:如果最佳选择是不锁定任何骰子,则随机选择一个非零倍率,并清空锁定的骰子;否则,倍率设为1,并将对应索引的骰子加入锁定的骰子列表中。
- 最后,打印出重新掷骰子的结果、更新后的得分以及当前总分。
在AI玩家的choose_multiplier方法中,如果传入的倍率参数为0,则随机选择一个非零倍率作为倍率值。
3.5 性能分析与改进(2分)
3.6 单元测试(2分)
(展示出项目部分单元测试代码,并说明测试的函数,构造测试数据的思路)
import random
from game_rules import check_score
from player import HumanPlayer, AIPlayer
MIN_CHIPS = 0
MAX_ROLLS = 3
TOTAL_SCORE = 100
MULTIPLIER_RANGE = range(1, 4)
INVALID_INPUT_MSG = "输入无效,请重新输入!"
def start_ai_game():
while True:
try:
total_rounds = int(input("请输入游戏局数:"))
if total_rounds <= 0:
print(INVALID_INPUT_MSG)
continue
break
except ValueError:
print(INVALID_INPUT_MSG)
player_name = input("请输入玩家姓名:")
players = [
HumanPlayer(player_name, TOTAL_SCORE),
AIPlayer("AI")
]
current_round = 0
while current_round < total_rounds and all(player.chips > MIN_CHIPS for player in players):
current_round += 1
remaining_rounds = total_rounds - current_round + 1
print(f"\n第{current_round}轮游戏开始!")
print(f"当前轮数:{current_round},总局数:{total_rounds},剩余局数:{remaining_rounds}")
for i, player in enumerate(players):
player.reset() # 开始新一轮游戏,重置玩家状态
print(f"\n{player.name}投掷骰子...")
player.roll_dice(5)
print(f"{player.name}骰子结果:{player.dice}")
for roll in range(MAX_ROLLS):
if isinstance(player, HumanPlayer):
while True:
decision_input = input("请选择要锁定的骰子(输入数字,用空格隔开;如不锁定任何骰子,请直接按Enter键):")
locked_dice = [int(dice) for dice in decision_input.split()] if decision_input != "" else []
if all(1 <= dice <= 5 for dice in locked_dice):
break
else:
print(INVALID_INPUT_MSG)
while True:
multiplier_input = input("请选择倍率(输入数字1-3,不输入则默认为1):")
multiplier = int(multiplier_input) if multiplier_input != "" else 1
if multiplier in MULTIPLIER_RANGE:
break
else:
print(INVALID_INPUT_MSG)
else: # AI玩家随机选择
locked_dice = random.sample(range(5), random.randint(0, 4))
multiplier = random.choice(MULTIPLIER_RANGE)
player.choose_locked_dice(locked_dice)
player.choose_multiplier(multiplier)
print(
f"锁定骰子数:{len(player.locked_dice)},锁定骰子结果:{player.locked_dice},倍率:{player.multiplier}")
if len(locked_dice) == 5: # 如果锁定了所有骰子,则不需要进行重新掷骰子
break
player.roll_dice(5 - len(locked_dice)) # 继续掷剩余的骰子
print(f"重新掷骰子结果:{player.dice}")
score = check_score(player.dice + player.locked_dice)
player.chips += score * player.multiplier
player.multiplier += 1 # 倍率递增
print(f"{player.name}本轮得分:{score},当前总分:{player.chips}")
if i < len(players) - 1: # 对于非最后一个玩家,等待其回合结束后才进行下一位玩家的操作
input(f"\n请按Enter键继续下一个玩家的操作...")
print("\n当前各玩家筹码数:")
for player in players:
print(f"{player.name}: {player.chips}")
print("\n游戏结束!")
max_chips = max(player.chips for player in players)
winners = [player for player in players if player.chips == max_chips]
if len(winners) == 1:
print(f"获胜者:{winners[0].name},最终得分:{winners[0].chips}")
else:
print("本轮游戏平局!")
print("获胜者为:")
for winner in winners:
print(f"{winner.name},最终得分:{winner.chips}")
print("\n各玩家最终得分:")
for player in players:
print(f"{player.name}: {player.chips}")
if __name__ == '__main__':
start_ai_game()
该代码是一个掷骰子游戏的AI版本。游戏开始时,根据玩家输入的局数和姓名创建玩家列表。每一轮中,轮到每个玩家投掷骰子。玩家可以选择是否锁定骰子和选择倍率。根据锁定的骰子和倍率计算得分,并更新玩家的筹码数。最终根据筹码数确定获胜者。
3.7 贴出GitHub的代码签入记录,合理记录commit信息(2分)
四、总结反思(11分)
4.1 本次任务的PSP表格(2分)
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 120 | 240 |
· Estimate | · 估计这个任务需要多少时间 | 120 | 240 |
Development | 开发 | 3420 | 3540 |
· Analysis | · 需求分析 (包括学习新技术) | 1200 | 1200 |
· Design Spec | · 生成设计文档 | 120 | 120 |
· Design Review | · 设计复审 | 120 | 120 |
· Coding Standard | · 代码规范 (为目前的开发制定合适的规范) | 60 | 60 |
· Design | · 具体设计 | 120 | 240 |
· Coding | · 具体编码 | 1200 | 1200 |
· Code Review | · 代码复审 | 400 | 400 |
· Test | · 测试(自我测试,修改代码,提交修改) | 200 | 200 |
Reporting | 报告 | 360 | 360 |
· Test Report | · 测试报告 | 120 | 120 |
· Size Measurement | · 计算工作量 | 120 | 120 |
· Postmortem & Process Improvement Plan | · 事后总结, 并提出过程改进计划 | 120 | 120 |
· 合计 | 3900 | 4140 |
4.2 学习进度条(每周追加)(2分)
- 杨桥东
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 50 | 50 | 8 | 8 | 学习了算法思想 |
2 | 90 | 140 | 12 | 20 | 本地对战功能实现 |
3 | 100 | 240 | 10 | 30 | Python编程语言的理解和使用能力 |
4 | 100 | 340 | 16 | 46 | AI基础逻辑的编写 |
- 李璐璐
第N周 | 新增代码(行) | 累计代码(行) | 本周学习耗时(小时) | 累计学习耗时(小时) | 重要成长 |
---|---|---|---|---|---|
1 | 0 | 0 | 5 | 5 | 理解孤独一掷玩法 |
2 | 0 | 0 | 10 | 15 | 对《孤独一掷》界面设计、寻找素材能力 |
3 | 0 | 0 | 20 | 40 | 原型设计能力 |
4 | 100 | 100 | 10 | 50 | 视频制作 |
4.3 最初想象中的产品形态、原型设计作品、软件开发成果三者的差距如何?(2分)
差距还是挺大的,只有原型设计作品,对于实现我们没能完成,算法部分还行。
1.技术限制:在初始的产品形态或想象中,可能没有考虑到实际技术的限制。在软件开发中,某些功能可能需要复杂的算法或大量的数据处理,而原型设计和初期的想象中可能没有充分考虑到这些技术方面的挑战。所以在本次作业中,我们两个的能力并不能让真正实现产品软件,只有原型以及代码。
2.时间和资源限制:在实际的软件开发过程中,有限的时间和资源可能限制了产品的最终实现。在想象中,我们往往只关注产品的核心特点,而在实际开发中,需要考虑到时间、预算和人力等方面的限制。比如我们在有限时间内学习新技术的能力以及心态,在面对超出自己能力范围的是否能够以积极态度去学习。
3.设计和交互细节:想象中的产品形态通常只是一个整体概念,而在原型设计和软件开发中,需要详细考虑各个界面、交互细节和用户体验。这需要更深入的思考和设计工作。
(也就是谈一谈本次任务中“理想与现实的差距”,是哪些因素造成了这些差距?)
4.4 评价你的队友(2分)
杨桥东:
-
值得学习:
在此次结对编程中,她在原型设计方面展现出了还不错的专业水准和创造力。她注意到了每个细节,包括布局、颜色、交互等方面的优化,使得原型设计看起来非常精美和专业。她能够将用户体验与设计美学相结合,从她的工作中,我学到了很多关于原型设计的技巧和方法。她的注重细节的态度和对用户体验的关注让我意识到设计不仅仅是外观,还包括了功能和用户的感受。她的工作也激发了我对创新和设计思维的兴趣。 -
对于改进方面:
她可能需要更加主动参与到项目的其他部分中,例如编码和测试等。总体而言,她在原型设计方面的能力给整个团队带来了巨大的贡献。她的工作展现了专业水准和创意,为项目开发提供了很好的基础。
李璐璐:
-
值得学习:
对作业的认真度,这次作业从我的角度来说真的很难(难度等级五个星,在原来的学校从未有如此难度的作业,连个人编程的难度都没有,所以我的态度从一开始一定要做出来变成尽力而为吧,被震惊到无从下手),但他不一样,他负责算法设计,对代码精益求精,经过不懈努力临近截止时间还是有点点错误,但是他还是一直改动,换成我可能就这样了。学习和执行能力也比较好,主动性很高,对于这次作业很认真,会经常询问我的进度。 -
对于改进方面:
总体而言,可能因为我是女生他不太好意思把话说得太重,沟通方面比较少,沟通这方面需要加强一下。
4.5 结对编程作业心得体会(3分)
(可包含但不限于评价作业难度、完成后的感受、遇到的代码模块异常或结对困难及解决方法、对之后学习或软件开发的启发)
(本部分需要包含队伍内所有成员的心得体会,若缺少一人,则队伍总分减少3分,减满6分为止)
杨桥东:
-
对于我来说,这次结对编程作业的难度确实比较大。我们需要实现一个投掷骰子的游戏,涉及到了很多细节和条件判断。在编写代码的过程中,我们需要考虑各种情况下玩家输赢的逻辑,并进行充分的测试和调试。这对于我的编程能力和逻辑思考能力是一个挑战。但通过和队友的合作和讨论,我们最终完成了作业,这个过程让我受益匪浅。
-
在结对编程的过程中,团队协作非常重要。我们需要互相沟通和协调任务分配、代码风格、注释规范等方面的事项。良好的合作可以提高工作效率,促进彼此的学习和成长。通过这次作业,我深刻体会到了团队协作的重要性,并学会了更好地与队友进行合作。
-
这次作业也给我带来了一些关于软件开发方面的启发。首先,细节问题非常重要。在编写代码时,我们需要仔细处理各种情况,确保程序的正确性和稳定性。其次,良好的代码组织和风格可以提高代码的可读性和可维护性。此外,测试和调试也是不可忽视的一部分,通过充分的测试和调试可以及时发现和解决问题。作为一个程序员,我应该不断学习新知识,适应新技术,并不断改进自己的代码,这样才能在软件开发领域不断提升自己。
李璐璐:
- 当看到这次结对编程作业,第一反应就是要完蛋,这么难怎么可能做得出来,就一直很恐慌这个作业,一度甚至想着干脆就这样吧,不写了,为什么会这么难等等想法。当然不行,因为这是结对编程,我总不能一点都不做。所以就和队友沟通进行分工,我就选择原型设计,很大一部分原因,我编程能力真的不行。
- 选择比较容易上手的工具墨刀进行设计,看到最后的成果感觉还是很不错的,没想到自己也能有如此技能。但过程真的很难,想象和上手不是一回事,素材虽然多,但是符合的很少(免费素材),大体出来后,就要开始抠细节,怎样能使动态组件更流畅,整体观感更好,感觉每打开一次都会有新的想法,然后想要做得更好。审美也很重要,自我感觉还是欠缺点灵性,做得比较死板。但是学到了如何做原型设计,点亮一个技能点,升级还需同志继续努力啊。
- 身为队员我不是很称职,没有做到自己的职责所在,我们两个沟通不是很多,整体和谐,但是感觉我的主动性要低一些在这次结对任务中,对于代码部分我没有去很好的完成。在今后的组队任务中要更积极一些,完成自己的部分,去交流,一定要记住这是团队,要有合作精神,不能摆烂!对于困难要迎难而上,万不可连头都不敢开,连半道崩卒都算不上。今后还是要学习如何进行软件开发,学习使用uniapp搭建前端框架。