Python五子棋AI实战:极大极小值搜索与alpha beta剪枝

发布时间:2026/9/28 19:21:45
Python五子棋AI实战:极大极小值搜索与alpha beta剪枝 简介这是一份面向Python初学者与AI算法爱好者的五子棋AI实战项目围绕极大极小值搜索与Alpha-Beta剪枝两大经典决策树技术展开帮助读者理解零和博弈中的搜索策略与剪枝优化思路。压缩包共12个文件约150KB以py源码、xml配置、pyc缓存及doc、pdf文档为主源码模块划分清晰涵盖棋盘规则、AI搜索、玩家交互与主程序入口另附论文与参考资料便于延伸阅读。目前已有3016人学习下载适合作为课程设计、毕业设计或AI入门练手素材。读者可从中获得完整的可运行代码框架、评估函数与剪枝逻辑的实现范例以及缓存优化等性能改进思路并借助文档资料理解算法原理与项目结构快速搭建属于自己的对弈AI。1. 从「会下棋的 Python 脚本」说起极大极小值搜索和 alpha beta 剪枝到底解决什么问题很多人第一次写五子棋 AI都会掉进同一个坑让程序把每一步都算一遍结果第一步还没落子风扇已经起飞。问题不在 Python 慢而在于搜索空间是爆炸的——15×15 的棋盘空位两百多个逐层展开就是天文数字。极大极小值搜索Minimax和 alpha beta 剪枝就是把这棵「不可能算完的树」砍到能算完的两把刀。这篇要讲清楚一件事怎么用 Python 从零搭一个能真正跟你对弈、还会主动堵你活三的 AI 五子棋。它适合刚学完 Python 基础语法、想找一个「有难度但不劝退」项目练手的人也适合想复习博弈搜索、把课本伪代码变成能跑代码的开发者。核心不是界面多花哨而是搜索算法、评估函数、剪枝顺序这三块能不能咬合上。下面按「先立住原理、再动手复现、最后讲坑」的顺序推下去每一步都给能直接抄的代码和参数。2. 极大极小值搜索把「我走一步、对手走一步」变成可计算的分数2.1 为什么是博弈树而不是穷举所有落子五子棋是典型的双人零和博弈一方赢另一方就输没有中间态。极大极小值搜索的核心假设是——双方都足够聪明。轮到我走我选让局面分最高的那步轮到对手走他一定选让局面分最低的那步因为对我越低对他越高。把这两层交替展开就得到一棵博弈树。树的根是当前局面往下每一层代表一步落子层数就是搜索深度。深度为 1 只看自己这步深度 4 就是「我走、他走、我走、他走」。深度越深AI 越强但节点数按分支因子指数增长。15×15 棋盘开局有 225 个空位深度 4 就是 225⁴ 量级纯 Python 根本扛不住。所以极大极小值本身只是「正确」要「可用」必须靠剪枝和候选点筛选。这里先明确一个概念评估函数。搜索到叶子节点达到设定深度或分出胜负时需要一个数字告诉上层「这个局面对我多有利」。五子棋里最直接的做法是数棋型活四、冲四、活三、眠三、活二……每种给不同权重我方棋型加分对方棋型减分。评估函数写得好不好直接决定 AI 是「会下棋」还是「乱下棋」。2.2 用 Python 写出可运行的 Minimax 骨架先给一个最小可跑的骨架棋盘用二维列表表示0 是空、1 是 AI、2 是人类。评估函数先用简化版后面再优化。# board: 15x15 二维列表0 空 / 1 AI / 2 人类 # depth: 剩余搜索深度 # is_max: True 表示轮到 AI极大层 def minimax(board, depth, is_max): # 终止条件深度耗尽或某一方已连成五子 score evaluate(board) if depth 0 or abs(score) 100000: return score if is_max: best -float(inf) for (r, c) in get_candidates(board): board[r][c] 1 best max(best, minimax(board, depth - 1, False)) board[r][c] 0 # 回溯必须还原 return best else: best float(inf) for (r, c) in get_candidates(board): board[r][c] 2 best min(best, minimax(board, depth - 1, True)) board[r][c] 0 return best逻辑说明is_max为 True 时是 AI 层取所有走法里的最大值为 False 时是人类层取最小值。board[r][c] 0这行回溯是血泪经验——忘了还原棋盘会被填满AI 后面全是废步。参数说明depth建议从 2 起步跑通再往上加get_candidates是关键优化点绝不能返回全部空位只返回已有棋子周围 2 格内的空点能把分支因子从 200 降到 20 以内。evaluate返回绝对值超过 100000 时视为胜负已定直接剪掉后续搜索。2.3 评估函数怎么写才不「瞎下」评估函数是 AI 的大脑。常见做法是扫描四个方向横、竖、两条斜线把每条线上的连续棋型提取出来打分。下面是一个可用的权重表棋型说明建议分值活四两端都空无法阻挡100000冲四一端被堵仍能成五10000活三两端空下一步可变活四8000眠三一端被堵的三500活二两端空的两300眠二一端被堵的二50打分时我方棋型加正分对方棋型乘一个略大于 1 的系数比如 1.1后减分。这个系数是玄学也是经验太小 AI 只顾进攻不防守太大又变得畏手畏脚。我一般从 1.1 开始调对手活三时能主动去堵就算合格。提示评估函数不要每次全盘重算落子后只更新受影响的四条线性能能提升好几倍。3. alpha beta 剪枝让同样的深度少算一大半节点3.1 剪枝的直觉已经知道更差就别再看了极大极小值搜索有个巨大浪费有些分支算到一半就已经能确定它不会影响最终决策但程序还在傻算。alpha beta 剪枝就是把这个「已经能确定」提前告诉搜索过程。两个参数alpha是极大层目前能找到的最好值下界beta是极小层目前能找到的最坏值上界。搜索过程中如果发现某个节点的 beta ≤ alpha说明这个分支对上层毫无价值直接返回不再展开。剪枝效果极度依赖走法顺序——先搜好棋剪得越狠。理想情况下同样的深度节点数能从 b^d 降到 b^(d/2)相当于深度翻倍。3.2 带剪枝的完整搜索代码def alphabeta(board, depth, alpha, beta, is_max): score evaluate(board) if depth 0 or abs(score) 100000: return score if is_max: best -float(inf) for (r, c) in get_candidates(board): board[r][c] 1 val alphabeta(board, depth - 1, alpha, beta, False) board[r][c] 0 best max(best, val) alpha max(alpha, best) if beta alpha: # 剪枝极小层不会选这个分支 break return best else: best float(inf) for (r, c) in get_candidates(board): board[r][c] 2 val alphabeta(board, depth - 1, alpha, beta, True) board[r][c] 0 best min(best, val) beta min(beta, best) if beta alpha: # 剪枝极大层不会选这个分支 break return best逻辑说明极大层更新 alpha极小层更新 beta一旦beta alpha立即 break。注意初始调用要传alpha-inf, betainf否则第一层就剪没了。参数说明get_candidates的返回顺序直接决定剪枝效率。我一般按「离最后落子点距离」排序再叠加一层启发式打分能形成活三、冲四的点优先实测节点数能再降 30% 以上。深度建议 4 起步配合候选点筛选普通笔记本一秒内能出招。3.3 候选点生成剪枝之外的第二把刀很多人只盯着 alpha beta却忽略了候选点筛选才是性价比最高的优化。全盘 200 多个空位真正值得考虑的通常不超过 20 个。def get_candidates(board, radius2): candidates set() for r in range(15): for c in range(15): if board[r][c] ! 0: # 只取已有棋子周围 radius 格内的空点 for dr in range(-radius, radius 1): for dc in range(-radius, radius 1): nr, nc r dr, c dc if 0 nr 15 and 0 nc 15 and board[nr][nc] 0: candidates.add((nr, nc)) return list(candidates)逻辑说明遍历所有已落子点把它们周围 radius 格内的空点收集起来。开局棋盘空时如果没有任何棋子直接返回中心点。参数说明radius2是常用值太小会漏掉远处的关键点太大会让分支因子回升。如果棋盘上棋子很少可以先在中心附近落子避免候选集为空。这个函数配合 alpha beta是让 Python 版五子棋「能玩」的关键组合。4. 从零跑通环境、棋盘、胜负判断和主循环4.1 环境准备与依赖这个项目不需要任何第三方库标准库足够。Python 3.8 以上都行装好之后用python --version确认。编辑器用 VS Code 或 PyCharm 都可以VS Code 里装个 Python 扩展选好解释器就能跑。如果你还在纠结 python 安装教程记住一点官网下载安装包时勾选「Add Python to PATH」能省掉后面一堆环境变量问题。项目结构建议拆成三个文件board.py管棋盘和胜负判断ai.py管搜索和评估main.py管主循环和输入输出。拆开的好处是调试时能单独测评估函数不用每次跑整局。4.2 棋盘表示与胜负判断def check_win(board, player): # 四个方向横、竖、主对角、副对角 directions [(0, 1), (1, 0), (1, 1), (1, -1)] for r in range(15): for c in range(15): if board[r][c] ! player: continue for dr, dc in directions: count 1 for step in range(1, 5): nr, nc r dr * step, c dc * step if 0 nr 15 and 0 nc 15 and board[nr][nc] player: count 1 else: break if count 5: return True return False逻辑说明对每个己方棋子沿四个方向数连续同色棋子达到 5 就赢。注意副对角方向(1, -1)要判断列不越界。参数说明range(1, 5)表示最多再数 4 个加上自身正好 5 个。这个判断在每次落子后调用一次即可不用全盘反复扫。4.3 主循环人机交替落子def main(): board [[0] * 15 for _ in range(15)] board[7][7] 1 # AI 先手占中心 print_board(board) while True: # 人类落子 move input(输入坐标 row,col: ) r, c map(int, move.split(,)) if board[r][c] ! 0: print(该位置已有棋子) continue board[r][c] 2 if check_win(board, 2): print(你赢了) break # AI 落子 best_val, best_move -float(inf), None for (r, c) in get_candidates(board): board[r][c] 1 val alphabeta(board, 3, -float(inf), float(inf), False) board[r][c] 0 if val best_val: best_val, best_move val, (r, c) board[best_move[0]][best_move[1]] 1 print_board(board) if check_win(board, 1): print(AI 赢了) break逻辑说明外层循环里人类先走AI 再走。AI 这一层遍历候选点对每个点调用 alphabeta 得到分数取最高分对应的落子。注意这里is_maxFalse因为 AI 落子后轮到人类下一层是极小层。参数说明alphabeta(board, 3, ...)里的 3 是搜索深度实际项目里可以设成 4。深度每加 1耗时大约翻几倍先用 3 跑通再往上调。best_move理论上不会为 None因为候选集在正常对局中不会为空但保险起见可以加个判空。5. 避坑与排查AI 五子棋最容易翻车的五个地方5.1 现象AI 第一步就卡死风扇狂转原因候选点没做筛选直接遍历全部空位深度 4 时节点数爆炸。解决确认get_candidates的 radius 参数生效开局阶段候选点应控制在 30 个以内。如果还是慢先把深度降到 2 验证逻辑再逐步加。5.2 现象AI 明明能赢却不落子反而去堵无关位置原因评估函数里对方棋型权重给太高AI 变得过度防守。解决检查活四、冲四的分值是否远大于活三同时把对方系数从 1.1 往下调比如 1.05。另一个可能是搜索深度不够没看到自己的连五机会把深度加到 4 再试。5.3 现象程序报IndexError或棋盘越界原因胜负判断或候选点生成时没做边界检查r dr * step可能超出 0~14。解决所有坐标计算后都加0 nr 15 and 0 nc 15判断。这个坑几乎每个新手都会踩一次写个in_board(r, c)辅助函数统一处理最省心。5.4 现象AI 落子后棋盘状态错乱出现重复落子原因回溯时忘了把board[r][c]还原成 0或者还原成了错误的值。解决搜索函数里落子和还原必须成对出现建议用 try/finally 或者写一个place_and_undo辅助函数。调试时可以在每次落子后打印棋盘肉眼确认状态。5.5 现象剪枝后 AI 棋力反而下降走出明显臭棋原因alpha beta 的初始值传错或者候选点排序把好棋排在后面导致剪枝过度。解决确认初始调用是alpha-inf, betainf候选点排序时把「能形成活三、冲四」的点放前面。如果还不对先关掉剪枝跑一遍纯 Minimax对比结果是否一致能快速定位是剪枝逻辑还是评估函数的问题。6. 进阶技巧用置换表和迭代加深把 AI 再提一档跑通基础版之后如果想让 AI 更强有两个性价比很高的方向。第一个是置换表Transposition Table不同走法顺序可能到达同一个局面用字典把「局面哈希 → 分数」缓存下来遇到重复局面直接查表能省掉大量重复搜索。局面哈希可以用 Zobrist 哈希给每个位置、每种棋子分配一个随机数落子时异或更新速度很快。# Zobrist 哈希示意 import random zobrist [[[random.getrandbits(64) for _ in range(2)] for _ in range(15)] for _ in range(15)] trans_table {} def board_hash(board): h 0 for r in range(15): for c in range(15): if board[r][c] ! 0: h ^ zobrist[r][c][board[r][c] - 1] return h逻辑说明每个位置每种棋子对应一个 64 位随机数局面哈希就是所有已落子的随机数异或。落子或撤销时只需异或对应随机数不用全盘重算。参数说明trans_table建议设个上限比如 100 万条超了就清空避免内存无限增长。查表时要同时校验深度浅层结果不能直接用于深层。第二个方向是迭代加深从深度 1 开始搜逐步加深到 4 或 6把上一层的最优走法作为下一层的首选排序。这样配合 alpha beta剪枝效率会明显提升而且时间可控——设定一个时间上限超时就返回当前最优。我一般会先跑深度 2 热身再逐层加到 4实测比直接搜深度 4 快不少棋力还更稳。最后说个习惯每次改完评估函数或剪枝逻辑别急着跟人下先让 AI 自己跟自己下十局看有没有出现「双方都不堵活四」这种低级局面。这种自对弈测试比人肉试错快得多也是我调参时最依赖的后悔药。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询