1. 为什么选择用Python写猜拳小游戏
1.1 从“最小可运行项目”说起
很多人学Python学到一半就卡住了,语法书翻了两三遍,视频课也刷了不少,但一合上电脑,脑子里空空如也,不知道能拿这门语言做点什么。这种状态我太熟悉了,因为我自己也经历过。后来我发现,问题不在于学得不够多,而在于缺少一个“从零到一”的完整项目体验。猜拳小游戏就是这样一个绝佳的切入点——它足够小,小到一两个小时就能跑通;又足够完整,完整到涵盖了输入输出、条件判断、循环控制、随机数、异常处理这些编程核心概念。
你可能会想,猜拳有什么好写的,不就是石头剪刀布吗?但恰恰是这种看似简单的东西,最能检验一个人对编程逻辑的理解是否扎实。我见过太多人,能背出Python的所有语法,却写不出一个健壮的猜拳程序。为什么?因为真实场景下的猜拳游戏要考虑的东西远比想象中多:用户输入了乱七八糟的东西怎么办?怎么让游戏可以反复玩而不是玩一局就退出?怎么统计胜率?怎么让电脑的出拳不那么容易被猜到?这些问题,才是编程思维真正落地的地方。
1.2 这个项目适合谁,能解决什么问题
这个项目适合所有已经掌握了Python基础语法、但还没有完整项目经验的人。不管你是刚学完大学计算机导论课的学生,还是转行自学编程的职场人,或者是想给孩子找个编程入门练习的家长,猜拳小游戏都是一个非常好的练手项目。它不需要你安装任何第三方库,不需要配置复杂的环境,打开Python自带的IDLE或者任何编辑器就能开始写。
从解决问题的角度来说,这个项目能帮你打通几个关键节点:第一,理解程序如何与用户交互,也就是输入输出的完整流程;第二,掌握条件分支和循环的组合使用,这是所有程序逻辑的基础;第三,学会用随机数模块模拟不确定性,这在游戏开发和数据分析中都很常见;第四,建立基本的异常处理意识,让你的程序不会因为用户乱输入就崩溃。这些能力,在你后续写更复杂的项目时,会反复用到。
1.3 我为什么反复推荐这个练手项目
说实话,我教过不少人学Python,也看过很多人的学习路径。那些进步最快的,往往不是看了最多教程的人,而是动手写了最多小项目的人。猜拳游戏的好处在于它的反馈极其即时——你写完几行代码,马上就能运行,马上就能看到结果。这种即时反馈对初学者来说太重要了,它能让你保持兴趣,让你觉得“我真的在写程序,而不是在背语法”。
另外,猜拳游戏的扩展性非常好。你可以在基础版本上不断增加功能:加个计分板、加个难度选择、加个图形界面、甚至加个简单的AI让电脑学会你的出拳习惯。每一次扩展,都是一次新的学习机会。我自己的习惯是,每学到一个新知识点,就想想能不能把它用到猜拳游戏里。比如学了字典,就用字典来存储出拳规则;学了函数,就把游戏逻辑拆成函数;学了类,就用面向对象的方式重写一遍。这种“以项目驱动学习”的方式,效率比单纯看书高得多。
2. 核心逻辑拆解与设计思路
2.1 猜拳游戏的规则抽象
在动手写代码之前,我们得先把现实中的猜拳规则抽象成计算机能理解的逻辑。现实中的猜拳很简单:石头赢剪刀,剪刀赢布,布赢石头,相同则平局。但计算机不会“理解”这些,它只能做比较和判断。所以我们需要把“赢”这个关系用数字或字符串表示出来。
最直观的方式是用数字编码:石头用0表示,剪刀用1表示,布用2表示。这样有一个很巧妙的地方——判断胜负可以用数学运算来实现。你想想,如果玩家出0(石头),电脑出1(剪刀),玩家赢;玩家出1(剪刀),电脑出2(布),玩家赢;玩家出2(布),电脑出0(石头),玩家赢。发现规律了吗?当玩家出的数字比电脑出的数字小1的时候,玩家赢。但这里有个边界情况:玩家出0,电脑出2的时候,0比2小,但实际上是电脑赢。所以我们需要用取模运算来处理这个循环关系。
具体来说,判断条件可以写成:(玩家出拳 - 电脑出拳) % 3。如果结果是1,说明玩家赢;结果是2,说明电脑赢;结果是0,说明平局。这个公式非常优雅,三行代码就能搞定所有胜负判断。我第一次发现这个规律的时候,觉得特别有意思,因为它把现实规则转化成了数学关系,这正是编程思维的核心——用计算的方式描述世界。
2.2 随机数模块的选择与使用
Python标准库里的random模块是我们实现电脑随机出拳的关键。random模块提供了多种随机数生成函数,最常用的是random.randint(a, b),它可以生成一个包含a和b的随机整数。比如random.randint(0, 2)就会随机返回0、1、2中的一个,正好对应我们的三种出拳。
这里有个细节值得注意:random模块生成的其实是“伪随机数”,也就是说,它基于一个种子值通过确定性算法计算出来的。默认情况下,种子值来自系统时间,所以每次运行程序的结果都不一样。但如果你设置了固定的种子,比如random.seed(42),那么每次运行程序,电脑的出拳顺序都会完全一样。这个特性在调试的时候特别有用——你可以复现每一次运行的结果,方便定位问题。
另外,如果你想让电脑的出拳不那么“随机”,比如让它更倾向于出某个拳,可以用random.choices()函数,它允许你指定每个元素的权重。比如random.choices([0, 1, 2], weights=[0.5, 0.3, 0.2])会让电脑有50%的概率出石头,30%出剪刀,20%出布。这种加权随机在游戏设计中很常见,可以让电脑的行为看起来更“像人”。
2.3 用户输入的处理策略
用户输入是猜拳游戏里最容易出问题的环节。你永远想不到用户会输入什么——可能是中文“石头”,可能是英文“rock”,可能是数字“0”,可能是大小写混搭的“Rock”,甚至可能是一串乱码。如果程序不做任何处理,用户随便输入一个东西,程序就可能报错崩溃。
我的处理策略是:先接收原始输入,然后做标准化处理。具体来说,用input()函数获取用户输入后,先用strip()去掉首尾空格,再用lower()统一转成小写。然后建立一个映射字典,把各种可能的输入都映射到对应的数字编码上。比如{"石头": 0, "0": 0, "rock": 0, "r": 0},这样不管用户怎么输入,只要在字典里,就能正确识别。
如果用户的输入不在字典里,就提示用户重新输入,并且给出正确的输入示例。这里可以用一个while循环,直到用户输入合法为止。这种“防御性编程”的思路非常重要,它能让你的程序在面对意外输入时依然保持稳定。我见过很多新手写的程序,功能逻辑都对,但用户一乱输入就崩溃,这就是缺少输入验证导致的。
2.4 游戏循环与退出机制的设计
一个完整的猜拳游戏不应该只玩一局就结束,而应该让用户可以选择继续玩或者退出。这就需要一个主循环来控制游戏的流程。常见的做法是用一个while True循环包裹整个游戏逻辑,然后在循环内部询问用户是否继续。如果用户选择退出,就用break语句跳出循环。
但这里有个细节:如果用户输入了非预期的选项,比如问“是否继续”的时候输入了“maybe”,程序应该怎么处理?我的做法是,把“是否继续”也做成一个输入验证的环节,只接受“y”、“yes”、“n”、“no”这几种输入,其他输入一律提示重新输入。这样虽然多写了几行代码,但用户体验会好很多。
另外,我还喜欢在退出的时候加一个统计功能,显示用户玩了多少局、赢了多少局、输了多少局、平了多少局,以及胜率是多少。这个功能虽然简单,但能让用户有成就感,也能让程序看起来更完整。统计数据的存储可以用几个变量来记录,每次游戏结束后更新,退出时统一显示。
3. 完整代码实现与逐段解析
3.1 基础版本:三十行代码跑通核心逻辑
先来看一个最精简的版本,这个版本去掉了所有花哨的功能,只保留最核心的游戏逻辑。我建议你先把这个版本跑通,理解每一行代码的作用,然后再逐步添加功能。
import random def get_computer_choice(): return random.randint(0, 2) def get_user_choice(): mapping = { "0": 0, "石头": 0, "rock": 0, "r": 0, "1": 1, "剪刀": 1, "scissors": 1, "s": 1, "2": 2, "布": 2, "paper": 2, "p": 2 } while True: user_input = input("请出拳(0-石头,1-剪刀,2-布):").strip().lower() if user_input in mapping: return mapping[user_input] print("输入无效,请重新输入。") def judge(user, computer): result = (user - computer) % 3 if result == 0: return "平局" elif result == 1: return "你赢了" else: return "你输了" def main(): print("=== 猜拳小游戏 ===") while True: user = get_user_choice() computer = get_computer_choice() names = ["石头", "剪刀", "布"] print(f"你出了{names[user]},电脑出了{names[computer]}") print(judge(user, computer)) if input("是否继续?(y/n):").strip().lower() != "y": break print("游戏结束,感谢游玩!") if __name__ == "__main__": main()这段代码虽然短,但五脏俱全。get_computer_choice函数负责生成电脑的出拳,get_user_choice函数负责获取并验证用户输入,judge函数负责判断胜负,main函数负责串联整个流程。每个函数只做一件事,职责清晰,方便后续修改和扩展。
3.2 输入验证的强化处理
上面的版本已经做了基本的输入验证,但还不够完善。比如用户输入了“石头 ”(后面带空格),strip()能处理;但如果用户输入了“石 头”(中间有空格),就识别不了了。再比如用户输入了“Rock”,lower()能处理;但如果用户输入了“ROCK”,也能处理。但如果用户输入了“石头!”(带标点符号),就没办法了。
我的做法是,在标准化处理的时候,把常见的标点符号也去掉。可以用str.translate()方法,或者简单地用replace()替换掉常见标点。另外,我还会在提示信息里把可接受的输入都列出来,让用户知道该怎么输入。比如提示语可以写成:“请输入石头/剪刀/布,或者对应的数字0/1/2,或者英文rock/scissors/paper”。
还有一个细节:如果用户直接按回车,输入的是空字符串,也应该被判定为无效输入。这个用strip()之后判断是否为空就能处理。我见过一些程序,用户按回车之后程序就崩溃了,就是因为没有处理空输入的情况。
3.3 计分板与胜率统计的实现
加计分板其实很简单,只需要在main函数里定义几个变量来记录局数、胜局、负局、平局,然后在每局结束后更新这些变量,退出时统一显示。
def main(): print("=== 猜拳小游戏 ===") total = 0 wins = 0 losses = 0 draws = 0 names = ["石头", "剪刀", "布"] while True: user = get_user_choice() computer = get_computer_choice() print(f"你出了{names[user]},电脑出了{names[computer]}") result = judge(user, computer) print(result) total += 1 if result == "你赢了": wins += 1 elif result == "你输了": losses += 1 else: draws += 1 if input("是否继续?(y/n):").strip().lower() != "y": break print(f"\n共玩了{total}局") print(f"赢:{wins}局,输:{losses}局,平:{draws}局") if total > 0: print(f"胜率:{wins / total * 100:.1f}%") print("游戏结束,感谢游玩!")胜率的计算要注意除以零的问题。如果用户一局都没玩就退出,total是0,直接做除法会报错。所以要先判断total是否大于0。这种边界情况的处理,是区分新手和有经验程序员的重要标志。
3.4 用字典优化胜负判断逻辑
前面我们用取模运算来判断胜负,代码很简洁。但如果你觉得取模运算不够直观,也可以用字典来存储胜负关系。比如:
win_rules = { 0: 1, # 石头赢剪刀 1: 2, # 剪刀赢布 2: 0 # 布赢石头 } def judge(user, computer): if user == computer: return "平局" elif win_rules[user] == computer: return "你赢了" else: return "你输了"这种写法的好处是可读性更强,一眼就能看出谁赢谁。缺点是代码稍微长一点,而且如果以后要加新的出拳类型(比如“蜥蜴”和“斯波克”),需要修改字典。取模运算的版本则更通用,只要出拳类型是3的倍数,公式都成立。两种写法各有优劣,你可以根据自己的喜好选择。
3.5 让电脑学会“读心术”:简单的AI策略
如果你想让游戏更有挑战性,可以给电脑加一个简单的AI。这个AI会记录用户最近几次的出拳,然后预测用户下一次可能出什么,并选择能赢那个拳的出法。比如用户最近三次都出了石头,那AI就预测用户下次还会出石头,于是AI出布。
from collections import Counter class SimpleAI: def __init__(self): self.history = [] def predict(self): if len(self.history) < 3: return random.randint(0, 2) recent = self.history[-3:] most_common = Counter(recent).most_common(1)[0][0] # 预测用户会出most_common,选择能赢它的拳 counter = {0: 2, 1: 0, 2: 1} return counter[most_common] def record(self, choice): self.history.append(choice)这个AI虽然简单,但已经能让电脑看起来“聪明”不少。当然,如果用户发现了这个规律,也可以故意改变出拳习惯来反制。这种博弈的过程,正是猜拳游戏的乐趣所在。
4. 常见问题与排查技巧实录
4.1 输入验证类问题速查
| 问题现象 | 可能原因 | 解决方法 |
|---|---|---|
| 用户输入中文时程序报错 | 没有做输入标准化处理 | 用strip().lower()处理,并建立映射字典 |
| 用户按回车后程序崩溃 | 没有处理空输入 | 判断strip()后是否为空字符串 |
| 用户输入数字0-2以外的数字 | 没有做范围检查 | 在映射字典中只保留合法输入 |
| 用户输入带标点符号 | 没有去除标点 | 用replace()或translate()去除常见标点 |
| 用户输入大小写混搭 | 没有统一大小写 | 用lower()统一转小写 |
4.2 逻辑判断类问题排查
最常见的问题是胜负判断写反了。比如把“石头赢剪刀”写成了“剪刀赢石头”。这种错误往往很难发现,因为程序不会报错,只是结果不对。我的建议是,写完之后手动测试所有九种组合(三种用户出拳乘以三种电脑出拳),确保每一种组合的结果都正确。
另一个常见问题是取模运算的边界情况。比如(0 - 2) % 3的结果是1,表示玩家赢,这是正确的,因为石头赢布。但如果你不小心写成了(computer - user) % 3,结果就完全反了。所以写取模运算的时候,一定要想清楚谁减谁。
4.3 循环控制类问题排查
如果程序陷入了死循环,最常见的原因是while循环的退出条件没有正确设置。比如在输入验证的循环里,如果用户一直输入无效内容,程序就会一直循环。这本身不是bug,但如果没有给用户一个退出的方式,体验就很差。我的做法是,在输入验证的循环里加一个计数器,如果用户连续输入错误超过5次,就自动退出或者给出更明确的提示。
另一个问题是break语句的位置。如果break写在了错误的位置,可能会导致循环提前退出或者无法退出。比如在嵌套循环里,break只能跳出最内层的循环。如果需要跳出多层循环,可以用标志变量或者函数返回来实现。
4.4 代码组织类问题排查
新手常犯的一个错误是把所有代码都写在main函数里,导致main函数越来越长,越来越难维护。我的建议是,每个独立的功能都封装成函数。比如获取用户输入是一个函数,生成电脑出拳是一个函数,判断胜负是一个函数,显示统计结果是一个函数。这样不仅代码更清晰,而且方便单独测试每个功能。
另外,全局变量的使用也要谨慎。比如计分板的变量,如果定义在全局,任何函数都能修改,容易出问题。更好的做法是把它们定义在main函数内部,通过参数传递给需要的函数,或者封装成一个类。虽然这样写起来稍微麻烦一点,但能避免很多潜在的bug。
4.5 我踩过的那些坑
第一个坑是中文编码问题。早期Python版本对中文支持不好,如果源代码里有中文字符串,可能会报编码错误。现在Python 3默认使用UTF-8编码,这个问题基本不存在了。但如果你在Windows上运行,控制台的编码可能是GBK,输出中文可能会乱码。解决办法是在代码开头加上# -*- coding: utf-8 -*-,或者在输出中文之前先设置控制台编码。
第二个坑是random模块的种子问题。有一次我调试程序,发现每次运行电脑的出拳顺序都一样,查了半天才发现是之前设置了random.seed()没有取消。这个坑让我明白了一个道理:调试用的代码一定要记得删掉,不然可能会影响正式运行的结果。
第三个坑是用户输入的数字和字符串混淆。比如用户输入了“0”,我直接用int()转换,结果用户输入了“石头”,int()就报错了。后来我改用映射字典,不管用户输入什么,只要在字典里就能正确转换,不在字典里就提示重新输入。这个方案虽然多写了几行代码,但健壮性好了很多。
5. 从命令行到图形界面:进阶扩展方向
5.1 用tkinter做一个可视化猜拳游戏
命令行版本的猜拳游戏虽然逻辑完整,但界面比较简陋。如果你想让它看起来更像一个“真正的游戏”,可以用Python自带的tkinter库做一个图形界面。tkinter是Python的标准GUI库,不需要额外安装,直接import就能用。
基本思路是:创建一个主窗口,在窗口里放置三个按钮,分别对应石头、剪刀、布。用户点击按钮后,程序生成电脑的出拳,然后在一个标签里显示结果。还可以加一个计分板标签,实时显示胜负统计。整个界面的布局可以用grid()或pack()来管理。
import tkinter as tk import random class GameGUI: def __init__(self): self.window = tk.Tk() self.window.title("猜拳小游戏") self.wins = 0 self.losses = 0 self.draws = 0 self.result_label = tk.Label(self.window, text="请出拳", font=("Arial", 16)) self.result_label.pack(pady=20) self.score_label = tk.Label(self.window, text="赢:0 输:0 平:0", font=("Arial", 12)) self.score_label.pack(pady=10) button_frame = tk.Frame(self.window) button_frame.pack(pady=20) tk.Button(button_frame, text="石头", width=8, command=lambda: self.play(0)).pack(side=tk.LEFT, padx=5) tk.Button(button_frame, text="剪刀", width=8, command=lambda: self.play(1)).pack(side=tk.LEFT, padx=5) tk.Button(button_frame, text="布", width=8, command=lambda: self.play(2)).pack(side=tk.LEFT, padx=5) self.window.mainloop() def play(self, user_choice): computer_choice = random.randint(0, 2) names = ["石头", "剪刀", "布"] result = (user_choice - computer_choice) % 3 if result == 0: self.draws += 1 outcome = "平局" elif result == 1: self.wins += 1 outcome = "你赢了" else: self.losses += 1 outcome = "你输了" self.result_label.config(text=f"你出{names[user_choice]},电脑出{names[computer_choice]},{outcome}") self.score_label.config(text=f"赢:{self.wins} 输:{self.losses} 平:{self.draws}") if __name__ == "__main__": GameGUI()这个图形界面版本虽然简单,但已经具备了完整的游戏功能。你可以在此基础上继续美化界面,比如添加图片、音效、动画效果等。
5.2 用类来重构代码
如果你想让代码更符合面向对象的编程思想,可以把游戏逻辑封装成一个类。这样做的好处是,游戏的状态(比如计分板)和操作(比如出拳、判断胜负)都绑定在一起,代码更内聚,也更容易扩展。
class RockPaperScissors: def __init__(self): self.total = 0 self.wins = 0 self.losses = 0 self.draws = 0 self.names = ["石头", "剪刀", "布"] def get_computer_choice(self): return random.randint(0, 2) def judge(self, user, computer): result = (user - computer) % 3 if result == 0: self.draws += 1 return "平局" elif result == 1: self.wins += 1 return "你赢了" else: self.losses += 1 return "你输了" def play_round(self, user_choice): computer_choice = self.get_computer_choice() result = self.judge(user_choice, computer_choice) self.total += 1 return f"你出{self.names[user_choice]},电脑出{self.names[computer_choice]},{result}" def get_stats(self): if self.total == 0: return "还没有玩过" return f"共{self.total}局,赢{self.wins}局,输{self.losses}局,平{self.draws}局,胜率{self.wins/self.total*100:.1f}%"用类重构之后,你会发现代码的可读性和可维护性都提升了一个档次。而且如果你以后想加一个“双人对战”模式,只需要在类里加一个方法,让两个玩家分别输入出拳,然后调用judge方法判断胜负即可。
5.3 添加难度选择和游戏模式
基础版本的猜拳游戏只有一个难度,就是电脑完全随机出拳。你可以添加难度选择功能,让玩家可以选择“简单”、“普通”、“困难”三个难度。简单模式下,电脑完全随机;普通模式下,电脑有30%的概率预测玩家的出拳;困难模式下,电脑有60%的概率预测玩家的出拳。
实现思路是,在游戏开始前让玩家选择难度,然后根据难度设置一个预测概率。每次电脑出拳时,先生成一个0到1之间的随机数,如果小于预测概率,就使用AI预测的出拳,否则随机出拳。这样既增加了游戏的挑战性,又不会让玩家觉得电脑在作弊。
另外,你还可以添加“三局两胜”或“五局三胜”的游戏模式。这种模式下,游戏不再是无限循环,而是先赢够指定局数的玩家获胜。实现起来也很简单,只需要在循环里加一个判断,当某一方的胜局达到指定数量时,跳出循环并宣布获胜者。
5.4 用文件存储游戏记录
如果你想让游戏记录可以跨会话保存,可以把每局的结果写入文件。比如用CSV格式存储,每行记录一局的时间、玩家出拳、电脑出拳、结果。这样你可以在游戏结束后分析自己的出拳习惯,看看自己是不是总是出石头。
import csv from datetime import datetime def save_record(user, computer, result): with open("game_records.csv", "a", newline="", encoding="utf-8") as f: writer = csv.writer(f) writer.writerow([datetime.now().strftime("%Y-%m-%d %H:%M:%S"), user, computer, result])这个功能虽然简单,但能让你的项目看起来更“专业”。而且通过分析游戏记录,你可能会发现一些有趣的规律,比如自己在输了一局之后倾向于出剪刀,或者电脑在连续出两次石头之后倾向于出布。这些规律本身就是很好的数据分析练习。
6. 从猜拳游戏延伸出的编程思维
6.1 状态管理的重要性
猜拳游戏虽然简单,但它涉及到一个核心的编程概念:状态管理。游戏的状态包括当前是第几局、玩家赢了多少局、电脑赢了多少局、平了多少局。这些状态需要在程序的整个生命周期内保持一致,不能出现“赢了但计分板没加”或者“退出了但统计没显示”的情况。
在实际开发中,状态管理是一个很容易出问题的地方。我的经验是,尽量把相关的状态放在一起管理,比如用一个字典或者一个类来存储所有状态变量。每次修改状态的时候,都要想清楚这个修改会不会影响到其他状态。比如增加胜局的同时,也要增加总局数,这两个操作应该绑定在一起,不能分开写。
6.2 防御性编程的习惯
猜拳游戏让我养成了一个很重要的习惯:永远不要相信用户的输入。用户可能会输入任何东西,你的程序必须能处理所有可能的输入,而不是假设用户会按照你的期望输入。这种“防御性编程”的思路,在实际工作中非常重要。
具体来说,防御性编程包括几个方面:第一,对所有外部输入做验证,确保它们在合法范围内;第二,对可能出错的操作做异常处理,比如文件读写、网络请求;第三,对边界情况做特殊处理,比如除以零、空列表、空字符串。这些习惯看起来是小事,但能避免很多线上事故。
6.3 从简单项目中发现深层原理
猜拳游戏看似简单,但如果你深入思考,会发现它涉及很多计算机科学的基础概念。比如随机数的生成原理、伪随机数的周期性、取模运算的数学性质、状态机的设计模式、用户交互的流程控制。这些概念在更复杂的项目中会反复出现,如果你能在猜拳游戏这个简单的场景里把它们理解透彻,以后遇到复杂问题时就能更快地找到解决方案。
我个人的体会是,学习编程不要一味追求“高大上”的项目,有时候把一个小项目做深做透,收获反而更大。猜拳游戏我写过很多遍,每一遍都有新的体会。第一遍学会了基本的输入输出和条件判断,第二遍学会了函数封装和异常处理,第三遍学会了面向对象和图形界面,第四遍学会了状态管理和数据持久化。每一次重写,都是对编程理解的一次深化。
6.4 测试驱动开发的初步体验
如果你想让猜拳游戏的代码质量更高,可以尝试测试驱动开发(TDD)的思路。具体来说,就是先写测试用例,再写实现代码。比如对于judge函数,你可以先写几个测试用例:judge(0, 1)应该返回“你赢了”,judge(1, 0)应该返回“你输了”,judge(0, 0)应该返回“平局”。然后运行测试,看着它们失败,再写实现代码让测试通过。
这种开发方式虽然一开始会慢一点,但能帮你写出更健壮的代码。因为你在写实现之前,就已经想清楚了所有可能的输入和输出。而且当你以后修改代码时,只要重新运行测试,就能知道有没有破坏原有的功能。Python自带的unittest模块或者第三方库pytest都可以用来写测试。
import unittest class TestJudge(unittest.TestCase): def test_win(self): self.assertEqual(judge(0, 1), "你赢了") self.assertEqual(judge(1, 2), "你赢了") self.assertEqual(judge(2, 0), "你赢了") def test_lose(self): self.assertEqual(judge(1, 0), "你输了") self.assertEqual(judge(2, 1), "你输了") self.assertEqual(judge(0, 2), "你输了") def test_draw(self): self.assertEqual(judge(0, 0), "平局") self.assertEqual(judge(1, 1), "平局") self.assertEqual(judge(2, 2), "平局") if __name__ == "__main__": unittest.main()写完这些测试用例之后,你会发现你对judge函数的理解更加清晰了,而且以后不管怎么修改这个函数,只要测试通过,就能保证逻辑是正确的。
6.5 代码风格与可读性
最后再聊一个容易被忽视但很重要的话题:代码风格。猜拳游戏虽然代码量不大,但正是练习良好代码风格的好机会。比如变量命名要有意义,不要用a、b、c这种名字;函数要短小精悍,一个函数只做一件事;注释要写清楚“为什么”而不是“是什么”;代码缩进要统一,不要一会儿用空格一会儿用Tab。
我见过很多新手写的代码,功能是对的,但读起来非常费劲。变量名是拼音缩写,函数名是拼音全拼,注释要么没有要么写的是废话。这种代码自己过两个月再看都看不懂,更别说给别人看了。所以从猜拳游戏开始,就养成写“干净代码”的习惯,对你以后的编程生涯会有很大的帮助。
我个人在实际操作中的体会是,猜拳游戏虽然简单,但它是一个非常好的“编程实验室”。你可以在里面尝试各种新的想法和技术,不用担心搞砸,因为代码量小,重写成本低。而且每次重写,你都会发现自己对编程的理解又深了一层。这种不断迭代、不断精进的过程,才是学习编程最有趣的地方。