井字棋 · 一个被完全解出的游戏
井字棋 (tic-tac-toe / 圈叉 / OX) —— 3×3 棋盘, X 先手, 谁先把自己的记号连成横 / 竖 / 斜任意一条线即胜, 填满未连成则平局。规则简单到几乎人人会玩, 它却是博弈论里少数被「完全解出」(solved game) 的例子:整棵博弈树小到可以穷举, 于是「双方都不犯错会怎样」有确定答案 —— 必然平局。
本系列把这件事拆开看:先上手玩一局 (本地双人 / 对战完美 AI);再看 minimax 如何向前看到底给每一步定胜负;为什么对着完美一方再也赢不了;不输的下法(中心 / 角 / 双威胁 fork);整个游戏的状态空间到底有多大;最后用 WebRTC 搭一个无需自建后端也能两台设备真对战的联机版。与 alpha-beta 剪枝一节互补:那里讲「如何少搜博弈树」,这里讲「这棵树本身长什么样、能解出什么」。
上手玩:本地双人 / 对战完美 AI
系列的入口体验。一个能下的棋盘:本地双人轮流落子,或对战完美 AI(可选先 / 后手)。AI 由 minimax 驱动,永不犯错——你最好的结果是平局。胜负即时判定并高亮连成的那条线。
AI 怎么想:把每一步都算到终局
完美一方不靠经验,靠穷举:对每个合法落点,假设此后双方都最优,一路递归到终局,给这步标上「必胜 / 必和 / 必负」。任意摆一个局面,看每格被标注的 minimax 分值与最优着法——分值还编码了「最快几步分胜负」。
为什么你再也赢不了
对完美对手发起挑战:无论怎么下,战绩只会是「平局或落败」,永远拿不到胜。这正是 solved game 的含义——从空盘起,每一种开局在双方最优下都通向平局。本页把九个开局的博弈论结果一并摆出,并让你亲手验证「赢不了」。
不输的下法:威胁、防守与双威胁
不必背完整棵树,一套优先级就够「不败」:能赢就赢 → 对方要赢就堵 → 造双威胁 fork(一步同时凑出两条将成之线,对方只能堵一条)→ 防对方的 fork → 抢中心 / 对角 / 角 / 边。逐条交互演示,重点看 fork 为何无解。
这棋到底有多大:数一数
9! = 362880 只是「填满九格」的排列上界。真正有意义的量在浏览器里现算:可达的不同局面 5478、完整对局 255168、终局 958(X 胜 626 / O 胜 316 / 和 16)。再用 8 个对称像把本质局面压到 765——旋转 / 镜像本是同一局。
联机对战:无后端的 P2P 实现
playground 是纯静态站点、没有后端,联机却仍可行:用 WebRTC 在两个浏览器间直接建 RTCDataChannel,落子通过它点对点传输。信令 (offer / answer) 不自建服务器,改为手动复制粘贴一段编码。含三种联机架构的取舍说明与一个真能跑的对战盘。
延伸阅读
- 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 信令交换的完整流程;本系列把信令通道替换成「手动复制粘贴」, 因而无需自建信令服务器。