改进我的扫雷求解算法

14
我用Python实现了一个求解扫雷游戏的算法。程序的工作方式如下:
假设求解器点击了一个名为“a”的方格。例如,所揭示数字为2。该方格周围未点击的邻居方格(同样以示例说明)命名为“b”和“c”。然后程序将该方格与表达式[2,{'b','c'}]相关联,并从所有其他表达式中删除“a”。通过两种情况下的这种表达式成对简化来推断哪些方格是地雷,哪些不是地雷。
  • 如果一个表达式中的方格是另一个表达式中的方格的子集:

    [2, {'a', 'b', 'c'}], [1, {'a', 'b'}] -> [1, {'c'}], [1, {'a', 'b'}]
    
  • 如果一个表达式中的所有方块都被标记为地雷:

  • [2, {'a', 'b'}], [1, {'b', 'c'}] -> [2, {'a', 'b'}], [0, {'c'}]
    
    然后,对于一些表达式X,如果X [0] == 0,我们可以自由地单击所有名为X [1]的方格,如果X [0] == len(X [1]),则我们可以标记它们。
    但是,我在努力确定要尝试简化哪些表达式。我的当前方法是维护一个方块堆栈;每当单击一个方块或其表达式成功简化时,将其添加到堆栈中(如果它还没有在那里)。从堆栈中弹出方块时,会尝试简化其表达式(X),并将其与任何其他表达式Y进行比较,使得X [1]&Y [1]!= set()。算法在堆栈耗尽时终止。然而,目前虽然这种方法运行良好,但无法正确解决所有明确的配置,而且如果我用队列替换堆栈,或使用某种算法确定弹出哪个方块,则其在给定棋盘上的性能变化显著!
    非常感谢您提供任何类似于我的方法的先例或潜在探索途径。

a、b、c是指潜在的地雷,还是只是相邻的方块,这样你就可以从每个8个相邻方块开始,并逐渐确定哪些是安全的,哪些是危险的? - Alex Nye
你从未点击的邻居中的每一个开始(如第二段所解释的),生成表达式时引用它们。 - user1502040
2个回答

7
几年前,我写过一个扫雷求解器,但是不幸的是,自那时以来我好像丢失了代码。我记得它是一种暴力方法,编译了潜在地雷的集合,而不是像你现在做的那样将组合打包起来。
我认为它比你现在使用的算法更具有能力。你的方法可以“解决”一个完全填满或空的地雷情况,或者如果它是另一个情况的子集。然而,有些推断这种方法无法处理。例如,考虑这个小的7x2棋盘,其中的a到h瓷砖是未知的:
a 2 1 2 1 2 i
b c d e f g h 

您的条件将是:

[2, {a, b, c, d}],
[1, {c, d, e}],
[2, {d, e, f}],
[1, {e, f, g}],
[2, {f, g, h, i}]

如果我理解正确的话,你的算法对此无法做出任何推断。但是,如果你是一个经验丰富的扫雷玩家,你会认识到中心的“1 2 1”模式只有一个解决方案,地雷位于“1”的下方。
a 2 1 2 1 2 i
b 2 * 2 * 2 h

仍有一些未知因素,其中一个矿井位于ab之下,另一个位于hi之下,但如果这是一个更大的谜题的一部分,您可能稍后可以解决这些问题(或者您可能需要猜测)。
我认为我的矿井集合方法是这样工作的:
对于每个已扩展的方块,收集其所有未扩展的邻居(即“区域”)的一个集合,并列出该区域中可能出现的所有矿井集合。因此,例如上面示例中的5个已知方块将生成以下内容(从左到右):
 ({a, b, c, d}, [{a, b}, {a, c}, {a, d}, {b, c}, {b, d}, {c, d}])
 ({c, d, e}, [{c}, {d}, {e}])
 ({d, e, f}, [{d, e}, {d, f}, {e, f}])
 ({e, f, g}, [{e}, {f}, {g}])
 ({f, g, h, i}, [{f, g}, {f, h}, {f, i}, {g, h}, {g, i}, {h, i}])

无论如何,要结合两个条件,我首先会检查它们是否重叠,通过交集面积来确定。如果没有重叠,这些条件就不能有用地结合。
如果有重叠,则新的条件将跨越它们区域的并集。至于矿井集合,我会对外部集合进行笛卡尔积,以获得内部集合的一对,然后检查是否存在矛盾。如果在区域交集中,两个集合没有完全相同的矿井,则存在矛盾。如果没有矛盾,则从矿井位置的并集中形成一个新的组合集合。以下是前两行的组合方式:
 Intersection of areas: {a, b, c, d} & {c, d, e} = {c, d}
 New combined area: {a, b, c, d} | {c, d, e} = {a, b, c, d, e}
 Cartesian product of mine sets (with X marking contradictions):
    |   {a, b}  {a, c}  {a, d}  {b, c}  {b, d}  {c, d}
 ---+-------------------------------------------------
 {c}|     X     {a, c}    X     {b, c}    X       X
 {d}|     X       X     {a, d}    X     {b, d}    X
 {e}| {a, b, e}   X       X       X       X       X

 New condition: ({a, b, c, d, e}, [{a, b, e}, {a, c}, {b, c}, {a, d}, {b, d}])

您可以通过简单地计算在条件区域内任何方块是地雷的概率来确定其所属集合数量与总集合数量之比。因此,对于上述组合条件,您可以计算出 a 是地雷的概率为 3/5,而 e 只有 1/5 的概率。当程序需要猜测扩展位置时,这些信息非常重要,因为没有任何保证安全的方块可供选择。我还进行了一些复杂的组合数学计算,以考虑使用的地雷数量(因此,上述 {a,b,e} 情况将与其他情况略有不同,因为它使用三个地雷而不是两个),但我恐怕记不清具体细节了。
扫雷是一个非常具有挑战性的游戏。我相信我的程序能够解决与“困难”难度等价的棋盘约50-60%的时间,大多数失败都发生在开始时(当你必须在很少的信息基础上猜测)或最后(当通常有一些无法解决的区域需要猜测)。它通常非常快,但偶尔会出现一些方块的模式,导致它在进行下一步移动之前陷入10到15秒的困境。(扫雷是NP完全问题,因此某些输入无法快速解决,这并不奇怪!)

1

这是我想到的,我没有完全能够想象出您的方法是什么。
我希望以图形方式呈现我的方法将有助于为您节省这种努力。

图像按“阅读顺序”进行。

看起来与我发布此后所做的工作相符,将未知瓦片获得的价值加上它从已知瓦片中获得的临时价值数量,可以进一步增加正确建模风险的可能性。
(使用此方法,16的临时值(或使用第一种方法的8)是显著的,因为这是地雷本身可以达到的最高值)


我感觉有点糊涂,没有早点看到这个。

任何归一化值为100%的东西,在我能找到的所有情况下,都是一个陷阱。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接