首页 / 资讯中心 / 文章详情

计算机博弈入门:从博弈树搜索算法到棋类AI程序实战

计算机博弈入门:从博弈树搜索算法到棋类AI程序实战 ★ FEATURED ARTICLE
简介一份聚焦计算机博弈竞赛的辅导课件源自东北大学机器博弈研究室教学讲稿面向参赛学生、算法爱好者及研究者。内容从棋类介绍与分类切入系统讲解中国象棋、国际象棋、围棋、五子棋、六子棋及民间棋类规则进而覆盖计算机博弈基本原理、博弈树展开与分析、常用搜索与α-β剪枝方法、棋局评估体系以及机器博弈的方法学特点与学科关系可帮助读者建立从规则认知到算法设计的完整知识链路。课件同时给出典型棋类的棋盘、行棋规则与胜负判定并通过图示直观呈现博弈树搜索与评估流程适合课余自学和竞赛辅导。包体为单份PPT演示文稿大小约2.57MB便于直接阅读与课堂上使用。该资源目前已获得329人学习下载可作为计算机博弈竞赛入门与进阶复习的实用参考资料。1. 别再觉得它是玄学计算机博弈到底是什么你第一次打开“计算机博弈竞赛辅导资料”这种标题时大概率会愣一下计算机博弈是研究怎么让电脑打游戏还是研究怎么用电脑下棋为什么这也能办竞赛别急着翻页先花两分钟把概念理清后面所有内容都好懂了。先说结论计算机博弈简单说就是“教电脑下棋”。它属于人工智能里一个非常经典的分支——程序在一个棋盘上按照规则自己走棋、自己评估局面、自己决定下一步最终目标是赢过人类对手或其他程序。你平时听到的“AI下棋”“机器博弈”说的都是这件事。这份资料能解决什么问题它其实是一套竞赛辅导材料面向初学者、准备参赛的学生以及单纯好奇“程序怎么学会下棋”的人。它从棋类分类讲起一路讲到博弈树、评估函数、搜索算法和程序的整体架构。换句话说它帮你把一个“看起来很玄”的问题拆成几块能理解、能上手、能动手做到的小步骤。这不是一本天书而是一张地图。2. 先回答一个关键问题为什么通过下棋入门人工智能2.1 下棋是规则最简单、反馈最明确的“练手场”想学人工智能最容易劝退新手的地方是问题太开放。比如“让程序识别一张图片里的猫”听起来简单但猫有千万种姿势背景千变万化你根本不知道从哪开始建模。而棋盘类游戏完全不一样——棋盘大小固定、棋子种类固定、每一步的合法走法有限胜负判定清清楚楚。规则的确定性让“程序思考”这件事变得无比透明。这也是为什么人工智能从诞生之初就拿棋类做实验场。早年的机器博弈程序甚至能在一个很小的棋盘上打败人类爱好者靠的就是“规则清楚搜索就能跑起来”。你看到的中国象棋、国际象棋、围棋、五子棋、六子棋全都有成熟的竞赛体系每一种棋都能训练你在不同层面上的编程与算法能力。更难得的是棋类游戏的“输赢”非常激励人。你写完一个程序立刻可以跟自己对弈赢了就有成就感输了就知道哪里要改。这种“即时反馈”是入门阶段最值钱的东西——程序哪里弱你一眼就能看到不像其他AI方向跑一次训练要半天还看不出好坏。2.2 竞赛是检验程序思维的最佳尺子很多人问我自学编程就够了为什么要参加竞赛因为竞赛逼着你在有限时间内、面对未知对手把程序调到最可靠的状态。电脑下棋这件事平时自己玩是一种感受放到竞赛规则下又完全是另一回事。计算机博弈竞赛通常分两类一类是写一个棋类程序跟同场比赛的其他程序对战另一类是给定同一个棋盘规则比谁的搜索算法更深、评估函数更准。无论哪一种你都要处理“走棋慢会被时间惩罚”“评估不准会走出昏招”“搜索太浅会被对手抓住破绽”等等实际问题。竞赛辅导资料之所以存在就是因为比赛时踩过的坑高度相似。评估函数怎么设计、博弈树怎么展开、搜索深度跟时间怎么平衡——这些经验如果没有人整理你自己要摸很久。有了资料等于有人先把坑填上了一半你只需要沿着路走然后自己把剩下的一半踩平。2.3 任何棋类都能成为你的第一个项目——从最简单的简易棋开始资料里介绍了大量棋种中国象棋、国际象棋、围棋、五子棋、六子棋、点格棋、苏拉卡尔塔、亚马逊棋甚至还有华容道这种单人滑块游戏。看到这么长的清单新手容易慌我总不能每种都学吧完全不用。竞赛里通常只要选一种棋深入做下去就够了。我最建议的策略是先挑一个规则最简单的棋种练手。比如一字棋规则只有“走子距离不限不许逆行先到终点者胜”这种级别或者井字棋先用它们把“博弈树搜素到底是什么”搞清楚然后你觉得哪个棋有意思、规则好实现再把它升级成你的主力项目。很多第一次参赛的同学最容易犯的错就是一上来就选中国象棋或者围棋这种复杂棋类。结果棋盘状态数大得惊人搜索写不好评估函数也调不动最后连一个能跑通的对弈程序都没做出来。从简单棋到复杂棋是一个循序渐进加代码、加模块的过程不是一步登天。3. 底层的 5 个概念从棋盘到搜索的完整链条3.1 完全信息动态博弈为什么棋和牌不一样资料里特别指出了一件事棋类属于完全信息动态博弈牌类属于不完全信息动态博弈。这两个词直白翻译就是下棋时双方都能看到棋盘上的所有棋子打牌比如斗地主、德州扑克时你不知道对手手里有什么牌。信息透明是一切算法的前提。因为棋局对双方完全可见理论上双方各自都能算出“当前局面下所有可能的后续变化”。这听起来简单但实际计算量非常大。以中国象棋为例棋盘是 9×10红黑各 7 个兵种共 16 个子走一步棋后对方可能有几十种回应。你要是把未来十步的所有变化都列出来分支数量大到天文数字级别。所以计算机博弈的核心任务就变成了在有限资源下尽可能把博弈树往深了搜、把没用的分支剪掉、把有价值的局面“估出分来”。所有后续概念都是围绕这三件事展开的。3.2 博弈树把一步棋“展开”成未来的图谱“博弈树”这个词听着玄幻其实就是把“如果走 A对手走 B我再走 C……”的这种推演画成一棵从根节点往下不断分叉的树。根节点是当前棋盘每个子节点是走完一步之后的新棋盘一层一层往下长。你可以把博弈树想象成一本“未来选择手册”。当前局面是第 0 层你走了一步之后到达第 1 层对手回应一步到达第 2 层以此类推。树的每一层代表一个玩家在决策搜索程序的任务就是站在这棵树的根部往下多看几层然后选一条对自己最有利的分支。这里有个初学者极易混乱的点自己走棋那层想选最好对手走棋那层必须假设对手也选对他最好的招。也就是说你在第 1 层找出一个局面但真正兑现的可不一定是它——对手会在第 2 层把你的好局面毁掉。简单说搜索要考虑的是“最坏情况下我能得到的最好结果”。3.3 棋局评估给棋盘打一个分数搜索把未来局面展开之后程序怎么判断谁占优这就需要一个“评估函数”。评估函数输入是一个棋盘局面输出是一个数值正数表示当前一方占优负数表示另一方占优绝对值越大优势越大。评估函数最简单的做法是数子力。国际象棋里通常给兵 1 分、马 3 分、象 3 分、车 5 分、后 9 分程序把双方子力各自加起来相减就得到一个粗略棋局分数。中国象棋也可以这样设计给车、马、炮、兵各自定一个分值再加上棋子位置分、威胁分等等。但评估函数没有“完全正确”一说它只是工程上的近似。资料里讲到的“人工评估”“启发式搜索”本质都是想让这个分数更贴近真实优劣势。你调得越准程序的中盘就不会乱走调得偏了程序会在某些局面上做出莫名其妙的选择这就是所谓的“棋感差”。3.4 搜索与剪枝从“穷举”到聪明的选择既然评估函数能给局面打分搜索就可以工作了。最经典的算法叫“最小最大搜索”minimax思路是我方的每一层选最大对手的每一层选最小最后根节点选出得分最高的那步棋。但纯最小最大搜索有一个大问题——太慢。国际象棋、中国象棋这种棋中盘每一步几十种合法走法要搜到第 8 层计算量常人无法承受。于是就有了 alpha-beta 剪枝。它会在搜索过程中发现某个分支已经明显不如已经找到的最佳分支就不再展开这个分支的剩余部分直接砍掉。剪枝听着神奇做起来一点也不玄本质上就是“这个分支最坏情况已经比已知最优差没必要看下去”。合理使用剪枝可以在相同时间内让搜索深度几乎翻倍。这是竞赛里拉开差距的关键技能之一也是资料第二部分反复强调“基于搜索”的原因。3.5 一个完整程序的结构从直观的棋盘到背后的思考评估函数和搜索算法听起来高大上但它们在一个完整的计算机博弈软件里并不是全部。资料里已经点明了典型软件的四部分构成用户界面、棋盘模拟、搜索算法、评估函数。这四部分完全可以看成一条流水线。用户界面负责画棋盘、接收你的落子指令棋盘模拟负责维护棋子的位置、合法走法、胜负判断搜索算法负责把博弈树往下推演并找到最优走法评估函数在搜索的终点给局面打分。四者各司其职哪一块弱都会拖累整体。我刚学的时候走过一次弯路一上来就猛写搜索算法完全没设计好棋盘的数据结构结果每一步只能走通固定的几条线路改都改不动。后来才明白棋盘模拟是地基评估函数是方向搜索算法是手段。新手学习时也建议按“棋盘数据结构 → 合法走法生成 → 评估函数 → 搜索”这个顺序来做而不是一上来就扎进算法库。4. 第一个例子用 Python 写出一个能和你对弈的搜索程序4.1 先动手跑一个最小程序能运行的井字棋 AI概念讲再多不写代码总是虚的。下面是一个用 Python 写成的井字棋人机对战程序你执 X程序执 O。程序全程使用搜索来决策——它会把当前棋盘往后模拟到终局每一步都选择对己方最有利的走法。# 井字棋AI 用最小最大搜索与你对战 # 棋盘用列表表示0空1你(X)-1程序(O) def winner(b): # 所有三连成一线的情况 lines [(0,1,2),(3,4,5),(6,7,8), (0,3,6),(1,4,7),(2,5,8), (0,4,8),(2,4,6)] for a, c, d in lines: if b[a] b[c] b[d] and b[a] ! 0: return b[a] if 0 not in b: return 0 # 平局 return None # 棋局还没结束 def possible_moves(b): # 列出所有空位即可以落子的位置 return [i for i in range(9) if b[i] 0] def evaluate(b): # 给当前局面打分程序赢返回 10你赢返回 -10 w winner(b) if w -1: return 10 elif w 1: return -10 return 0 def minimax(b, depth, is_program_turn): # 递归搜索程序回合取最高分你的回合取最低分 w winner(b) if w is not None: return evaluate(b) if is_program_turn: best -999 for m in possible_moves(b): b[m] -1 best max(best, minimax(b, depth 1, False)) b[m] 0 return best else: best 999 for m in possible_moves(b): b[m] 1 best min(best, minimax(b, depth 1, True)) b[m] 0 return best def best_move(b): # 程序挑选分数最高的一步 best -999 move -1 for m in possible_moves(b): b[m] -1 score minimax(b, 0, False) b[m] 0 if score best: best score move m return move def print_board(b): # 把棋盘打印出来方便人看 symbol {0: ·, 1: X, -1: O} for i in range(3): print( .join(symbol[b[i*3j]] for j in range(3))) print() # 主程序轮流走棋 board [0] * 9 print_board(board) for turn in range(9): if turn % 2 0: # 偶数轮由你走 while True: try: choice int(input(轮到你走输入位置(0-8): )) except ValueError: continue if choice in possible_moves(board): break board[choice] 1 else: # 奇数轮由程序走 m best_move(board) board[m] -1 print(程序走, m) print_board(board) w winner(board) if w is not None: if w 0: print(平局) elif w 1: print(你赢了) else: print(程序赢了) break把这段代码保存成tic_tac_toe.py用python tic_tac_toe.py运行就能直接在终端里跟程序对弈。输入 0 到 8 的数字落子0 是左上角8 是右下角。代码最核心的段落在minimax函数里它把当前局面递归展开成博弈树is_program_turn为真时取最高分程序希望赢为假时取最低分假设你会走对你最有利的棋最后把分数逐层传回。b[m] -1然后b[m] 0这段是“先假设自己在这里落子算完分数再还原棋盘”也就是所谓的回溯——没有它搜索会破坏原棋盘。4.2 参数怎么改从井字棋迈向真正竞赛程序的第一步上面这个程序能让新手直观地感受“搜索算法是怎么工作的”但距离竞赛用的程序还有几步。最容易改、也最有价值的两处评估函数和搜索深度。先看评估函数。井字棋的评估只有 -10、0、10 三种因为局面要么赢、要么输、要么还在继续。但放到中国象棋里一个局面通常没有输赢只有“优势大小”这时评估就要返回连续值比如“车 5 分马 3 分炮 3 分兵 1 分”这样累加。你可以试着修改上面的evaluate函数比如把“程序赢”从 10 改成 100你会观察到程序在搜索时更“敢冒风险”去追求必胜局面因为它对胜利的渴望被放大了。再看搜索深度。井字棋由于复杂度低完整搜索就能穷尽整棵树但中国象棋、五子棋这种棋盘完整搜索根本跑不完。竞赛程序通常会限制搜索层数搜到第 4 层就停止用评估函数替代继续展开。你可以试着给minimax加一个深度参数比如在depth 4时直接返回evaluate(b)感受一下深度变浅后棋力的明显下降。4.3 把代码模板迁移到五子棋、六子棋、中国象棋井字棋代码的真正价值在于它是“最小可运行的搜索框架”。把它改造成五子棋路径非常清晰先把棋盘从 9 格改成一个二维列表比如 15×15 或 19×19再把possible_moves改成“找出所有空位”五子棋没有太多走法限制再把winner改成“扫描横竖斜四个方向是否连成五子”。换成六子棋时你还要处理一个特殊规则先手第一步只下一子之后每步下两子。这意味着possible_moves一次要返回两个落子位置的组合搜索的决策粒度也变了。换成中国象棋或国际象棋最麻烦的不是搜索而是“合法走法生成”——每个兵种的行动规则不同马走日、象走田、王车易位、兵升变这些限制要在possible_moves里全部实现。我的建议是先把井字棋框架跑透确认自己理解每一行在做什么再开始换棋盘。不要一上来就想直接写中国象棋的完整搜索否则你会在排盘、吃子、将军判断这些非算法细节上耗费大量时间连搜索的正确性都验证不了。从棋类竞赛的历届经验看八成新手第一版程序失败原因都是棋盘表示和走法生成出错而不是搜索算法本身出错。5. 新手避坑指南三个最容易踩的坑5.1 坑一搜索树逻辑写对了但不会验证现象程序能跑但走出来的棋特别傻。有时明明一步就能赢它偏不走那条路或者搜索后返回的“最佳走法”在棋盘上看完全不合理。原因大多数新手写完minimax后从不测试单步局面。最常见的问题是“最高分/最低分”的层级搞反了——在程序回合取了最高分在自己的回合也取了最高分结果程序每一步都在帮对手走棋。还有一种情况是回溯忘了把临时落子还原导致搜索过程中棋盘被污染。解决先用小棋盘、固定棋局做测试。比如故意摆一个“程序再走一步就能赢”的残局看程序能不能走对再摆一个“对手下一步要赢”的局面看程序会不会阻止。只有这些基础测试全过了才轮到调整评估函数。我第一次写完搜索时程序经常往对手的胜利路线上送子排查了半天发现是递归返回分数时少了一层判断这类错误光靠看代码很难发现必须靠简单残局验证。5.2 坑二评估函数“拍脑袋”打分程序很快变笨现象程序在开局还算正常到了中盘就开始乱走经常把好位置送掉或者明明子力占优却主动兑子换劣势局面。原因评估函数设计得太粗糙。比如只数棋子数量不关心位置或者把某个子力值定得太高导致程序为了保这个子宁愿放弃整个局面优势。资料里讲到棋局评估时要考虑子力、位置、机动性等多个维度但新手很容易把一个维度放得过大。解决评估函数要“慎重建模、分层测试”。先做一个子力差的基础版本跑通再逐步加入位置分比如兵过河加分、马进中心加分每加一项就自己跟旧版本对战十局看棋力是升是降。你会发现有些看着合理的打分项实战里反而让程序变成“只会占位置、不会进攻”的防守机器。评估函数没有标准答案只能用对战数据一遍遍调。5.3 坑三只啃理论不写代码资料翻完仍不会动手现象把辅导资料的每一页都看完了博弈树、alpha-beta 剪枝、评估函数都“看懂了”真正打开电脑写程序时却不知从哪一行开始。原因这是最典型的“眼高手低”症状。资料里的每一章都在讲概念和原理但编程入门本质需要动手。博弈树的展开细节、回溯的时机、胜负判断的边界条件这些只有亲手实现过才会真正变成你自己的知识。我见过不少同学花两周看完一整份资料代码一行没写比赛报名后只剩 20 天时间只好手忙脚乱赶工。解决给自己定一个硬指标——资料每看完一章就必须实现一个对应的最小功能。看完棋类介绍就写一个能摆棋盘的数据结构看完评估函数就写一个能统计子力差的小函数看完搜索就把上一章的井字棋跑起来。这样资料看完时你已经有一个雏形程序剩下的只是优化和加模块。先跑通再理解是新手最不容易翻车的路径。6. 下一步从“跑通”到“参加竞赛”的成长路径如果你把井字棋程序写完并跑通了恭喜你你已经迈过了计算机博弈最难的入门门槛。接下来我会建议你按下面三个阶段往下走。第一个阶段扩展规则系统。挑一个你最喜欢的棋种比如五子棋或中国象棋先把棋盘表示、合法走法、胜负判断这三件事写得完整可靠。这一步不需要复杂算法只需要踏实编码。做完后你可以让程序自己跟自己对战看看有没有崩溃或非法走子。第二个阶段优化搜索与评估。在规则完整的基础上尝试给搜索加上 alpha-beta 剪枝、限制搜索深度、加入简单的子力评估。每次只改一个变量然后用新旧版本对弈 20 局记录胜负。你会发现有时仅仅把搜索深度加一层胜率就大幅提升评估函数加一个位置分中盘控局能力也明显变强。这就是计算机博弈最迷人的地方小改动带来肉眼可见的进步。第三个阶段研读资料里的棋类分类与规则细节找竞赛真题或往届题目的规则说明按竞赛时限去限时调试自己的程序。资料里提到的六子棋、点格棋、苏拉卡尔塔棋等冷门棋种恰恰是竞赛中对手准备最不充分的地方。如果你能挑一个冷门棋种深入研究比赛中的胜率往往比死磕中国象棋更高因为对手缺少针对训练。回想我自己第一次参加校内博弈竞赛时前一天晚上才发现评估函数把“将死”局面误判成负分差点全场翻车。从那以后每次改完代码我都会强制自己走一遍“基础残局测试 十盘自对战记录胜率”的流程再也不敢直接拿新版本上场比赛。这条习惯比任何一次调参都更能保证你的程序稳定发挥。希望你从这份资料出发少走我走过的弯路早日写出第一个能赢你的棋类程序然后在竞赛里拿到自己满意的成绩。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站