数独生成 - 谜题自动生成算法的设计思想
优质数独谜题的自动生成不是简单地随机放置数字就能实现的。本文讲解唯一解的保证、难度控制、美观提示分布的实现方法。
生成的 3 个步骤
数独谜题的生成分 3 个步骤:(1) 随机生成有效的完成盘面。(2) 逐个移除格子中的数字,验证移除后仍保持唯一解。(3) 分析移除后谜题的难度,确认是否符合目标难度。步骤 2 和 3 是决定品质的核心部分,计算成本的大部分集中于此。
唯一解的保证
作为谜题成立的前提是解必须唯一。每次移除格子后,都需要验证剩余谜题是否拥有唯一解。验证方法是使用求解器计算解的数量。一旦发现 2 个以上的解,即判定为「非唯一解」,撤销该格子的移除。这种验证计算成本较高,但对保证谜题品质不可或缺。
难度的控制
难度通过解题所需技巧的复杂程度来定义。对生成的谜题模拟人类解法,分析需要哪些技巧。仅用显性单数就能解出则为 Easy,需要隐性单数则为 Medium,需要数对则为 Hard,需要 X-Wing 则为 Master。如果与目标难度不符,则丢弃谜题重新生成。
种子值实现可重现性
要实现「所有用户解同一道谜题」的每日挑战功能,需要给随机数生成器提供种子值以确保可重现性。从日期计算种子值,相同种子始终生成相同谜题。这样无需在服务器保存谜题,客户端即可重现相同谜题。
挖空顺序与对称性
从完整盘面挖去格子时,挖空的顺序与对称性会同时影响所生成谜题的质量与外观。最简单的做法是按随机顺序逐个挖去,并在某格挖去后破坏唯一性时将其还原,但这往往导致提示分布不规则。许多谜题会以中心对称的方式同时挖去成对的两格,从而获得传统数独特有的优美布局。由于挖空越深,唯一性检验的成本越高,通过精心安排挖空候选的顺序以增加提前退出,可加快生成。当再也无法挖去任何格子时,盘面便接近该谜题的最少提示配置。
为何难度评估如此困难
生成中最棘手的是难度评估。难度通常以「所需技巧的最高难度」来定义,但同一谜题的体感难度会随解题者的路径而变化。而且这一刻度并非线性:仅把所需技巧提高一级,体感难度就可能骤增。实现上会用模拟人类解法的求解器,依次判断谜题能否仅凭显性唯一数解出、是否需要隐性唯一数等,并在首次卡住的阶段确定难度。然而这一判断取决于求解器所实现的技巧范围,因此若缺少某种技巧,可能高估难度。要让难度标注保持一致,求解器对技巧的覆盖必须足够全面。
加速唯一性验证
好题的条件与重新生成
生成器应当追求的,不只是拥有唯一解,而是解起来令人愉快的好题。好题的条件包括:能仅凭逻辑解到最后、不强迫漫无目的的试错,以及所标注的难度与实际手感相符。为满足这些条件,生成之后会用求解器重演解题路径,以验证是否存在需要猜测的环节、能否用预期的技巧解出。不满足条件的谜题会被丢弃并重新生成,因此目标难度越高,生成所需的尝试次数越多。相比量产,更重视逐题质量的设计,最终会提升游玩者的满意度。仅以提示数量来判定难度是一种误解:有些提示少的题目却很顺畅,有些提示多的题目反而需要高级技巧。