新闻详情

新闻详情

首页 / 资讯中心 / 详情

Python + tkinter 实现五子棋:一文掌握GUI编程核心

发布时间:2026/10/1 14:49:33来源:尧图网络
Python + tkinter 实现五子棋:一文掌握GUI编程核心
你有没有经历过这样的场景想下几盘五子棋打发时间打开手机要忍受开屏广告切个程序还得看一段视频才能继续下有时候输了想复盘还得登录账号。后来我干脆自己用Python写了一个基础五子棋项目从零开始把棋盘、落子、赢棋判断全部实现了一遍。做完之后我有一个很强烈的感受这不仅仅是一个游戏项目它几乎覆盖了GUI编程里所有核心的基础知识点——坐标换算、数据建模、事件响应、状态管理、算法判定一样不缺。你把这一套逻辑走通往后写任何带界面的小工具思路都是相通的。这篇文章我就把完整的实现思路、关键代码和踩坑记录写下来。项目用纯Python标准库实现不依赖任何第三方包依靠tkinter提供界面适合刚学完Python基础语法、想做一个真正能跑起来的完整项目的朋友。你也可以直接照着思路改成扫雷、黑白棋、走迷宫之类的游戏核心套路都一样。1. 为什么我选了tkinter而不是pygame或网页方案1.1 先想清楚基础项目到底在练什么开始动手之前我先问了自己一个问题我这个基础五子棋项目核心目标到底是图形炫酷还是把编程基础打扎实答案是后者。五子棋本身是一个规则极其简单的游戏两个人轮流在15x15的交叉点上放棋子谁先在横、竖、斜任意方向连成五个谁赢。但正因为规则简单它的复杂度全都在怎么用代码表达一个游戏状态上。它需要你理解逻辑坐标和像素坐标的区别需要你选择一个合适的数据结构来存储棋盘需要你在鼠标事件里做坐标反算还需要你写出一个正确的胜负判断算法。这四件事正好是几乎所有桌面软件和游戏都绕不开的课题。如果我用pygame来做确实画面可以做得更华丽但pygame替你处理了很多底层事件循环的逻辑反而容易让人忽略游戏状态到底是怎么流转的这个问题。如果用网页方案那又牵扯到HTML、JS、前后端通信一堆东西早就偏离了Python练手的出发点。tkinter最合适的一点是它够原始又够完整。所有交互逻辑都得你自己写但写起来又不至于痛苦到放弃。1.2 千万别被tkinter界面丑劝退很多人一听到tkinter就皱眉觉得它做出来的界面像是上个世纪的产物。这个想法害人不浅。实际上tkinter的绘图能力做五子棋这种棋盘界面的游戏完全够用而且用Canvas组件画出来的棋盘和棋子配合合理的配色观感并不差。我在实际做完之后棋盘的质感甚至比很多网页版小游戏还要干净。更重要的一点是tkinter是Python标准库自带的你只要装了Python就能跑不需要pip install任何东西。这一点对于新手非常友好因为你不再需要先解决装包失败版本冲突这类环境问题直接把代码保存成.py文件双击就能看到窗口弹出来。这种即时反馈对保持学习动力特别重要。1.3 我做技术选型时的判断逻辑如果你也在做一个基础项目我的建议是先列一个需求清单再对着清单选工具。我的清单是能在本地双人轮流下棋、能判断五子连珠、界面不要花哨但必须看得清楚、代码量控制在合理范围。对照这份清单tkinter没有一项不满足。而像pygame、PyQt5这些框架虽然也能做但要么引入更多概念要么安装包体积大对于基础二字来说属于过度设计。选型的核心原则就一句话用最小的复杂度满足需求剩下的复杂度留给业务逻辑本身。2. 棋盘的数据骨架二维数组才是真正的主角2.1 不要一上来就画界面先想清楚数据怎么存我在动手写这个项目的时候踩过的第一个大坑就是一上来就想把棋盘画出来结果画到一半发现棋盘上的棋子和程序逻辑完全对不上号。后来我才意识到一个游戏程序里界面只是数据层的一个可视化投影真正决定游戏状态的是背后的数据结构。五子棋的棋盘是15x15那我们很自然会用一个15x15的二维数组来表示。数组里每个元素有三种状态0表示空位1表示黑棋2表示白棋。画界面的时候遍历这个二维数组遇到1就在对应坐标画一个黑圆遇到2就画白圆。判断胜负的时候也是在这个二维数组上做扫描跟界面没有任何关系。这样一拆分整个程序的逻辑就清晰了数据层负责棋局状态界面层负责把状态画出来两者通过坐标换算衔接。2.2 逻辑坐标与像素坐标的换算这是整个项目里最容易让新手绕晕的地方。棋盘上第r行第c列的交点在Canvas画布上的像素坐标是多少这里需要引入一个格子大小的概念。如果每个相邻交叉点的间距是40像素棋盘左上角的第一个交叉点距离画布边缘是30像素那么代码实现时我用MARGIN 30表示棋盘边距CELL_SIZE 40表示格距。第r行第c列的交叉点像素坐标为x MARGIN c * CELL_SIZEy MARGIN r * CELL_SIZE。反过来鼠标点击时像素坐标先减去边距再除以格距用四舍五入取到最近的交叉点。这段换算逻辑是整个程序的心脏后面所有功能都建立在它上面。2.3 初始化棋盘与基础数据结构我用一个Python类来管理整个游戏状态核心数据结构就三样二维数组、当前玩家、落子记录。初始化代码大概长这样import tkinter as tk from tkinter import messagebox BOARD_SIZE 15 CELL_SIZE 40 MARGIN 30 class Gomoku: def __init__(self): self.board [[0 for _ in range(BOARD_SIZE)] for _ in range(BOARD_SIZE)] self.current_player 1 self.history []board是棋盘数据本体current_player用1和2轮流切换history则用来记录每一步的落子坐标后面做悔棋功能时只要有这个列表就够了。2.4 落子的数据接口先判断再写入当鼠标点击棋盘时程序要做的第一件事不是画棋子而是先判断这次落子是否合法。合法要同时满足三个条件坐标在棋盘范围内、该位置为空、游戏还没结束。我可以把落子逻辑封装成一个函数这样界面层和逻辑层就彻底分开了def place_piece(self, row, col): if not (0 row BOARD_SIZE and 0 col BOARD_SIZE): return False if self.board[row][col] ! 0: return False self.board[row][col] self.current_player self.history.append((row, col)) return True这样设计有一个很明显的好处以后想加AI对战AI只需要直接调用place_piece完全不用关心界面的事情。3. 画布交互从空白窗口变成一盘能下的棋3.1 用Canvas画出棋盘网格界面部分我选用tkinter的Canvas组件它在绘制图形方面比Label、Button这些更灵活。画棋盘线其实很简单15x15的棋盘有15条横线和15条竖线两条for循环就能画完。我先创建一个宽度和高度都等于MARGIN * 2 CELL_SIZE * (BOARD_SIZE - 1) 20的画布然后画线canvas_width MARGIN * 2 CELL_SIZE * (BOARD_SIZE - 1) 20 canvas_height MARGIN * 2 CELL_SIZE * (BOARD_SIZE - 1) 20 self.canvas tk.Canvas(self.root, widthcanvas_width, heightcanvas_height, bg#E8B86D) self.canvas.pack() for i in range(BOARD_SIZE): self.canvas.create_line(MARGIN, MARGIN i * CELL_SIZE, MARGIN (BOARD_SIZE - 1) * CELL_SIZE, MARGIN i * CELL_SIZE, fill#000000) self.canvas.create_line(MARGIN i * CELL_SIZE, MARGIN, MARGIN i * CELL_SIZE, MARGIN (BOARD_SIZE - 1) * CELL_SIZE, fill#000000)背景色我用了接近木纹的淡黄色棋盘的视觉效果立刻就不一样了。这只是个小细节但实际做完之后我真心建议你花一点时间调颜色因为一个赏心悦目的界面会让你更有动力继续做下去。3.2 棋子的绘制与胜负标记画棋子的时候我在place_piece成功后从数据层读取落子位置换算成像素坐标后调用create_oval画圆。create_oval接收的是外接矩形的左上角和右下角坐标所以想画一个半径为CELL_SIZE // 2 - 4的圆要从圆心坐标往四个方向做运算。黑棋直接用黑色填充白棋用白色填充再画一圈灰色描边这样在浅色背景上更容易分辨。另外我还给最后一步做了一个红色小方框标记。做法是每次落子后先把上一局的标记删掉再在最新落子的位置画一个红色小矩形。这个功能让人能快速看清当前对局局势特别是复盘时很管用。3.3 鼠标事件的坐标拾取与容差控制鼠标点击事件绑定在Canvas上事件对象里的x和y是相对于画布左上角的像素坐标。我要把它们转成棋盘行列号转换公式就是前面说的逆运算关键点是四舍五入self.canvas.bind(Button-1, self.on_click) def on_click(self, event): x, y event.x, event.y col round((x - MARGIN) / CELL_SIZE) row round((y - MARGIN) / CELL_SIZE) if self.place_piece(row, col): self.draw_piece(row, col)但这里有个很实际的容差问题玩家点击的位置不可能精准落在交叉点上往往偏离几个像素。四舍五入能解决大部分误差可如果玩家点得离交叉点太远比如点到格子正中间也容易被换算到错的交叉点。我的解决方案是加一个距离校验只有当点击位置离最近交叉点的距离小于CELL_SIZE * 0.4时才接受这次落子。这不是必需品但加上之后操作手感会好很多强烈推荐。3.4 用tag管理画布上的棋子tkinter的Canvas有一个特别好用的功能叫tag可以给一组图形对象起一个共同的名字之后用这个名字统一操作它们。我在绘制所有棋子时都给它绑定一个形如piece_3_5的tag表示第3行第5列的棋子。这样做有两个好处一是悔棋时可以直接按tag删除特定棋子二是不用遍历board数组再费劲地找图形对象。4. 核心判定算法四方向扫描的胜负判断4.1 判定时机落子之后再查五子棋的胜负判断有个天然的优化空间每一次落子都不会影响已经下过的棋子的连珠关系唯一可能产生新五连的是以当前刚落下的棋子为端点或中间点的连线。所以在place_piece写入数据成功之后我只需要以这个新棋子为中心检查横、竖、两条斜线一共四个方向看有没有任意一个方向已经连成了五个同色棋子。这个方案比每次落子后全盘扫描所有棋子要高效得多。15x15的棋盘全盘扫描也就225个点性能差距其实不大但以落子点为中心检查的思路更清晰代码也更短。在实际对局中每次判定几乎瞬间完成没有任何卡顿。4.2 方向数组一个循环搞定四个方向判断胜负的时候可以用两个列表表示八个方向的行列增量(-1,0)表示向上(1,0)表示向下(0,-1)表示向左依次类推。但实际上横竖斜四个方向就够了因为每个方向只需要朝两个相反的方向延伸搜索找到的同色棋子数相加再加1就是当前连子的总长度。我写了一个辅助函数从指定位置出发朝某个方向数连续同色棋子数量def count_direction(self, row, col, dx, dy): color self.board[row][col] count 0 r, c row dx, col dy while 0 r BOARD_SIZE and 0 c BOARD_SIZE and self.board[r][c] color: count 1 r dx c dy return count def check_win(self, row, col): directions [(1, 0), (0, 1), (1, 1), (1, -1)] for dx, dy in directions: total count_direction(row, col, dx, dy) count_direction(row, col, -dx, -dy) 1 if total 5: return True return False这个实现很简洁四个方向各数一遍总数超过4因为加上当前棋子是5就判定胜利。这里要注意的是 0 BOARD_SIZE这种链式比较在Python里合法且可读性极好能有效防止数组越界。4.3 边界条件的双重保险写边界条件时新手最容易犯的错误是只检查了一次边界就开始访问数组结果横向越界报错IndexError。我在count_direction里把越界检查放在整个while循环的第一位每次移动后都立刻判断新位置是否越界这样绝不会出现访问负数下标或超过14下标的情况。还有一个边界细节棋盘最边缘的交叉点比如(0,0)位置检查斜向时其左上方向是(-1,-1)如果越界检查漏了程序直接崩溃。但只要你的while循环条件写得严谨这些极端位置都能安全通过。4.4 平局判定的处理当棋盘下满但仍没有五连时就是平局。我在落子逻辑里加了一个计数器判断当前已落子数是否等于BOARD_SIZE * BOARD_SIZE。如果棋盘满了没有胜负就弹出平局提示并重置棋局。这个功能写起来就一行但如果没有它程序会在棋盘满了之后依然接受点击造成逻辑混乱。5. 人机对战给电脑装一个笨但实用的脑子5.1 为什么先从打分制而不是搜索树开始基础五子棋做熟了之后很多人会想着加一个人机对战功能。我一开始也想用博弈树加Alpha-Beta剪枝后来认真评估了一下发现对新手来说这不现实。因为博弈树方案的复杂度主要包括评估函数设计、搜索深度控制、剪枝策略优化、性能调优这几个环节哪一步出了问题AI的表现都很糟糕而且调试起来非常痛苦。所以第二步我选择用打分制/权值评估法来写AI。它的核心思路是遍历棋盘上每个空位计算如果在这个位置落子能得到多少价值分——这个分数综合了进攻自己连子的潜力和防守阻挡对手连子的潜力然后挑分数最高的位置落子。这个方案的代码量不大逻辑直白虽然下棋水平只能算业余入门但作为基础项目的AI已经足够了。我搜资料的时候看到boa、rapfi这些开源五子棋AI用的都是更复杂的方法但它们背后也都绕不开评估棋型价值这一步先把打分制吃透再去理解高手的AI就不难。5.2 棋型权值表的设计打分制的灵魂是棋型权值表。你得告诉程序什么形状的棋连起来价值多少。这张表是我手动调出来的不同形状的权值设置直接影响AI的下棋风格棋型权值说明五连100000已经赢了最高优先级活四50000两头畅通无法阻挡冲四10000只有一头畅通对手必须堵活三5000再走一步能成四眠三1000被堵了一头但仍能发展活二500开局常用有发展空间眠二100潜力很小这张表的核心思想是进攻时AI优先形成活三活四这种对手必须应对的棋型防守时AI看到对手有冲四或活三也会在这张表里被赋予极高的防守得分从而去堵。5.3 遍历打分与选点实现AI每走一步程序会遍历棋盘上每一个空位分别在假设我是黑棋和假设我是白棋两种情况下给该位置打分然后把进攻分和防守分加权相加。实现思路可以写成这样def evaluate_point(self, row, col, player): score 0 directions [(1, 0), (0, 1), (1, 1), (1, -1)] for dx, dy in directions: score self.evaluate_line(row, col, dx, dy, player) return score def find_best_move(self): best_score -1 best_move (BOARD_SIZE // 2, BOARD_SIZE // 2) for r in range(BOARD_SIZE): for c in range(BOARD_SIZE): if self.board[r][c] ! 0: continue attack_score self.evaluate_point(r, c, self.current_player) defense_score self.evaluate_point(r, c, 3 - self.current_player) total_score attack_score * 1.1 defense_score if total_score best_score: best_score total_score best_move (r, c) return best_move注意我在进攻分上乘了1.1的权重让AI在攻守均衡时稍微偏进攻一点。这个系数是可以调的如果你希望AI更偏防守型就把分母反过来希望它更敢下就调高进攻权重。实测下来1.1这个值组合出的对局观感比较自然既不会龟缩防守也不会连基本的堵都忘记。5.4 开局随机与中盘策略为了不让AI每次下棋的套路完全一致我在开局前五手加了一点随机性如果棋盘上棋子少于等于5个AI会在分数最高的几个位置里随机挑一个。这个处理很小但能让AI看起来更人性化同一个存档多次新开对局会有不同路子不会让人觉得是个机器模板。5.5 进阶路线从打分制走向更强AI写完打分制之后我明显感觉到它有两个短板它只看当前一步的棋型分数缺乏对多步之后局势的预判它没有应对复杂交换、双活三等组合杀法的能力。这正是boa和rapfi这类专业五子棋AI存在的意义——它们用模式库、搜索树、机器学习的方法把棋力推到了极高水准。如果你对这个方向感兴趣我的建议路线是先把手里的打分制版本跑熟悉理解评估函数对AI风格的影响然后试着引入一层Minimax搜索最后再慢慢加Alpha-Beta剪枝。每一步改动都能明显感受到AI的棋力变化比一上来就啃复杂论文有成就感得多。6. 实测体验与三个让我头疼的坑6.1 坐标错位和最后一列点不中第一次完整跑通程序时我发现一个奇怪现象棋盘最下面一行、最右边一列特别难点中鼠标点得很偏才能落子成功。排查半天发现问题出在Canvas的滚动区域上——窗口默认大小刚好卡在棋盘边缘加上我后来给棋子画了外圈导致最外围的交叉点被窗口边缘吃掉了。解决方法很简单在创建窗口时给画布留出足够的内边距不要把画布尺寸和棋盘尺寸设成完全一样。我在高度上额外加了20像素缓冲问题就再也没出现过了。6.2 Windows高DPI下的界面模糊问题在Windows系统上如果屏幕缩放比例不是100%tkinter窗口会出现界面模糊或者鼠标坐标偏移的情况。这是因为tkinter没有自动适配DPI缩放。解决办法是创建一个缩放因子并应用在Canvas上。实测在125%和150%缩放下加了这个设置之后坐标定位准确了很多。代码片段如下import ctypes try: ctypes.windll.shcore.SetProcessDpiAwareness(1) except Exception: pass6.3 不要在事件回调里执行耗时操作写AI计算的时候我踩过一个很隐蔽的坑直接在鼠标事件回调里调用find_best_move()AI算分需要遍历225个位置、每个位置再评估四个方向在早期版本里总共要跑几十毫秒。体感上虽然不是卡死但会出现点击后界面僵住半秒的情况。后来我把AI计算放到after方法里延迟执行让界面先完成当前帧的绘制再等计算结果出来更新棋子这样交互就流畅得多。这个经验对所有带计算逻辑的GUI程序都适用UI线程千万别干活活都要放到事件循环外去。6.4 整体表现与可玩性做完之后我拉了两个朋友来实测两人对战模式完全流畅胜负判断准确无误悔棋功能稳定。AI模式强度基本上相当于一个会堵活三、会冲四的初级陪练对刚入门五子棋的玩家来说压力刚好不会无聊也不会绝望。整个项目代码量不到300行完全用标准库实现打包成exe之后在任何没有Python环境的电脑上都能直接运行作为练手项目可以说是性价比拉满。7. 几个提升体验的小技巧到这里基础项目已经完整了但我实际用下来发现还有几个小改动能极大提升使用体验。第一个是给黑棋和白棋增加不同的音效用tkinter自带的bell()方法就能实现基础提示音虽然音色一般但落子有反馈手感完全不一样。第二个是在窗口标题和图标上花点心思设置一个合适的标题栏文案打包成exe时再用图标工具换一个棋子图案整个程序的专业感立刻上来了。第三个是制作一个简易的开始菜单加一个选择双人对战还是人机对战的初始界面这个改动让程序的完整度直接上升一个档次。个人体验方面我最满意的是整个项目的模块划分逻辑部分和各种界面绘制彻底分离这也让我之后给别人展示代码时能讲得很清楚。如果你也想做一个类似的基础项目我的建议是把重点放在数据结构和判定算法上界面美化反而是次要的。先把棋下明白再把窗口画漂亮每一步都有实打实的收获。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Jenkins环境可信度校验清单:Java版本、权限模型与JENKINS_HOME设计 2026/10/1 19:31:05

Jenkins环境可信度校验清单:Java版本、权限模型与JENKINS_HOME设计

1. 为什么Jenkins安装不是“点下一步”就能完事的?很多人第一次接触Jenkins,看到官网那句“Download Jenkins LTS”就以为万事大吉——点开链接、双击安装包、狂按“Next”,最后浏览器打开 http://localhost:8080,看到那个蓝白相间…

阅读更多 →
Visual Studio 2022搭建ONNX Runtime C++推理环境完整指南 2026/10/1 19:30:58

Visual Studio 2022搭建ONNX Runtime C++推理环境完整指南

1. 为什么我最终把 ONNX 推理环境搭在了 Visual Studio 2022 里 先说下我的实际处境。模型是 PyTorch 训练出来的,效果挺满意,但到了部署阶段就犯难了。公司生产环境是 Windows C,主程序是个老牌的 MFC 桌面应用,不能为了一个模型…

阅读更多 →
对偶GAN去雾实战:PyTorch从网络结构到部署的完整指南 2026/10/1 19:30:58

对偶GAN去雾实战:PyTorch从网络结构到部署的完整指南

简介:这份资源是面向计算机相关专业毕业设计学生与深度学习实践者的图像去雾项目源码包,基于PyTorch搭建对偶生成对抗网络架构,可用于毕业设计、课程设计或期末作业等教学场景。包内共31个文件,以10个Python脚本为核心&#xff0c…

阅读更多 →
马德拉蛋糕家庭烘焙指南:从乳化原理到完美裂纹 2026/10/1 19:30:58

马德拉蛋糕家庭烘焙指南:从乳化原理到完美裂纹

1. 马德拉蛋糕到底是什么 1.1 名字背后的故事 我最早知道马德拉蛋糕,不是在西点店的柜台里,而是在一本讲英国下午茶的书上。书里写得很随意:一块外表朴素、顶部有一条标志性裂纹的黄油蛋糕,配着红茶,旁边偶尔还会放一…

阅读更多 →
PyTorch对偶GAN图像去雾实战:从环境搭建到训练调优 2026/10/1 19:30:58

PyTorch对偶GAN图像去雾实战:从环境搭建到训练调优

简介:这份资源是面向计算机相关专业毕业设计、课程设计及期末作业场景的PyTorch实战项目,核心任务是用对偶生成对抗网络完成图像去雾。项目由生成器与判别器双网络协同训练,配套训练、预测、参数解析、数据加载与可视化等模块,适合…

阅读更多 →
基于PyTorch的对偶生成对抗网络图像去雾实战:从原理到源码解析 2026/10/1 19:30:58

基于PyTorch的对偶生成对抗网络图像去雾实战:从原理到源码解析

简介:这份资源是面向计算机相关专业毕业设计、课程设计及期末作业场景的PyTorch实战项目,核心任务是用对偶生成对抗网络完成图像去雾。项目由生成器与判别器双网络协同训练,配套训练、预测、参数解析、数据加载与可视化等模块,适合…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉