简介:一份聚焦计算机博弈竞赛的辅导课件,源自东北大学机器博弈研究室教学讲稿,面向参赛学生、算法爱好者及研究者。内容从棋类介绍与分类切入,系统讲解中国象棋、国际象棋、围棋、五子棋、六子棋及民间棋类规则,进而覆盖计算机博弈基本原理、博弈树展开与分析、常用搜索与α-β剪枝方法、棋局评估体系以及机器博弈的方法学特点与学科关系,可帮助读者建立从规则认知到算法设计的完整知识链路。课件同时给出典型棋类的棋盘、行棋规则与胜负判定,并通过图示直观呈现博弈树搜索与评估流程,适合课余自学和竞赛辅导。包体为单份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*3+j]] 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 局,记录胜负。你会发现,有时仅仅把搜索深度加一层,胜率就大幅提升;评估函数加一个位置分,中盘控局能力也明显变强。这就是计算机博弈最迷人的地方:小改动带来肉眼可见的进步。
第三个阶段:研读资料里的棋类分类与规则细节,找竞赛真题或往届题目的规则说明,按竞赛时限去限时调试自己的程序。资料里提到的六子棋、点格棋、苏拉卡尔塔棋等冷门棋种,恰恰是竞赛中对手准备最不充分的地方。如果你能挑一个冷门棋种深入研究,比赛中的胜率往往比死磕中国象棋更高,因为对手缺少针对训练。
回想我自己第一次参加校内博弈竞赛时,前一天晚上才发现评估函数把“将死”局面误判成负分,差点全场翻车。从那以后,每次改完代码,我都会强制自己走一遍“基础残局测试 + 十盘自对战记录胜率”的流程,再也不敢直接拿新版本上场比赛。这条习惯,比任何一次调参都更能保证你的程序稳定发挥。希望你从这份资料出发,少走我走过的弯路,早日写出第一个能赢你的棋类程序,然后在竞赛里拿到自己满意的成绩。希望帮到你。
本文还有配套的精品资源,点击获取