
简介这是一份基于Python实现的五子棋小游戏完整项目资源面向Python初学者与游戏开发入门者帮助学习者掌握图形界面编程、事件响应、棋盘逻辑判断及音效/字体等多媒体资源集成方法。压缩包共19个文件包含9个核心Python源码含主程序、AI逻辑、UI模块等、3张PNG棋盘与棋子素材、1个可双击运行的pyw启动脚本、1种中文字体ttf确保中文显示正常以及ogg/mp3音效文件、git配置文件和README.md说明文档整体大小为5.4MB。目前已有1050人学习下载。资源采用清晰的模块化结构含client客户端、lib工具库、data资源目录等子目录配套详细注释与README指引支持本地双击运行无需额外配置即可体验人机对战与胜负判定功能是理解Pygame或tkinter游戏开发流程的实用参考案例。1. 为什么我把五子棋作为Python练手项目的首选先聊点实际的。我见过太多学Python的朋友学了列表、字典、循环、函数之后突然不知道该做什么了。语法书翻了好几遍教程也跟了不少一提到“自己写个东西”就卡壳。其实这很正常语法是砖头你得有个房子才能知道砖头怎么垒。五子棋就是我心目中那个特别适合用来“垒房子”的项目。这个项目到底能练什么我拆开给你看二维列表的操作棋盘本质就是一个15x15的二维数组、事件驱动编程鼠标点击落子、GUI布局画棋盘、画棋子、逻辑判断五子连珠的胜负判定。这些刚好覆盖了Python入门到进阶中间最实用的一层。你不需要懂什么高深的算法不涉及类继承的复杂设计模式更不需要多线程、网络编程这些容易把人劝退的东西。但做完之后你对Python的理解深度会和单纯刷语法题完全不一样。另外有一点我特别想强调五子棋的反馈感很强。你写的东西能立刻在屏幕上跑起来点一下鼠标就落一颗子连成五个就弹提示。这种即时反馈对学习动力的维持非常重要。很多人在学习过程中坚持不下去就是因为代码太抽象——做了半天看不到任何实际效果。五子棋不会给你这种感觉每一步改动都能立刻看到变化这种成就感是刷100道算法题也给不了的。这篇文章我就用纯Python标准库Tkinter来写不装任何第三方包。这样你只要电脑上有Python就能直接跑不用折腾pip安装那些乱七八糟的依赖。整个项目大概200多行代码我会一步步拆开讲清楚每一块的原理和坑。2. 技术选型Tkinter、Pygame还是Web方案写图形界面游戏Python里常见的选择就那么几个Tkinter、Pygame以及用Flask/Django搞个网页版。我先说结论这个项目我推荐Tkinter原因后面详细说。对比项TkinterPygameWeb方案安装难度Python自带零安装需要pip install pygame需要Flask/Django还有前端知识学习曲线平缓控件就那几种稍微陡峭需要理解渲染循环最陡前后端都要会代码量200-300行够用300-400行前后端加起来400行起步适合人群Python入门3个月以内有游戏开发兴趣的初学者想走Web方向的初学者画面效果够用像素风能做动画、音效上限高取决于前端水平我见过很多新手一上来就选Pygame理由是“游戏开发当然要用游戏库”。但这里有个误区Pygame的力量在于它给你一个自己控制画面的画布和游戏循环但同时也意味着你所有的UI组件都要自己画、所有的事件都要自己处理。对于五子棋这种棋盘固定、交互模式固定的游戏Pygame属于杀鸡用牛刀反而增加理解负担。Tkinter的优势在于它自带Canvas画布组件你可以在上面画直线、画椭圆、绑定鼠标事件这些正好覆盖五子棋的全部需求。而且Tkinter本身就是Python的标准库你在任何一台装了Python的电脑上都能跑不需要额外配置环境。这种“零依赖”的特性对于刚入门的朋友来说太友好了——要知道好多初学者最后不是被代码难倒的而是被环境配置劝退的。如果你非要问Pygame在什么情况下我更推荐我大概会说是你想做有动画效果的游戏比如棋子落下时的平滑移动、胜利时的闪烁特效或者你想加音效。这些需求Tkinter确实做起来比较吃力。但就五子棋这个项目而言Tkinter是性价比最高的选择。3. 棋盘与棋子的数据建模这是整个游戏的地基3.1 为什么用15x15的二维列表五子棋的棋盘标准是15路也就是15x15的交叉点。为什么不是围棋的19路因为五子棋的连五需要在对局中快速形成15路的棋盘既保证了足够的战略纵深又不会让对局拖得太长。实际开发中15行15列的二维列表是最直接的建模方式。我用的数据结构是# 棋盘状态0表示空1表示黑棋2表示白棋 board [[0 for _ in range(15)] for _ in range(15)]有人可能会问为什么要用二维列表而不是字典这是个好问题。字典当然也能存用(row, col)作为key但二维列表的优势在于访问方便board[row][col]直接读写语义清晰遍历方便嵌套循环直接扫描整个棋盘内存占用固定15x15不就是225个整数完全没压力还有一个容易犯的错我见过不少新手这样写board [[0] * 15] * 15这样写会出大问题。Python里[[0] * 15] * 15创建的是同一个内层列表的15个引用意味着你改board[0][0]board[1][0]、board[2][0]也都会跟着变。这是一个非常经典的Python陷阱所以一定要用列表推导式来创建独立的行。3.2 棋子的表示简单用1和2就够了棋子的状态我用1表示黑棋、2表示白棋。为什么不用字符串或者对象因为纯整数在判断时最高效、代码也最简单。如果你想把代码写得更有扩展性也可以像下面这样定义常量EMPTY 0 BLACK 1 WHITE 2这样做的意义在于当你后续想加AI功能时这些常量的语义会更加清楚。而且在整个代码里你不需要记忆数字和颜色的对应关系。这个习惯建议从开始写代码就养成等代码量上来之后好处会越来越明显。3.3 当前落子方与回合管理五子棋是双人游戏所以需要一个变量记录当前该谁下。我用current_player表示初始值为BLACK。每次成功落子后切换current_player 3 - current_player这个写法很简洁因为3 - 1 23 - 2 1刚好在黑白之间切换不用写if判断。这种小技巧在平时写代码时很实用但在换人逻辑里还是建议看懂了你再写——如果你觉得用if更直观那就用if代码可读性永远是第一位的。4. 胜负判断的完整实现五子连珠的判定逻辑4.1 从哪个方向判断胜负判断是整个五子棋游戏最核心的算法部分。逻辑说难不难就是检查以刚落子位置为中心四个方向水平、垂直、左斜线、右斜线上是否有连续的五个同色棋子。我来说一个我踩过的坑新手最容易想到的做法是每次落子后扫描整个棋盘把所有连续的同色棋子都数一遍。这样做不是不行但效率很低而且代码写起来很绕。更好的做法是只从当前落子的位置向四个方向延伸查找。具体思路是这样的以当前落子位置为起点沿着一个方向往前数连续且同色就计数遇到不同色或边界就停下再沿着相反方向往后数同样的逻辑两个方向的计数加起来再加上当前位置本身就是这条线上连续同色棋子的总数如果总数大于等于5就赢了这个算法的时间复杂度是O(1)不会随着棋盘变大而变慢因为最多只需要检查8个方向各4步的距离。4.2 方向向量的设计四个方向可以定义为坐标变化量代码会更整洁# (行增量, 列增量) directions [ (0, 1), # 水平方向 (1, 0), # 垂直方向 (1, 1), # 右下-左上方向 (1, -1) # 左下-右上方向 ]每个元组表示在这个方向上移动一步时行和列各自的变化量。比如水平方向行不变列加1垂直方向行加1列不变右下方向行和列都加1左下-右上方向行加1列减1。4.3 核心判断函数下面我写一个完整的判断函数def check_win(row, col): 在(row, col)位置落下棋子后检查是否获胜 color board[row][col] if color EMPTY: return False for dr, dc in directions: count 1 # 当前位置已经有一颗棋子了 # 正方向延伸 r, c row dr, col dc while 0 r 15 and 0 c 15 and board[r][c] color: count 1 r dr c dc # 反方向延伸 r, c row - dr, col - dc while 0 r 15 and 0 c 15 and board[r][c] color: count 1 r - dr c - dc if count 5: return True return False这个函数里最关键的部分是越界检查。0 r 15和0 c 15这个条件必须放在最前面因为一旦下标越界board[r][c]会直接抛异常。4.4 为什么只判断当前位置周围的棋子就够了这个问题我专门提一下。五子棋的规则是“五个或以上连续同色棋子”你只需要检查最后落下的那颗棋子是否形成了五连。原因很简单在落这颗棋子之前棋盘上是没有五连的如果有游戏早该结束了。所以任何新的胜利组合都必须包含这颗新落下的棋子。这一来判断范围就从整个棋盘缩小到了以落子点为中心的四条射线逻辑量和代码量都大幅减少。还有一个边界情况值得提在15x15的棋盘上如果一直没人赢理论上可能下满225手。这时候需要判断平局。我习惯在落子计数达到225时弹出“平局”提示。5. 从像素坐标到棋盘坐标Tkinter交互实现5.1 画布绘制棋盘Tkinter里的画布组件叫Canvas非常直观。你需要设置一个画布尺寸然后在上面画线、画圆。我用的画布尺寸是600x600每个格子间距40像素边距20像素。这样计算下来棋盘线覆盖的区域是20到20 14 * 40 580像素每个交叉点的坐标是20 col * 40和20 row * 40创建画布canvas tk.Canvas(root, width600, height600, bg#DEB887) canvas.pack()然后画15条横线和15条竖线for i in range(15): # 横线 canvas.create_line(20, 20 i * 40, 580, 20 i * 40, fillblack) # 竖线 canvas.create_line(20 i * 40, 20, 20 i * 40, 580, fillblack)画完之后你还需要在棋盘上标记天元和星位。五子棋的标准棋盘上有5个星位分别在中间和四个角的中点。位置是(7, 7)、(3, 3)、(3, 11)、(11, 3)、(11, 11)用小圆点画出来for x, y in [(7, 7), (3, 3), (3, 11), (11, 3), (11, 11)]: px 20 x * 40 py 20 y * 40 canvas.create_oval(px - 3, py - 3, px 3, py 3, fillblack)5.2 鼠标点击与坐标换算这是整个交互里最容易出问题的地方。鼠标点击事件返回的是相对画布的像素坐标你需要把它换算成棋盘的行列坐标。换算公式很直白def click_handler(event): col round((event.x - 20) / 40) row round((event.y - 20) / 40) # 检查是否落在棋盘范围内 if not (0 row 15 and 0 col 15): return # 检查该位置是否已经有棋子 if board[row][col] ! EMPTY: return # 落子 board[row][col] current_player draw_piece(row, col, current_player) # 判断胜负 if check_win(row, col): ... # 弹出胜利提示, 重置游戏 return # 切换玩家 current_player 3 - current_player这里有一个关键细节像素坐标和交叉点坐标的偏移。我用的公式是(event.x - 20) / 40这里的20就是棋盘边缘留白的宽度。因为第一个交叉点位于x20处第n个交叉点位于x20 n*40处所以反过来知道像素坐标减去20再除以40就能得到列号。你可能会问为什么用round()而不是int()因为鼠标很少能精确点中交叉点round()可以让鼠标点击位置在交叉点附近的格子区域内都归到最近的交叉点上体验更好。比如你点到了x39的位置(39-20)/40 0.475round之后是0仍然认为是第0列刚好是这个格子的中心附近。5.3 绘制棋子绘制棋子用create_oval画椭圆填充成黑色或白色def draw_piece(row, col, player): x 20 col * 40 y 20 row * 40 r 17 # 棋子半径 color #000000 if player BLACK else #FFFFFF canvas.create_oval(x - r, y - r, x r, y r, fillcolor, outlineblack)这里有个细节白棋的轮廓线要画成黑色否则在浅黄色棋盘背景上看不清楚。黑棋则无所谓填充黑色就能看清但加上黑色轮廓也不影响视觉效果。6. 完整的游戏主循环与代码整合6.1 界面布局我把整个界面分为两个部分上面是画布下面是控制按钮。控制按钮至少要有两个重新开始和悔棋。root tk.Tk() root.title(五子棋 - Python小游戏) root.resizable(False, False) canvas tk.Canvas(root, width600, height600, bg#DEB887) canvas.pack() button_frame tk.Frame(root) button_frame.pack(pady10) restart_btn tk.Button(button_frame, text重新开始, commandrestart_game) restart_btn.pack(sidetk.LEFT, padx10) undo_btn tk.Button(button_frame, text悔棋, commandundo_move) undo_btn.pack(sidetk.LEFT, padx10) canvas.bind(Button-1, click_handler)布局上我推荐用容器Frame来组织按钮这样后续想加更多按钮比如“保存棋局”“载入棋局”会很方便不用重新调整布局。6.2 用列表记录落子历史让悔棋变得简单悔棋功能怎么实现很简单用栈的数据结构。每次落子时把坐标push到一个列表里悔棋时pop出来把棋盘上那个位置重置为空再重新绘制棋盘。move_history [] # 记录[(row, col, player), ...] # 落子时 move_history.append((row, col, current_player)) # 悔棋时 def undo_move(): if not move_history: return row, col, player move_history.pop() board[row][col] EMPTY canvas.delete(all) # 清空画布 draw_board() # 重绘棋盘 draw_all_pieces() # 重绘所有棋子这里要注意悔棋之后当前玩家应该是刚才悔掉那颗棋子的玩家。所以你需要从move_history的最后一个或根据当前玩家状态来恢复。6.3 重绘棋盘无脑重绘还是增量更新说到重绘这里有个选择每次操作后直接canvas.delete(all)全量重绘还是只更新变化的部分刚开始写的时候我建议全量重绘。代码简单、不容易出Bug而且15x15的棋盘总共才225个格子重绘一次的耗时完全可以忽略不计。Tkinter的Canvas绘制几百个对象轻轻松松不会有任何性能问题。当然如果你以后想把游戏做得更精致比如加落子动画那就需要增量更新了。具体做法是在draw_piece时只画那一个新的圆不删除其他元素。用对象ID来管理piece_id canvas.create_oval(...)这样后续想移动、删除这个棋子可以通过ID精确操作不需要全局重绘。6.4 完整代码我把整个核心代码拼起来你直接复制就能跑import tkinter as tk EMPTY 0 BLACK 1 WHITE 2 SIZE 15 CELL 40 MARGIN 20 CANVAS_SIZE MARGIN * 2 (SIZE - 1) * CELL class Gomoku: def __init__(self): self.root tk.Tk() self.root.title(五子棋 - Python小游戏) self.root.resizable(False, False) self.board [[EMPTY for _ in range(SIZE)] for _ in range(SIZE)] self.current_player BLACK self.move_history [] self.game_over False self.canvas tk.Canvas( self.root, widthCANVAS_SIZE, heightCANVAS_SIZE, bg#DEB887 ) self.canvas.pack() button_frame tk.Frame(self.root) button_frame.pack(pady10) restart_btn tk.Button( button_frame, text重新开始, commandself.restart_game ) restart_btn.pack(sidetk.LEFT, padx10) undo_btn tk.Button( button_frame, text悔棋, commandself.undo_move ) undo_btn.pack(sidetk.LEFT, padx10) self.canvas.bind(Button-1, self.click_handler) self.draw_board() def draw_board(self): # 画网格线 for i in range(SIZE): self.canvas.create_line( MARGIN, MARGIN i * CELL, CANVAS_SIZE - MARGIN, MARGIN i * CELL ) self.canvas.create_line( MARGIN i * CELL, MARGIN, MARGIN i * CELL, CANVAS_SIZE - MARGIN ) # 画星位 for x, y in [(7, 7), (3, 3), (3, 11), (11, 3), (11, 11)]: px MARGIN x * CELL py MARGIN y * CELL self.canvas.create_oval( px - 3, py - 3, px 3, py 3, fillblack ) def click_handler(self, event): if self.game_over: return col round((event.x - MARGIN) / CELL) row round((event.y - MARGIN) / CELL) if not (0 row SIZE and 0 col SIZE): return if self.board[row][col] ! EMPTY: return self.board[row][col] self.current_player self.draw_piece(row, col, self.current_player) self.move_history.append((row, col, self.current_player)) if self.check_win(row, col): self.game_over True winner 黑棋 if self.current_player BLACK else 白棋 tk.messagebox.showinfo(游戏结束, f{winner}获胜) return # 平局判断 if len(self.move_history) SIZE * SIZE: self.game_over True tk.messagebox.showinfo(游戏结束, 平局) return self.current_player 3 - self.current_player def draw_piece(self, row, col, player): x MARGIN col * CELL y MARGIN row * CELL r 17 color #000000 if player BLACK else #FFFFFF self.canvas.create_oval( x - r, y - r, x r, y r, fillcolor, outlineblack ) def check_win(self, row, col): color self.board[row][col] if color EMPTY: return False directions [(0, 1), (1, 0), (1, 1), (1, -1)] for dr, dc in directions: count 1 r, c row dr, col dc while 0 r SIZE and 0 c SIZE and self.board[r][c] color: count 1 r dr c dc r, c row - dr, col - dc while 0 r SIZE and 0 c SIZE and self.board[r][c] color: count 1 r - dr c - dc if count 5: return True return False def restart_game(self): self.board [[EMPTY for _ in range(SIZE)] for _ in range(SIZE)] self.current_player BLACK self.move_history [] self.game_over False self.canvas.delete(all) self.draw_board() def undo_move(self): if self.game_over or not self.move_history: return row, col, player self.move_history.pop() self.board[row][col] EMPTY self.current_player player self.canvas.delete(all) self.draw_board() for r, c, p in self.move_history: self.draw_piece(r, c, p) def run(self): self.root.mainloop() if __name__ __main__: game Gomoku() game.run()跑起来之后你的Python窗口会显示一个木色棋盘鼠标点击落子黑白交替连成五子弹出胜利提示。到这一步一个基本能玩的五子棋就完成了。7. 实测中的Bug与体验优化7.1 点击按钮后也能落子事件穿透问题我在测试时碰到过一个很诡异的Bug点了“悔棋”按钮之后按钮所在区域下面居然也能落子。排查了半天才发现是事件绑定的问题。Tkinter的按钮和画布绑定的事件是独立的按钮在画布的上层但它本身不消耗鼠标事件点击按钮时会穿透到画布上。解决办法是给按钮做事件拦截或者在click_handler里先判断鼠标位置是否在棋盘区域内。其实我的代码里已经做了越界检查所以这个Bug在加了按钮之后并没有出现。但如果你发现点完按钮后画布上多了一颗棋子就一定要注意把棋盘区域的判断写严谨别只依赖坐标换算。7.2 落子太靠近边缘棋子画出棋盘这个问题也很经典。当col14最右侧时棋子圆心坐标是20 14 * 40 580半径17那棋子右边缘就是597画布宽度600刚好能放下。但如果你把棋盘尺寸算错了或者把CELL改成45却不调CANVAS_SIZE棋子就会画到画布外面去。通用的做法是画棋子之前先算好边界或者干脆按我推荐的方式CANVAS_SIZE MARGIN * 2 (SIZE - 1) * CELL这样画布尺寸和格子尺寸永远不会错位。7.3 棋子重绘时的顺序问题悔棋功能中重绘时需要先canvas.delete(all)清空画布再重绘所有棋子。这里有个顺序问题必须先把画布清空再画棋盘线再画所有棋子。如果先画棋子再清空画布棋子就全消失了。我用canvas.delete(all)会删除画布上所有对象包括棋盘线。所以每次重绘都要走一遍draw_board()。如果你只删了部分棋子通过对象ID那棋盘线就不需要重画。这两种方式各有优劣但我推荐全量重绘因为代码逻辑简单行为可预期。7.4 胜负提示后还能继续落子的问题有一部分人会遇到这个情况弹出“黑棋胜利”的窗口后把窗口关掉还能继续在棋盘上落子。这是因为弹出消息框后click_handler还会继续执行到self.current_player 3 - self.current_player而game_over的检查只在click_handler开头做了一次如果你用tk.messagebox.showinfo消息框是阻塞的用户关掉消息框后click_handler的剩余代码会继续跑所以需要在胜负判断之后立即return我上面的代码已经处理了这个问题。你可以在窗口标题上动态显示当前轮到谁或者在状态栏上用文字提示。我习惯在窗口标题栏显示“五子棋 - 当前黑棋落子”这样不用看棋盘颜色也能知道轮到谁。7.5 交互体验上的几个小优化除了功能性的Bug我还做了一些体验方面的优化落子时加一个短暂的高亮提示不需要太复杂落子后在最后落子位置画一个红色小方框表示“这是最新的一步”。实测下来对手感和观感提升很明显。在棋盘边缘加坐标标注第一列标A第一行标1。虽然对游戏本身没有帮助但对调试代码和后续扩展AI功能很有用。棋子颜色微调纯黑纯白在有些显示器上看不清我习惯把黑棋调成#111111白棋调成#FEFEFE保留一个极少的色差可以让黑棋的轮廓和白棋的区分在浅棕色背景上更柔和。8. 从双人对战到AI对战这个项目还能怎么扩展做完双人对战版的五子棋你的Python基本功已经过关了。如果你还想继续深入有几个明确的扩展方向按难度从低到高排列8.1 简单AI随机落子最简单的AI就是随机找一个空白位置落子。虽然毫无策略可言但至少能陪你下一局。这个实现几乎不需要改变现有代码只需要在轮到AI时让程序随机选一个空位落子即可。8.2 中等AI基于评分的启发式搜索这个阶段可以让AI具备基本的攻防意识。核心思想是给棋盘上每个空位打分然后选分数最高的位置落子。打分规则可以包括如果某个位置能帮助自己形成四个连子给很高的分数如果某个位置能阻止对手形成四个连子给类似的分数形成三连子、活二的分数依次递减这个打分逻辑虽然还不算真正的“智能”但玩起来已经有点意思了。而且它用到的核心技巧——遍历棋盘、组合计数、排序取最大——都是面试和工作中常用的基础能力。8.3 进阶AIMinimax与Alpha-Beta剪枝如果你真的想做一个“电脑下不赢”的五子棋AI那就得学Minimax算法和Alpha-Beta剪枝了。这是很多算法课程和面试里都会涉及的经典内容。当然这个阶段你需要开始学习递归、搜索树、评估函数等进阶知识整个项目也会从“Python入门练习”变成“算法实战项目”。不过我不建议一上来就尝试这个先把手上的双人版写扎实把代码结构理清楚再逐步扩展。五子棋的好处就在这里它像一台功能齐全的开发机从最简单的语法练习到最复杂的搜索算法都能在同一个项目里循序渐进地练。8.4 加音效和动画如果你对游戏体验有更高的要求可以加一个棋子落下的音效或者做一个简单的落子动画。Tkinter实现动画确实有点费劲但也不是不行用after方法定时更新位置就行def animate_piece(row, col, player, step1): if step 10: return # 更新棋子位置, 稍微偏移 ... canvas.after(20, lambda: animate_piece(row, col, player, step 1))说实话我给Tkinter加动画的次数并不多因为它的定位就是轻量级GUI工具。如果你发现自己在动画上花了太多时间那就说明你该换Pygame了。9. 一个被忽略的细节环境配置和常见运行报错最后说个很多人刚上手时会遇到的问题——代码写完运行报错。我挑几个最常见的9.1 运行报错 No module named tkinterWindows的官方Python安装包默认带Tkinter但如果你用的是Linux服务器或者精简版Linux发行版大概率没有图形界面支持库需要额外安装。# Debian/Ubuntu sudo apt install python3-tk # CentOS/RHEL sudo yum install python3-tkinter如果是macOS系统自带的Python版本不一定带Tkinter建议直接安装官网的Python然后把python路径指到新装的版本。9.2 中文显示乱码Tkinter对中文的支持比较稳妥但有两个地方容易出问题关闭窗口标题的编码错误用root.title(五子棋 - Python小游戏)没问题消息框里的中文tk.messagebox.showinfo里的中文在Linux下偶尔会出现编码问题需要设置UTF-8环境变量如果你遇到了试试在文件开头加上import sys import io sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodingutf-8)不过Tkinter的messagebox通常不受这个影响这只是帮助输出调试信息不乱码。9.3 窗口太大超出屏幕如果你把画布改成800x800在某些小屏幕笔记本上会放不下。一个解决办法是设置最小分辨率检查或者把画布尺寸改成根据屏幕自适应。不过最省心的还是保持600x600这是Tkinter在绝大多数显示器上都能正常显示的尺寸。10. 写完这个项目后你接下来该练什么一款能玩的五子棋写完之后你在Python这条路上算是迈出了真正的一步。从“会语法”到“能做东西”这个跨越的价值比很多课程都值钱。我建议你接下来按这个路径继续先把这个五子棋程序重构一遍把代码拆成Board、Game、AI三个模块你会发现类设计其实比想象中好用然后试试给不同玩家加不同的计时规则比如要求每个玩家在30秒内落子超时就判负再到给AI加评分逻辑甚至试试让两个AI自己对战。五子棋这个项目就像一块建筑材料你可以无限往上盖楼。我自己的很多编程习惯其实都是从这种小游戏项目里练出来的。比如模块化的意识、状态管理的思路、事件驱动的理解——这些在后来的Web开发、爬虫项目、自动化脚本里全都用得上。最后说一个小技巧写完代码后在GitHub上建个仓库把代码传上去。哪怕只有几十行哪怕写得再幼稚这也是属于你的开源项目。以后找工作时一个持续更新、真实可运行的小项目比简历上写“精通Python”有说服力得多。我自己招人的时候看到那种有几个小游戏开源作品的简历好感度会一下子高很多——那说明这人真的动手写过代码而不只是背过概念。本文还有配套的精品资源点击获取