井字棋 · 一个被完全解出的游戏
井字棋 (tic-tac-toe / 圈叉 / OX) —— 3×3 棋盘, X 先手, 谁先把自己的记号连成横 / 竖 / 斜任意一条线即胜, 填满未连成则平局。规则简单到几乎人人会玩, 它却是博弈论里少数被「完全解出」(solved game) 的例子:整棵博弈树小到可以穷举, 于是「双方都不犯错会怎样」有确定答案 —— 必然平局。
本系列把这件事拆开看:先上手玩一局 (本地双人 / 对战完美 AI);再看 minimax 如何向前看到底给每一步定胜负;为什么对着完美一方再也赢不了;不输的下法(中心 / 角 / 双威胁 fork);整个游戏的状态空间到底有多大;最后用 WebRTC 搭一个无需自建后端也能两台设备真对战的联机版。与 alpha-beta 剪枝一节互补:那里讲「如何少搜博弈树」,这里讲「这棵树本身长什么样、能解出什么」。
上手玩:本地双人 / 对战完美 AI
系列的入口体验。一个能下的棋盘,本地双人轮流落子或对战 minimax 驱动的完美 AI,胜负即时判定并高亮连成的那条线。
AI 怎么想:把每一步都算到终局
对每个合法落点假设此后双方都最优、一路递归到终局,给这步标上必胜 / 必和 / 必负。分值还编码了最快几手分胜负。
为什么再也赢不了
solved game 的含义——从空盘起,九种开局在双方最优下全部通向平局。本页摆出九个开局的博弈论结果,并留一局给挑战方亲手验证。
不输的下法:威胁、防守与双威胁
一套八条的优先级清单就能保证不败,无需展开整棵博弈树。清单的关键一条是 fork——一步同时凑出两条将成之线,对手只能堵一条。
这棋到底有多大:数一数
只是填满九格的排列上界。真正有意义的量在浏览器里现算:可达局面 5478、完整对局 255168、终局 958,对称归约后压到 765。
联机对战:无后端的 P2P 实现
用 WebRTC 在两个浏览器间直建 RTCDataChannel 传落子,信令降级为手动复制粘贴一段编码,于是连信令服务器都不需要。
延伸阅读
- Tic-tac-toe — Wikipedia en.wikipedia.org 规则、组合学计数 (255168 局 / 765 个对称归约局面) 与「双方最优必和」的标准结论。
- Solved game — Wikipedia en.wikipedia.org ultra-weak / weak / strong 三档「解出」的定义, 以及各类游戏 (井字棋 / 西洋跳棋 / 四子棋…) 的解出现状。
- Minimax — Wikipedia en.wikipedia.org 双人零和博弈的极小化极大决策, 本系列完美 AI 的算法基础;配合 alpha-beta 一节看「如何少搜」。
- RTCDataChannel — MDN developer.mozilla.org 浏览器间点对点数据通道的标准 API;联机页用它在两个 peer 之间直接传落子, 无需经过服务器中转。
- WebRTC signaling — MDN developer.mozilla.org offer / answer / ICE 信令交换的完整流程;本系列把信令通道替换成「手动复制粘贴」, 因而无需自建信令服务器。