基于Python与Pygame的五子棋AI实战:从棋盘建模到Alpha-Beta剪枝
2026/9/8 17:29:22 网站建设 项目流程

1. 先盘清楚需求,再动键盘

1.1 一个看似简单但极易失控的小项目

五子棋游戏,表面上看就是一张棋盘棋子,你一颗我一颗,谁的五个子连成线谁赢。但你真做起来就会发现,这玩意儿的坑一点都不比做一套后台管理系统少。

我做这个版本时,最初的需求列得很朴素:支持人机对战和双人对战,能悔棋,能重开一盘,电脑棋力不能太弱。等真正动手才知道,光一条“电脑棋力不能太弱”就把我按在地上摩擦了很久。因为五子棋的规则简单到小学一年级就能学会,但它背后的博弈复杂度,却足以让一台普通电脑在纯暴力搜索下原地卡死。

先说清楚我做的方案选型:用 Python + Pygame 做大界面,逻辑层全部独立成纯 Python 模块,AI 用“贪心打分 + 有限深度极小化极大搜索”的组合策略。为什么要用 Python?因为逻辑表达清晰,做原型迭代最快,Pygame 在 2D 棋盘类游戏里足够轻量,不需要引入沉重的引擎。

如果你问能不能用其他技术栈,当然可以。我见过用纯 JavaScript 在网页里实现的版本,也见过用 Electron 套壳的桌面版,还见过有人把它做成了小程序。但从个人练习或中小规模项目角度出发,Python + Pygame 依然是我愿意推荐的首选组合,原因后面会详细讲。

需要特别提醒的是:一旦你决定要做一个“AI 不下低级臭棋”的五子棋,重心就会迅速从界面绘制跑到状态评估和搜索决策上。所以这个项目看似没有多少工程量,却覆盖了数据结构设计、规则建模、启发式评估、搜索剪枝、UI 事件循环这几个硬核模块,完整做下来,比做十个 CRUD 增删改查都锻炼人。

1.2 哪些人适合拿它练手,以及它到底能练什么

我做完这套以后回头复盘,认为它最适合三类人。

第一类是刚学完 Python 基础,但不满足于写爬虫和命令行小工具的人。因为你要处理二维数组、类设计、事件循环、图形绘制,还要把一个逻辑上的棋盘状态映射到像素坐标上。这能帮你把基础语法真正用起来。

第二类是对 AI 算法有兴趣但不想一上来就啃深度学习的人。五子棋的 AI 是典型的传统博弈 AI,不依赖 PyTorch 或 TensorFlow,用一套评估函数加搜索算法就能做出肉眼可见的智能感。你可以从零开始理解什么叫评估、什么叫深度、什么叫剪枝,这些概念将来迁移到其他棋类或者决策类问题上一样成立。

第三类是想做个人作品集的人。五子棋有完整交互、有图形界面、有计算机“思考”过程,是一个能够清楚展示工程能力和算法思维的微型项目。

这里我还要说句容易得罪人的实话:如果你只会调 API,从来没有独立设计过一个包含状态管理和决策模块的程序,那你做这个项目的过程会很难受。但难受恰恰说明踩到了成长点。

1.3 项目架构怎么切才顺手

我把整个程序拆成了棋盘模型、规则引擎、AI 决策、界面交互四层。它们之间是单向调用关系。

棋盘模型负责存储 15×15 格子的状态,用二维列表保存,空位是 0,黑子是 1,白子是 2。规则引擎负责判断落子是否合法、检查胜负、统计棋型。AI 决策接手落子建议,它读棋盘状态,算候选点,打分,返回一个坐标。界面交互层只做一件事,把鼠标点击转换成棋盘坐标,调用 AI 或本地玩家逻辑,最后把棋盘画出来。

四层分离的最大好处是:你可以写单元测试,不用打开窗口就能验证胜负逻辑和 AI 的计算结果。我做的第一版最吃亏的地方,就是把棋盘数组和界面画布耦合在了一起,结果每次想测一个边界条件,都要手动点几十次鼠标,人都要疯了。第二版重构后,我把核心算法全部做成无界面依赖的纯函数,测试效率一下子提了上来。一个稳定的项目,核心逻辑必须跑在无头环境下。

2. 棋盘建模和棋型识别,是整个项目的地基

2.1 二维数组与坐标转换,不能图省事

棋盘建模我选 15×15 标准规格,为什么是 15×15?因为这是五子棋最常见的比赛规格,太小的棋盘容错性低,容易先手必胜,太大会拖慢 AI 搜索。初始化不用 NumPy,直接用 Python 内置的列表推导式生成二维数组即可。毕竟一个 15×15 的规模,内置结构完全够用,没必要为此引入重依赖。

这里有一个非常容易出错的点:二维数组的行列顺序与屏幕坐标的对应。我建议统一把board[x][y]定义为“第 x 行第 y 列”,其中 x 代表横向像素坐标换算后的列,y 代表纵向行。但这样做在打印棋盘时反而不直观,因为控制台输出通常是先行后列。如果你在调试时打印棋盘,会看到行列是反的。

我的解决方案是:逻辑层全部用“先行后列”,即board[row][col],在做像素转换时再按行列映射到(x, y)。简单来说,就是棋盘的左上角原点的第一维永远留给行号,第二维留给列号。这样写 AI 评估时,遍历横向、纵向、斜向都顺了,不容易绕晕。

落子动作的合法性判断很简单,两步:坐标不能越界;格子必须为空。你可能会想,这能有什么坑?还真有。Pygame 的鼠标事件返回的是像素坐标,你在 UI 层要通过除法和取整把它归算到最近的交叉点上。如果棋盘左边距和上边距不一致,归算就会偏移,常见的表现是“明明点到这一格,棋子却跑到隔壁”。所以界面设计时,棋盘绘制区域至少要有一个起始偏移margin,换算公式是:

row = (mouse_y - margin) // cell_size col = (mouse_x - margin) // cell_size

千万不要把 margin 遗漏掉,这个细节我在测试时踩过,一旦棋盘画的不是顶满窗口,点击偏差就会出现,用户会立刻觉得这个游戏是“坏的”。

2.2 胜负有四种方向,少一个都不行

胜负判断的朴素做法是,每次落子后从当前点出发向四个方向检查:水平、垂直、正斜、反斜。每个方向分别向两侧延伸,数出同色连续棋子数,总数大于等于 5 即判胜。

这个逻辑听上去毫无障碍,但有一个隐蔽问题:如果你只是单方向延伸,一旦遇到对手的棋子或边界就停,那么从当前落子点出发,可能把两端棋型拆成了两段。比如当前落点把两段己方棋子连接成了一个长连,但你只从落点向一侧数,很可能只数出 4 颗,明明已经赢了却判不出来。

正确做法是把四个方向各写成一个独立函数,每次从落子点出发向左和向右同时延伸,加起来减去一次当前重复计数的棋子,才是完整的连续长度。更稳妥的方案是每下一步棋后直接扫描全棋盘,检测是否有任意连续五子,而不是只围绕落子点检查。虽然多花一点 CPU 时间,但逻辑简单到不可能出错。考虑到棋盘只有 225 个格子,全盘扫描的性能代价完全可以忽略。

我把规则引擎写成了check_win(board, row, col, player)返回布尔值,再额外写一个get_winner(board)用于终局界面的整体判定。比赛过程里,每次落子后只调用前者;到了残局或者需要展示终局结果时,再调用后者做兜底。这样既保证性能,又保证准确性。

2.3 棋型识别才是 AI 棋力的分水岭

如果你只用“某点周围同色子多就下哪里”这种粗暴逻辑,那 AI 下的就是幼儿园棋,永远不知道防守。要让 AI 有点水平,必须先定义棋型。

我归纳了五种基础棋型:连五、活四、冲四、活三、眠三。所谓活四就是“两头都通畅的四连”,你堵了任何一头,它依然能连成五;冲四则是“只有一头通”的威胁形态;活三是“再有一个子能变成活四”的形态;眠三是“被堵了一头或者只能变成冲四”的形态。

评分上,我给连五设成超大分值,比如 10000000;活四大约 100000;冲四 10000;活三 5000;眠三 800。这样设置的核心原因是:一旦棋盘上出现了这一威胁,AI 必须立刻做出反应,这些分值的相对比例比绝对数字重要。如果活三的分值设定太高,超过冲四,AI 就可能不去堵对方的冲四,反而自己在边上搭活三,结果被对手一记连五带走。

棋型识别的一个难点在于,五子棋不是只看四个方向的局部连续区间,还要看当前落点与两侧棋子的距离。我见过不少人用一个滑动的五元组窗口把棋盘扫一遍,统计窗口里各种颜色的数量来粗判棋型。好处是快,缺点是无法区分活和眠——同样是四个白子加一个空位,XXXX__XXXX_完全不在一个威胁等级。

我的做法是写一个基于方向的连续棋型扫描器。从一个起点出发,沿着某个方向把连续的同类棋子和其后的空位抓出来,生成一个字符串,再返回字符串里的形态描述类型。最后根据形态类型转成分数,放进候选点评分里。实现代码比滑动窗口长一点,但精准得多,识别出来的活三不会和眠三混淆。

3. AI 决策引擎:让电脑真正会下棋

3.1 贪心评分法,先跑通一轮再谈优化

刚开始做 AI,我不建议上来就写 Alpha-Beta 剪枝。很多人容易一头扎进剪枝搜索,结果连评估函数都没写好,AI 表现依然很笨。我建议分两步走:先做一轮纯贪心版本,再叠加搜索深度。

所谓贪心版本,就是在所有空位里,对每个位置计算两个分数:AI 自己如果下这里的进攻分,以及如果对手下这里对 AI 构成威胁的防守分。它最终的分值是“进攻分 + 防守分 × 防守系数”的加权。我先用一个遍历找出所有进攻分最高的点,再找出防守分最高的点,然后看两个核心点位的关系做取舍,等于先建立一套基础的“有攻有守”逻辑。

具体实现时,我会维护一个数据结构,里面保存候选点的坐标、进攻分、防守分和总分。每次轮到 AI 下棋,先把每个空位都遍历一遍。如果你嫌 225 个空位都跑一次棋型扫描太慢,也可以在每次落子后只更新受影响的行列斜线上的候选点,而不是全局重算。我最初图省事做了全局全量重算,依然能跑起来,只是后面搜索深度增加到两步时,性能明显吃紧。

第一版贪心写完后,实测效果基本上能打败不思考的新手,但会出两种丑态。第一种是不懂得连下后续,第二步就断开,导致明明有活三的局面最后变成散子。第二种是过于执着堵对手活三,一步一步跟着别人走,毫无策略布局可言。这是正常的,说明你需要引入多层搜索了。

3.2 极小化极大与 Alpha-Beta 剪枝,正确的复杂度降维

真正让 AI 摆脱“一步看一步”的,是对未来局面的提前搜索。最基础的模型是极小化极大:假设黑方是 AI,白方是玩家。轮到 AI 落子时,它要挑一个让己方收益最大的点;下一层玩家落子时,它会认为玩家要挑一个让 AI 收益最小的点。于是落子点收益值的传递是极大、极小、极大交替进行。

五子棋的搜索树极为庞大,全量搜索根本吃不消。所以要在搜索时限制深度,比如只看两步或四步,也就是 AI 下一步、对手下一步、AI 再下一步,这样棋盘上的连续威胁基本能体现出来。同时必须做剪枝。Alpha-Beta 剪枝的原理可以这么理解:你手里已经找到了一个不错的落子候选,它至少能带来 80 分收益,那这时候你发现某个候选点在下一层模拟中的某个分支会让收益掉到 60 分以下,你就不再需要细看这个候选点的其他分支了,因为它不可能超过你手里的 80 分。剪枝能把需要展开的节点数从指数级压到大约平方根级别。

实际操作中,剪枝带来的提速非常可观。同样的单步评估,我用朴素极小化极大做两步搜索大概要计算 20000 多个节点,加上排序启发后 Alpha-Beta 剪枝只需要计算 3000 多个节点,速度差了接近七倍。

3.3 候选点生成策略,绝不能把全部空位丢进搜索

我见过最天真的搜索优化是:盲目加大搜索深度,然后等电脑算到睡着。为什么不能直接搜深度四?因为如果每个节点都要考察 225 个空位,哪怕每一步都做一些启发式过滤,搜索次数依然是指数爆炸。

我做了一组限制:搜索时只考虑距离已有棋子两格以内的空位。比如棋子落在 (7,7),我最多搜索范围内 (5,5) 到 (9,9) 这个 5×5 扩展块里的空点。事实上对于大多数局面,真正有价值的落子点一定紧贴现有棋子,因为离所有棋子都很远的点既形不成攻势,也做不了防守,当前阶段没必要算。

进一步优化是“由评到搜”。我先用贪心评估函数给每个候选点排序,把分数最高的前 N 个点挑出来进入深层搜索。N 我通常设为 10 到 12 个。这招非常务实,因为绝大多数最佳落子点都存在于前 10 个高分候选点中。这种策略牺牲了极小概率的最优解,换取了巨大的性能提升。

在 AI 的最终实现里,我的落子流程是:先用贪心扫描生成所有候选点并排序,如果最高分超过必胜阈值(比如大于 1000000),直接落子,这通常是连五或活四这类绝对胜负手;否则取前若干个候选点进入两层 Alpha-Beta 搜索;搜索完成后,谁的总分高选谁。

这套组合下来的棋力稳到什么程度?我在普通笔记本上做了一次人机对战,AI 每一步思考时间在 0.1 到 0.3 秒之间,棋力和谨慎的业余玩家有来有回。如果你接着把搜索深度提升到 4 层,并继续用候选点收缩,仍然可以保持秒级响应,但棋力提升的空间边际效应已经变小,这时候再回头调评分函数,比盲目加大深度更有用。

4. 从界面到交互,让游戏“像”一个游戏

4.1 棋盘绘制与棋子反馈的核心参数

用 Pygame 绘制时,建议把窗口默认设置成 700×700 像素,margin 设为 40 像素。这样棋盘的落子区横跨 (40,40) 到 (660,660),格子边长是 44 像素。棋盘线条用灰色调,背景用偏木质的颜色,纯黑和白太刺眼。棋子半径可以按格子边长的 42% 计算,太大容易和旁边格子粘连,太小视觉上又显局促。

绘制完成后需要把鼠标位置捕捉到最近交叉点,这时需要做像素坐标到棋盘坐标的换算。每次点击时先做宽松判断:只要点落在整个棋盘区域附近(margin 以外的格子范围内),就允许落子。你可以在 UI 层做一个鼠标移动到交叉点附近时显示半透明预览棋子的交互效果,这会让整个游戏手感细腻很多。具体做法是在主循环里捕获MOUSEMOTION事件,将鼠标坐标换算成行列,绘制一个带 alpha 的圆。

棋子落下的动画,如果不做也行,但要有一点即时反馈。我用的方案是播放一个很短的音效,并让刚落下的棋子变成高亮色。这样玩家能分出自己刚下的是什么,不会盯着满屏黑白点看不出自己走到哪里。

4.2 悔棋和重开,逻辑上比你想的麻烦

悔棋的实现,表面看只要把棋盘上最后一颗子清掉。但人机对战时有个特殊逻辑:AI 走后,如果你悔棋一次,应该把玩家刚下的那一手和 AI 的回应一起撤销,回到玩家落子前的状态。否则玩家悔一步棋,棋盘上却依然多出一个 AI 子,没法正常接续。

我实现的悔棋栈不单纯保存坐标,还保存每一步的行列、棋手身份,以及局面的编号。每次落子,把本步推进栈里。执行悔棋时,如果是双人模式直接弹出最后一步;如果是人机模式且轮到玩家走棋,要连续弹两步;如果轮到 AI 走棋,只要弹一步即可。这算是我认为最容易写乱的一段逻辑,不建议节省这个“栈”,它值得你专门用一个列表来维护。

重开一盘相对简单,直接清空棋盘、清空历史栈、重置当前执子方。不过这里有一个常被忽略的细节:先手权可以轮换或由玩家选择,每次点击重开时不要总是硬编码成黑棋先手。

我做了两种模式:如果本局是玩家执黑获胜,重开后玩家继续保持执黑;如果玩家是执白获胜,可以提示是否交换执子方。这个细节能够让游戏更有变化,不至于每局都是黑棋走同样开局。

4.3 人机对战模式下,轮转状态机别写死

人机对战的状态切换强烈建议用状态机来设计,不要用一个布尔变量 in_player_turn 一路硬戳下去。因为中途会有 AI 计算、游戏结束、玩家悔棋这些打断性事件,布尔变量很容易被搅成一团浆糊。

我的状态定义是:PLAYER_TURNAI_THINKINGAI_MOVE_DONEGAME_OVERWAITING_REMATCH。AI 不直接在事件循环里同步跑搜索,而是用一个启动线程去执行 AI 决策。搜索完成后把结果放到一个队列,主循环检测到队列里有结果再更新棋盘。这样 UI 不会在 AI 思考时变成卡死状态,棋盘上可以播放“AI 思考中”的提示动画。

从双人对战切换到人机模式时,尤其要小心开局回合归属。默认配置让玩家执黑先行,如果玩家是白棋,那第一手必须由电脑先下。不要把它留给玩家手动触发。

5. 禁手规则到底做不做,我是怎么取舍的

5.1 只做民间规则,也能跑得很开心

在实现过程中,有个绕不开的纠结:要不要做黑棋禁手规则。

所谓禁手,是指黑棋在正规比赛规则下禁止下出“三三”“四四”或“长连”等特殊棋形,这是为了平衡黑棋先手的巨大优势。如果做禁手,你需要在规则引擎里额外判定这些棋形;如果不做,你实际上用的是民间玩法。对大多数个人项目而言,我建议第一版不做禁手,原因非常简单:不做禁手机的判断逻辑清晰,先手优势在普通玩家之间没有大到不可接受,大多数人下五子棋图的是休闲,而不是竞技。

但如果你和我一样,想做一个相对严谨的版本,就必须考虑禁手导致的另一个连锁问题:AI 作为黑棋时,它不能主动下出禁手点;作为白棋时,又要学会“逼”黑棋下到禁手点上。逼禁手比起识别禁手难得多,因为需要让 AI 主动往“黑棋看起来最诱人,实则违反规则”的位置引导。我第一版没有做逼禁手,所以白棋 AI 对抗黑棋玩家时,并不会刻意制造禁手陷阱,它的打法更偏向“堵死你”。

这里我建议先把棋型识别模块做到足够可靠,再做禁手判定。禁手判定不是单独判断一个点是不是“双三”,而是要看下在这个点后形成的四个方向棋型中,同时出现两个或以上活三、或两个或以上冲四、或者是六子以上长连。这些都需要把棋型识别函数拆得更底层,允许你从任意位置、任意方向去取棋型。

5.2 先手优势,需要靠难度策略来稀释

如果把黑棋先行优势原样留给人类玩家,AI 胜率会受到明显影响。所以我在难度设置里做了一层补偿。

简单难度下,AI 有 30% 的概率把评分第一位的位置弃掉,改选第二位甚至稍弱一点的位置,故意放水。中等难度下 AI 依然执行完整搜索,但不会使用“必胜点提前返回”的机制,因此它偶尔会漏掉必胜手。困难难度则是完整评估,开局阶段还会从内置开局库里面调用几段定式,争取把先手主动权抢回来。

开局库其实很简单,就是保存了一些流行开局的前几手坐标序列。程序在棋盘为空或手数很少时,直接从库里提取下一步,而不是重新搜索。因为刚开始几个子离得太远,所有候选点评分都很分散,AI 反而容易下出怪局。开局库能避免这一点,让 AI 前几步至少下在合理区域,后面再交给搜索来接手。

6. 开发中容易踩的坑,我帮你提前避开

6.1 五个方向的细节问题

第一,是胜负裁判漏判斜向。我在早期测试中发现吃子模式下的对角方向少加了一行坐标增量,导致所有从左上到右下正好连成五子的局面永远不判胜。原因是row + icol + i的循环里写错了边界。这个问题非常隐蔽,因为多数测试都集中在水平和垂直连线上,斜向连线的测例如果没特别添加,很少会被大家注意到。我的建议是把四种方向都写一个独立测试用例。

第二,是悔棋时没有正确处理 AI 轮次。玩家悔棋时如果棋盘上只剩玩家一颗子、AI 还没来得及回应,我的第一版直接弹掉两个栈帧,居然把空棋盘弹出负历史,界面瞬间报错。后来我改成先判断当前轮到谁,再决定弹出步数,这个错就消失了。

第三,是候选点排序不稳定导致 AI 偶尔“优柔寡断”。评分法因为棋型扫描的顺序固定,导致完全对称的局面可能随机选中某一侧,视觉上显得摇摆。解决办法很简单,对同分的候选点加入一个小小的随机扰动,或者按照到棋盘中心的距离做次级排序,这样 AI 决策更稳定。

第四,是鼠标点击的坐标判断用错事件。Pygame 的MOUSEBUTTONDOWNbutton属性,如果你把右键的点击也算作落子,就会出现“左右键一起下子”的诡异结果。记得只对event.button == 1做响应。

第五,是在窗口尺寸变化时忘记重新计算缩放后的格子坐标。如果做了窗口缩放,就不能继续用画布固定尺寸的旧参数去换算落子坐标,坐标偏到天边去了。如果你不想处理这个复杂度,一开始就设置pygame.RESIZABLE但固定内部渲染尺寸,再通过缩放变换输出,不要让逻辑层感知新的窗口尺寸。

6.2 性能监控和调试技巧,教你一个笨方法

你以为 AI 慢,是算法慢,其实有一大半时间是赢在 debug 打印上。早期版本我把评估函数里每个候选点的分数都打印到终端,225 个点全打印一次,每步棋的调试输出几十屏,终端渲染速度反而比算法耗时还高。后来我加了一个全局 debug 开关,默认关闭,只在需要单步分析某个局面时手动打开,精确打印前五个候选点的评分明细。

另外,你需要一个“无头测试”套路。我把棋盘状态序列化成一个字符串,比如 225 个字符,黑子用 1,白子用 2,空用 0。测试时可以直接把某个实战残局拼成字符串,载入棋盘,然后让 AI 决策,输出它会选的位置。通过这样的方式,我能准确复现一个局面,并在不同的评分系数下对照 AI 的选择变化。经过几十个局面案例的反复对比,你才会真正理解评分体系里每一个数值的影响。

6.3 常见问题排查,直接给出对照表

下面这份速查表是从我自己项目的调试记录里整理出来的,不一定覆盖所有情况,但对大多数相似实现都有参考价值:

现象主要原因解决办法
点击位置落子偏移一格像素坐标换算时漏算或算错 margin核对行列换算公式中的边距补偿
明明五子连珠却不判胜棋型检查方向不完整,或循环边界写错写出四个方向的独立单测,覆盖斜向
AI 从不防守防守分的权重过低,敌方的棋型没参与扫描在候选点评分中加入对手视角的分值扫描
AI 越下越慢搜索深度增加后未做候选点过滤只保留有棋子邻近的候选点,二次排序后取前 N
悔棋后轮到下的人不对没有考虑 AI 是否已经落子修改悔棋逻辑,按当前轮次决定弹栈步数
程序启动时窗口卡住在主线程里执行长耗时搜索用线程跑 AI 搜索,通过队列回传结果
双人对战能落子但颜色不变玩家切换身份逻辑被 if 写死检查玩家身份切换是否发生在每次落子后

每一行背后都是一个真实的下午,我调试到头皮发麻才解决的问题。你看看自己的项目里有没有同样的影子,有就立刻去修。

6.4 难度调节的数值经验

难度调节不只是控制搜索深度。我试过只切换深度,发现效果并不理想,因为玩家在不同难度下感受到的差异不单纯是“电脑反应速度快了”,而是“电脑是不是会突然走出一步让我头疼的棋”。

比较有效的做法有三个维度。第一是搜索深度,简单模式只搜 0 层,即贪心直接用;中等模式搜 2 层;困难模式搜 4 层。第二是候选点数量,简单模式下候选池扩大到 40 个,然后在其中随机选一个分数前五的点;中等模式前 12 个里选;困难模式前 5 个里选,并且尽量选分数第一。第三是落子延迟,简单模式可以在 AI 决策完成后故意增加 0.5 秒延迟,模拟一种“不紧不慢”的感觉,但键盘反馈仍然正常。

这三个维度最好全部开放,做成一个可调的配置字典,方便你在游戏设置界面里切换。如果以后想加难度,不需要改代码,只加一个配置条目即可。

7. 后续扩展方向和小技巧,我聊几句实在的

这个项目本身做完后,我最受益的不是最终能下赢一个什么样的 AI,而是每一步决策都要面对“如何量化一个棋局好坏”“如何在有限计算时间内做取舍”“如何设计一个能让玩家理解的交互状态机”这些看似抽象的问题。它们会在你以后再写任何带状态、带策略的程序时反复出现。

如果你还想继续扩展,可以考虑给 AI 加入基于蒙特卡洛树搜索的变体,或者把棋盘规格改成标准规则以外的模式。不依赖预定义评分函数,蒙特卡洛树搜索通过大量随机模拟来估计每一步的胜率,在复杂度上比 Alpha-Beta 更灵活,但也更耗费计算资源,做出来会是一个完全不同风格的 AI。

最后说一个小经验:我强烈建议你在一开始就为自己保留一套命令行版本。什么意思?就是除了 Pygame 图形界面外,把棋盘渲染成一个纯文本的终端图形,空格子用点,黑子用 X,白子用 O。这样你在测试 AI 算法时,不需要打开窗口,直接用命令行就能复盘每一步的落子。我后来很多 bug 都是在这个纯文本版本下快速重现的,窗口版本负责展示,终端版本负责调逻辑,两个版本共用同一套核心代码。这个项目做完之后,你不仅收获了一个能玩的五子棋游戏,更重要的是锻炼了“拆分系统”的思维模式,这可能是这个项目带给我最值钱的回报。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询