无猜扫雷:怎么保证每一局都能靠推理走完

扫雷最烦的不是踩雷,是踩到一颗只能靠运气的雷。这篇讲生成器怎么用「先解一遍再发牌」保证整局可推理,以及为什么这件事比看起来贵。

扫雷输掉的原因有两种,性质完全不同:

  • 看漏了。这是我的问题,重开一局就好。
  • 必须猜。局面到了某一步,所有确定推理都用完了,剩下的只能掷骰子。

第二种让人不想再玩。所以「无猜」不是一个难度选项,它是这个游戏能不能被当成技术游戏来玩的前提。

无猜的判据

定义要精确,否则实现会跑偏:

一局是无猜的,当且仅当存在一条从开局到通关的路径,路径上每一步都只使用确定推理。

注意是「存在一条」,不是「所有分支都不需要猜」。后者太强,会筛掉大量本来很好的局面。

还有一条更隐蔽的约束:求解器只能使用玩家能看到的信息。如果它偷看答案来判断「这格是雷」,那生成出来的局面是「上帝视角可解、玩家不可解」—— 看起来满足条件,实际毫无价值。

最直白的做法:生成、求解、不行就重来

loop:
  随机布雷
  挖开一个安全起点
  if 求解器能推完: 返回这一局
  else: 重来

这就是拒绝采样。它正确、简单,而且在小棋盘上完全够用 —— 低雷密度时第一次就通过的概率很高。

代价是它的尾部很难看。棋盘一大、雷密度一高,命中率会掉得很快,偶尔要重试几十上百次。如果布雷本身是 O(n),最坏情况就是 O(n × 重试次数),而重试次数没有上界。

更省的做法:边生成边修

与其整局重来,不如把「必须猜」的那个位置直接改掉:

  1. 生成一局,求解
  2. 求解卡住时,找出当前所有未知格里无法被确定的那些
  3. 从里面挑一个,把雷挪走或挖开(改完之后它周围的开局信息也变了)
  4. 重新求解;反复几次仍不行,才整局重来

改一格通常比重新生成一整局便宜得多。实践中大多数“卡住”只需要挪一两颗雷。

求解器要覆盖到哪一层

这是整件事里唯一需要拿捏的地方。确定推理有三个层级:

层级 手法 覆盖
一 直接数数:某格周围剩余雷数等于未知邻居数 → 全是雷;等于 0 → 全安全 大部分
二 子集差集:A 的未知集是 B 的子集 → 差集里全是雷或全安全 一大批
三 局部枚举:把边界上的未知格枚举到一定规模,找在所有解里取值相同的格 剩下的多数

第二层是「1-2-1」「1-2-2-1」这些经典模式的通用化 —— 那些模式不是规则,是这一层的特例。

第三层是分界线。它在数学上仍然是确定性的(某格在所有解里都是雷,那它必然是雷),但计算量上去了,而且“枚举规模”是个需要调的旋钮:放得太松,生成变慢;收得太紧,又会有局面被判成“需要猜”。

我的取舍是算到第二层 + 小规模枚举。理由是前两层覆盖了绝大多数局面,枚举吃掉剩下的绝大部分,而再往上投入的算力换来的通过率提升非常小。

开局第一步

一个容易被忽略但很关键的点:第一步必须安全。

标准做法是首次点击之后再布雷,或者先把首点及其八邻域挖空。它一次解决两件事:

  • 玩家不会在第一步就死 —— 这纯粹是体验问题
  • 开局立刻有一片数字,也就是立刻有信息。否则第一手是纯随机的,无猜的前提从一开始就不成立

无猜到底改变了什么

它没有让游戏变简单,难度仍然由棋盘大小和雷数决定。

它改变的是随机性作用的位置:从「运气决定胜负」移到「局面形状」。玩家仍然会输,但输在“这里我没看出来”,而不是“这里谁也看不出来”。

对我来说这才是关键 —— 一个游戏值不值得反复玩,取决于失败能不能被归因到自己的判断上。

← 返回文章列表

评论

…