简介:计算机博弈竞赛辅导资料面向准备参加计算机博弈竞赛的学生、指导教师及入门研究者,围绕机器博弈的基础原理、方法学与典型棋类建立系统认识,可用于赛前梳理知识框架、课程辅导和方案设计参考。压缩包内共1个PPT文件,大小约2.57MB,以讲义课件形式集中呈现东北大学机器博弈研究室关于计算机博弈原理与方法学概述的内容。课件从棋类介绍与分类切入,涉及中国象棋、国际象棋、围棋、五子棋、六子棋及点格棋、苏拉卡尔塔、亚马逊、华容道等对象,并讲解博弈树、搜索算法、alpha-beta剪枝、棋局评估、博弈树展开与分析、软件构成及基于搜索的方法学特点,兼及与计算机科学、数学、人工智能的学科关系。已有330人学习或下载,便于读者建立从棋类建模、评估函数到搜索实现的整体脉络,也可作为竞赛辅导备课与专题讨论的提纲。
1. 计算机博弈是什么:让机器"往前想几步"这件事
「计算机博弈竞赛辅导资料」这名字听着有门槛,其实它讲的事情离日常很近:你坐在棋盘前,落子之前总会想"我走这步,对方大概会怎么应,然后我再怎么走"——计算机博弈就是把这套"往前想几步"的动作,拆解成机器能执行的计算。一句话定义:让机器在规则明确的对抗性棋局里,靠搜索加评估选出下一步走哪里。
打个比方,它像个只会做选择题的考生,题目永远只有一道:"现在该走哪一步"。选项几十个,它没有直觉可用,只能把每个选项背后的分支摊开算分,挑分数最高的那个。
这份资料的价值也在这里:搜索、评估、剪枝这些思路,都是在这类棋盘上被反复打磨出来的;搞懂它,你就摸到了"程序如何在爆炸式可能性里做选择"的门把手。
2. 为什么偏偏是棋类:完全信息博弈给机器的天然便利
棋类被选作研究对象,不是因为它们好玩,而是因为它们"适合被计算"。这个适配性来自两条性质,一条决定了机器能不能把局面完整描述出来,另一条决定了搜索有没有意义。搞清这两条,后面所有关于博弈树、评估函数、剪枝的讨论才有落点。
2.1 棋类是完全信息动态博弈,牌类不是
先把两个词拆开看。"完全信息"说的是每个参与方对当前局面、规则和已经发生过的所有行动都看得见,没有暗着;"动态"说的是双方轮流行动、有先后顺序,不是同时落手。棋类通常被归为完全信息动态博弈,牌类则归为不完全信息动态博弈——原料里那句"不含牌,棋牌性质有很大的区别"说的就是这道分界线。
差别具体在哪?中国象棋里,红方有几个兵、黑方的车在哪一路,双方一目了然,机器的任务只是把这个可见局面复制成一份数据结构,然后开始推演。换成扑克或麻将,对手手里有什么你根本看不到,机器必须先建立一个"对手可能持有什么"的概率分布,再在这个分布上做决策。后者的难度维度完全不同——不是"算得更深"能解决的,而是要引入概率推断和不确定性下的决策模型。
这解释了为什么棋类成了算法试验田:局面可以被完整表示成一个状态快照,状态之间的转移由规则唯一确定,同一局面反复出现时结果完全一致。这意味着程序可以复现、可以调试、可以对比两组参数谁强谁弱。换成带随机发牌的游戏,"同样输入两次结果不同"会让调参变成玄学。
还有一层容易忽略:动态意味着回合制。回合制让"你一步我一步"的推演天然形成树状结构,机器可以按层展开。同时行动的对抗问题就没这么规整了,需要考虑对方和你同时选择时的组合情况,模型的形状都不一样。
2.2 人们为什么爱下棋,机器就为什么盯上棋
原料里列了五条"人们为什么喜欢下棋",这五条其实每一条都能翻译成机器视角的优势,值得逐条对一遍。
第一,目的在于决出胜负,并关联某种收获。对应到程序,就是必须有一个明确的终局判定:将死、无子可动、连成线、占据格子多。有了这个判定,搜索才有终点,评估才有目标。
第二,规则简单明确,成功与失败的判定标准简单。这一条对机器极其关键——规则简单意味着"走法生成"这个函数好写,不需要海量常识。很多现实问题难就难在"什么算一步合法的操作"都说不清,棋盘上没有这种模糊地带。
第三,博弈过程透明、公平,不包含任何机会或偶然性,唯一的例外是先手优势。无偶然性意味着同样的局面永远得到同样的评估,调试时你能确定问题出在代码而不是运气。这也是为什么棋类程序的强弱可以用对局数客观衡量。
第四,智力和经验决定胜负,问题的解决在认识意义上不需要过多的知识。换句话说,棋力主要来自"算得深"和"评得准",而不是背了多少百科常识。这正好是搜索算法和评估函数能发挥作用的地方。
第五,变化无穷,很难走出完全相同的棋谱。这一条保证了研究不会很快撞到天花板:中国象棋平均每步有几十种合法走法,走到第六层就是几十亿个局面,穷举不动,永远有优化空间。
看懂这五条对应关系,你就明白机器博弈为什么长期是搜索算法的练兵场——它把"决策"这件事压缩成了一个干净、可判定、可重复的数学问题。
2.3 按人数和按着法分类:棋不是一种东西
棋类内部差别极大,乱套一套方法必然翻车。原料给了两条分类线索,一条按参与人数,一条按着法性质。
按人数分:双人弈棋占绝大多数,象棋、围棋、五子棋都是;单人游戏如华容道,本质是滑块移动;多人游戏如跳棋,多方参与。按着法分:走子类是开局前双方摆好棋子、开局后轮流走动,中国象棋、国际象棋、跳棋、苏拉卡尔塔、亚马逊都属于这类;填子类是开局盘面全空、双方轮流放子,围棋、五子棋、点格棋是代表;混合类则是先填子、到一定条件后转为走子,还带吃子,五道棋和日本将棋属于这一类。
| 分类维度 | 类别 | 代表棋种 | 对程序设计的直接影响 |
|---|---|---|---|
| 参与人数 | 双人 | 中国象棋、国际象棋、围棋、五子棋 | 直接对应两方对抗模型,一方得分即另一方失分 |
| 单人 | 华容道 | 退化为最短步数搜索,没有对手 | |
| 多人 | 跳棋 | 多方利益不一致,不再是严格的零和 | |
| 着法类型 | 走子类 | 中国象棋、国际象棋、苏拉卡尔塔、亚马逊 | 存在子力概念,评估可以算子力加位置 |
| 填子类 | 围棋、五子棋、点格棋 | 盘面由空到满,评估要看形状、连接与势力 | |
| 混合类 | 五道棋、日本将棋 | 要按阶段切换评估策略,前期重布局后期重吃子 |
这张表不是学术洁癖,它直接决定你写评估函数时该看什么。走子类棋里,一步吃子会让盘面分数立刻跳变,"子力价值"是硬通货;填子类棋里,落下一子当下不产生任何收益,价值要很多步之后才显现,所以围棋才那么难——它要求评估函数看懂"势力"和"厚薄"这种模糊概念,而不是数棋子。
具体到盘面参数,各棋种差异也很实在:中国象棋棋盘 9×10,红黑各 7 个兵种、16 枚子,还有长将、长捉的限制,60 步不吃子判和;国际象棋 8×8,六个兵种各 16 枚子,有王车易位、兵升变、吃过路兵这些特殊规则;围棋 19×19,靠占地多者胜,先手要贴目 5 到 7 目;五子棋 15×15,无禁手规则下先手优势被证明大到影响竞技性,于是有了禁手、换手等规则改良;六子棋 19×19,先手只下一子、之后每手两子,用"多给后手一颗子"的方式来削先手优势,复杂度反而更高。
再看几个小众棋种,能看出规则设计对程序的影响有多细。一字棋的走子距离不限,至少一步、至多走到终点,不许逆行,可以一次移动一个或多个棋子,但必须都停在同一个目标位上;二虎棋由虎方先走,犬方每次走一子到邻近空位,虎方可跳吃且每次只吃一子,虎方吃够一定数量获胜,犬方则靠围困让虎方全部无法走动取胜;三通棋有 64 个正三角形棋位、双方各 32 枚子,轮流布子,乙方有一次连续下两着的机会,先实现"三通"者胜;点格棋靠连边围格,最后一个占边者得到这个格子并要再连一边,程序里要专门识别死格、双环、长链短链和环;苏拉卡尔塔除了吃子外每步只能走一格,吃子必须沿弧线走完一整段弧,吃光对方才算赢;亚马逊每方四个"后",每个着法包含移动和设障两部分,比拼活动范围,无子可动判负;华容道是滑块类单人游戏,一次把一个人物移进空位,直到最大那块移到下方中央出口,国外常把它和魔方、独立钻石并称为智力游戏里的三大不可思议。
把这些摆在一起你会发现,"棋"只是一个笼统的称呼,盘面尺寸、子力概念、胜负条件、特殊规则,每一样都会改写程序结构。这也正是这份辅导资料要先讲棋类再讲原理的原因——不认清对象,方法学就是空转。
3. 一台会下棋的程序由什么组成:从棋盘模拟到评估函数
3.1 棋盘模拟:先把棋盘变成程序认得出的样子
任何棋类程序的第一层都是把真实棋盘翻译成内存里的结构。常见做法是用一维或二维数组,每个格子存一个编码,用正负号区分阵营、用数值区分兵种,空格存 0。中国象棋就是 9×10 的数组,国际象棋 8×8,围棋和六子棋 19×19,五子棋 15×15。这种表示最直观,走一步只需要改两个格子的值,撤回一步也只需要改回来。
更讲究的实现会用位棋盘,把整张棋盘压进几个整数里,用位运算一次性算出某一类棋子的所有可达位置。这种做法在评估和走法生成上快得多,但可读性差,适合已经跑通逻辑之后再做优化。
棋盘模拟这一层还负责两件事:走法生成和合法性校验。走法生成是"当前局面下我方所有合法着法",合法性校验负责过滤掉送将、长将违规、禁手这些情况。这两块写错,后面搜索再漂亮也没用——这是很多初学者的第一道坎,血泪经验是:先写一堆单元测试把走法生成测干净,再动搜索。
3.2 搜索和评估的分工:一个管想得远,一个管看得准
一台会下棋的程序,骨架基本是四件套:用户界面、棋盘模拟、搜索模块、评估函数。界面负责人机交互和局面显示,棋盘模拟负责规则和状态,搜索模块负责往前推演,评估函数负责在推演尽头给局面打分。
搜索和评估是一对互补关系,缺一不可。搜索负责"想得远",把未来若干步的可能走法展开成一棵树;评估负责"看得准",在搜索走不下去的深度上给出一个分数。如果评估很粗糙,搜得再深也只是把错误放大;如果搜索很浅,再准的评估也救不了短视。
这两者的取舍是机器博弈里最经典的工程问题。资源有限时,你要决定是把算力花在"多算两层"还是"把评估函数写细一点"。经验上,中等强度的棋类程序往往先优化评估,再优化搜索深度,因为评估错了,搜索只是更快地走到错误结论。
3.3 评估函数怎么写:子力值这类经验分
评估函数是整台程序里最像"经验"的部分。它不推演,只对当前局面打个分,正数表示我方占优,负数表示不利。
最朴素的做法是只算子力。国际象棋里有个流传很广的相对子力值参考:以兵为 1,马约 3,象约 3,车约 5,后约 9。这些数字不是天上掉下来的,而是长期对局统计出来的近似换算关系——一个车大约抵得上一个半马,一个后大约抵得上一个车加一个象。
# 极简评估函数:只算子力,正分代表红方占优 PIECE_VALUE = { "pawn": 100, "knight": 300, "bishop": 320, "rook": 500, "queen": 900, "king": 20000, } def evaluate(board): score = 0 for piece, side in board.pieces(): # side 取 +1 表示红方、-1 表示黑方 score += side * PIECE_VALUE[piece] return score这段代码的逻辑很直白:遍历盘面上所有棋子,按兵种查表取分,红方加分、黑方减分,最后得到一个正负分。参数说明上,PIECE_VALUE里的数字是相对刻度,不要求绝对精确,只要保持比例关系即可;王给到两万分,是因为王的丢失等于终局,必须让任何子力交换都无法弥补。
但只算子力的评估函数有明显短板:它看不出位置好坏。一个待在角落的马和一个占据中心格的马,子力分一样,实战价值差很多。所以真实程序会再加一层位置分,比如给每个兵种维护一张位置价值表,棋子站到好位置额外加分。中国象棋里还要考虑兵过河、车占肋道这些细节,围棋里则要评估影响力和连接形状。
4. 博弈树怎么展开:搜索为什么是机器博弈的方法学内核
4.1 博弈树:把未来的所有可能摊成一棵树
博弈树是把"未来"摊开的一种画法。树根是当前局面,它的子节点是我方所有合法走法形成的局面,再往下一层是对手所有合法应对,如此交替延伸,直到某一方获胜或达到设定的深度。
树的规模由两个量决定:深度和分支因子。中国象棋平均每步大约有几十种合法走法,假设每层 40 个分支,只看 6 层就是 40 的 6 次方,约 40 亿个节点。这个数量级已经超出普通机器的暴力枚举能力。围棋更夸张,19×19 的盘面每步有几百个候选点,所以它长期被认为是机器博弈里最硬的骨头,研究中常先缩到 9×9 的四分之一盘面来积累方法。
理解这种爆炸式增长,是理解整套方法学的起点:既然穷举不可能,就必须想办法"少算一些而不算错"。
4.2 极小极大:假设对手一定挑最坑你的那步
最基础的搜索框架叫极小极大。逻辑很符合直觉:轮到我走时,我在所有子节点里挑分数最高的;轮到对手走时,我假设他会在所有子节点里挑分数最低的——因为他挑得越低,对我越不利。
这个假设看起来悲观,其实是理性的:你不是在跟一个会失误的对手下棋,而是在评估"如果对方每一步都走最优,我还能拿到多少分"。在这种假设下选出的走法,最坏情况下的损失最小。
把评估函数和极小极大接起来,就得到一段能跑的搜索代码。下面用的是负极大值写法,把"取最大"和"取最小"合并成一个方向,代码短一半:
def alphabeta(board, depth, alpha, beta, side): # 到达终局或达到深度上限:直接返回评估分(换成当前行棋方视角) if board.is_game_over() or depth == 0: return side * evaluate(board) best = -float("inf") for move in board.legal_moves(): board.make(move) # 试走一步 score = -alphabeta(board, depth - 1, -beta, -alpha, -side) # 换对手行棋,分数取反 board.undo(move) # 撤回,恢复局面 best = max(best, score) alpha = max(alpha, score) if alpha >= beta: # 对手不会让我走到这个分支 break return best参数说明:depth是还能往下搜几层,减到 0 就调用评估函数;alpha是我方目前能确保的最好分数,beta是对手能压低到的最好分数;side表示当前行棋方,用于把分数统一到同一视角。make和undo必须成对出现,这是递归搜索里最容易翻车的地方——漏掉一次 undo,盘面就会被逐步污染,搜索结果的错误极难定位。
4.3 alpha-beta 剪枝:为什么能砍掉一大半还不影响结果
上面那段代码里的if alpha >= beta: break就是 alpha-beta 剪枝的全部秘密。它的道理可以用一句话概括:如果对手在某个分支上已经有更好的选择,他就不会走到这个分支来,那这个分支后面的内容算了也白算。
举个具体场景。我方在比较两个走法 A 和 B。试完 A 之后,已知 A 至少能保证 30 分。现在试 B,B 的对手有一手很凶的应对,能把我方压到 10 分——这时已经没有必要继续算 B 的其他变化了,因为不管剩下那些变化多好,对手都不会选择它们,B 的上限就是 10,比 A 的 30 差。整枝剪掉。
剪枝不改变最终答案,只减少计算量。在走法排序理想的情况下,同样的时间大约能多搜一倍的深度,这个提升是实打实的。所以真实程序都会在搜索前对走法排序,先试吃子、先试上次搜索里表现好的走法,排序越准,剪得越狠。
剪枝也有前提:它要求评估函数和搜索过程是确定性的,不能有随机成分;同时剪枝的正确性依赖"对手永远选最优"这个假设。如果引入随机开局库或非确定性的评估,剪枝的边界条件要重新检查。
4.4 剪枝救不了的地方:地平线效应和越算越蠢
搜索深度不是越深越好,这是初学者最容易踩的坑。原因叫地平线效应:程序在深度上限处被迫停手,用评估函数给局面打分,而那个局面可能正处在一次交换的中途——比如我刚吃掉对方一个车,还没被反吃回来。评估函数看到的是"我多一个车",于是给出高分,程序高高兴兴选了这条路,实战里下一步就被人吃回去。
这类问题的根源不是搜索错了,而是"看得见的边界"和"真实的终局"对不上。缓解办法有几种:在叶子节点之前多做一层静态搜索,专门把吃子交换算到底;把评估函数写得平滑一些,让局面的过渡不那么剧烈;或者在搜索中检测重复局面,避免在循环里打转。
另一个反直觉的现象是奇数层和偶数层效果差异。因为搜索是双方交替的,某些战术在不同深度下的评价会来回摆动,"多搜一层反而变弱"的情况确实存在。遇到这种波动,先别怀疑代码写错了,多半是评估函数在某个特定局面下有系统性偏差。
5. 几个最容易搞混的地方:棋类博弈和你想的不是一回事
5.1 误区一:计算机博弈就是深度学习,搜索已经过时了
这是当下最普遍的混淆。因为新闻里下棋的程序总跟神经网络绑在一起,很多人以为传统搜索是上一代技术。
为什么会混:两类内容经常出现在同一篇报道里,而且都叫"AI 下棋",外行看不出层次差别。
怎么区分:看问题的重心。基于搜索的方法关注的是"在有限时间里如何高效地展开博弈树",alpha-beta 剪枝、走法排序、置换表都属于这一脉;基于学习的方法关注的是"如何从大量对局里自动获得评估能力"。前者的结论是可解释的、可验证的,后者能给出人类说不清理由的直觉判断。两者不是替代关系——在同一个程序里,神经网络常常只是替换掉了原来手工编写的评估函数,搜索框架依然在下面跑。搞竞赛辅导,先把搜索这条主干吃透,再看学习方法是叠加还是替换,顺序不能反。
5.2 误区二:规则越简单,程序就越好写
直觉上,五子棋规则比围棋简单得多,程序应该容易得多。实际情况要分两层看。
为什么会混:把"规则描述的复杂度"和"程序实现的复杂度"当成同一件事。
怎么区分:规则简单只说明走法生成和胜负判定好写,不代表搜索空间小。围棋规则一句话能说完,但每步候选点极多、局面评估极其模糊,所以难度公认最大。反过来,某些规则繁琐的小众棋,走法类型有限、评估维度单一,反而容易做出不错的程序。判断一个棋种好不好做,要同时看三件事:分支因子有多大、评估函数能不能写得准、有没有成熟的参考资料。
5.3 误区三:换个棋种,只要把棋盘尺寸改一下
这条误区在动手阶段杀伤力最大。很多人写完一个五子棋程序,觉得改成象棋只需要换掉棋盘大小和胜负条件。
为什么会混:表面上看,都是"轮流落子、连成线或吃掉对方"的框架,长得像。
怎么区分:回到着法分类上看。走子类棋有子力概念,棋子本身携带价值,一步棋能直接改变盘面分数;填子类棋盘面从空到满,落下的子当下不产生收益,价值延后体现;混合类棋前期填子后期走子,评估策略要分阶段切换。这三类的评估函数几乎是两套东西:一个以子力为主,一个以形状和势力为主。更别说特殊规则——王车易位、兵升变、吃子必须经过完整弧线、每次移动多个棋子但必须停在同一位置,这些都会渗透到走法生成里。换棋种不是换参数,是换模型。
5.4 误区四:先手优势是细节,不影响大局
原料里提到,棋类的公平性有个例外——先手优势。很多人把它当成可以忽略的小事。
为什么会混:业余对局里水平差距远大于先手差距,感觉不出来。
怎么区分:看竞技规则怎么处理它。围棋用贴目来补偿后手;五子棋在无禁手规则下先手优势大到影响竞技性,于是发展出禁手、换手等限制;六子棋干脆改成先手只下一子、之后每手两子,用"给后手多一颗子"来削平优势。这些设计说明,先手优势是规则层面必须正面处理的问题。落到程序上,执先和执后的策略往往要分别调,同一个评估函数在两种颜色下表现不一致是常见现象。
5.5 误区五:搜索越深,棋力一定越强
前面讲过地平线效应,这里再强调一次它的实践含义。
为什么会混:深度是最容易看见、最容易调的参数,加一层就感觉变强了。
怎么区分:看提升来自哪里。如果评估函数粗糙,加深只是把误差传播得更远;如果走法排序很差,剪枝效率低,加深的代价是时间成倍增长却换不来有效信息。判断方法很朴素:在同一批测试局面上对比不同深度下的选点,如果深层选点和浅层选点经常互相矛盾且没有明显优劣,说明问题出在评估而不是深度。这时候该做的是补位置分、加静态搜索、检查终局判定,而不是继续堆层数。
6. 什么时候用得上这份资料:从入门选棋到竞赛备赛的边界
讲清边界比讲清原理更实用。这份资料覆盖面很宽,从棋类介绍到原理、软件构成、棋局评估、博弈树展开都有,但它不是万能的,也不适合所有人。
它适合的场景很明确:你准备参加计算机博弈类竞赛,需要一个从"棋是什么"到"程序怎么写"的整体框架;你在做课程设计或毕业设计,想找一个规则清晰、可验证、有评测标准的题目;你教别人编程,想用棋盘这种直观载体讲清搜索与评估的分工。这几种情况下,先把棋类分类、走法类型、完全信息这一串概念捋顺,再动手写代码,能省掉大量返工。
它不适合的场景同样明确:想直接拿现成代码交差的人,会失望,因为资料的重心在原理与方法学,不在某个棋种的完整实现;想深挖某个具体算法数学证明的人,也会觉得不够,因为这是概论层面的梳理。另外,如果你的目标棋种是带随机发牌或不完全信息的游戏,那第 2 章那套完全信息的前提假设就要重新审视,不能照搬。
| 你的处境 | 建议从哪里入手 | 需要绕开的地方 |
|---|---|---|
| 完全没接触过机器博弈 | 先看棋类介绍与分类,挑一个走子类小棋种练手 | 别一上来选围棋,评估函数写不出来会卡死 |
| 会写代码但不懂搜索 | 直接看博弈树与评估函数,先把极小极大跑通 | 别急着加剪枝,先确认基础搜索的结果正确 |
| 参加过比赛但成绩一般 | 重点看评估函数与走法排序,对比不同深度下的选点 | 别盲目加深,先排查地平线效应 |
| 想换一个棋种重做 | 先按着法分类判断新棋种属于走子、填子还是混合 | 别以为改个棋盘尺寸就能复用 |
我自己的习惯是:每次开一个新棋种之前,先逼自己用三句话写清楚它的分类归属、胜负条件、评估维度,写不出来就说明还没读懂规则,这时候写代码一定翻车。这套动作我坚持了很多年,比任何调试技巧都省时间。希望帮到你。
本文还有配套的精品资源,点击获取