题目重点:Onsite 开放题要求设计 Checkers 游戏后端,作者写到初始化、移动和胜负,吃子未完成;先固定规则变体再实现状态机。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练版用 8×8 深色格、美式 checkers:双方各 12 子,普通子朝前斜走 1 格,王可双向斜走 1 格;可跨一枚对方子跳到空格并移除被跳子。
- 训练版强制吃子,吃子后若还能继续吃则同一回合继续;升王在吃子链结束时生效。不同地区规则可能不同,面试先确认。
- 单局命令按 version 串行应用,服务端为规则权威;客户端只提交 (gameId,player,from,to,expectedVersion) 而不直接改棋盘。
解题路线
- 状态包含 8×8 棋盘、当前玩家、棋子类别、是否必须继续同一子吃、回合号、结果和版本。初始化只在可用深色格放棋,核对双方子数。
- 集中生成 legalMoves(state,player),先枚举可吃路径;若有强制吃子,过滤普通移动。applyMove 只接受生成器给出的合法项,避免移动函数和胜负函数各自复制规则。
- 吃子验证跨越中间恰一敌子、落点为空,移除敌子;若当前子可继续吃则保持回合并限制同一子,否则处理升王和换手。
- 胜负:对手无棋子,或轮到对手时无合法移动;平局/重复局面/时钟要另定规则,不能暗中宣称已有。所有状态转移使用 expectedVersion 防重复和并发覆盖,记录 move event 供回放。
- 优化追问先指出正确性瓶颈:重复扫描全盘生成 moves 可为每方维护棋子位置索引,但缓存合法走法会因吃子与升王失效;小棋盘先求清晰正确,再按性能数据优化。
手动走一遍
训练例:普通子在 (5,2),对方子在 (4,3),(3,4) 为空,则可以跳到 (3,4) 并移除 (4,3);若新位置还能跳另一敌子且规则要求连续吃,仍由同一玩家同一子继续。若普通走法可用但有另一颗子能吃,强制吃子版本必须拒绝普通走法。
规模与取舍
8×8 固定棋盘一次扫描 O(64),单个普通移动/跳验证 O(1);枚举合法走法 O(P) 乘每子常数方向,P≤24;连续吃路径枚举在变体规则下可能分支增长,须限制棋盘状态并验证无重复落点。每局状态 O(64),历史 O(回合数)。
易错点与追问
容易失分的边界
- 把 Checkers 当国际象棋,实现了错误棋子移动。
- 不澄清强制吃子/升王时机就混用规则。
- 只在对方棋子清零时判胜,忽略对手无合法走法。
- 客户端直接提交新棋盘,跳过服务端合法性校验。
- 并发两个 move 都基于同一旧棋盘,未用版本拒绝后到写。
继续深挖
- 如何支持不同 checkers 规则版本而不把 if 散在代码里?
- 断线重连怎样从 move log 恢复?
- 双人同时提交动作时如何序列化?
- 平局和计时规则如何添加?
