Hashi谜题表示法,用于解决具有Prolog限制的所有解决方案

Hashi谜题表示法,用于解决具有Prolog限制的所有解决方案,prolog,puzzle,solver,Prolog,Puzzle,Solver,我正在尝试编写一个prolog程序,它接收未解决的Hashi板的表示,并使用限制回答所有可能的解决方案。我很难弄清楚哪一种是最好的,还是一种很好的方式来代表董事会,有桥和无桥。该程序应绘制图板,以便轻松阅读解决方案 board( [[3, 0, 6, 0, 0, 0, 6, 0, 3], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0,

我正在尝试编写一个prolog程序,它接收未解决的Hashi板的表示,并使用限制回答所有可能的解决方案。我很难弄清楚哪一种是最好的,还是一种很好的方式来代表董事会,有桥和无桥。该程序应绘制图板,以便轻松阅读解决方案

board(
       [[3, 0, 6, 0, 0, 0, 6, 0, 3],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 1, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [2, 0, 0, 0, 0, 1, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0, 0, 0, 0, 0],
        [1, 0, 3, 0, 0, 2, 0, 0, 0],
        [0, 3, 0, 0, 0, 0, 4, 0, 1]]
    ).
例如,这种表示只有在没有桥的情况下才是好的,因为它不包含关于桥的信息。该电路板的绘图基本上将0转换为空格,电路板的绘图方式如下:

3   6       6   3 

  1               

2         1       


1   3     2       
  3         4   1 
这是一个真正的hashi板的体面代表

现在的重点是能够画出同样的东西,但如果有桥的话也能画出来。我必须能够做到这一点,甚至在我考虑自己做出限制之前,因为用一种不好的表达方式去做会让我的工作更加困难

我开始思考这样的解决方案:

3   6       6   3 

  1               

2         1       


1   3     2       
  3         4   1 
如果董事会的每个元素都是一个列表:

[NumberOfConnections, [ListOfConnections]]
但这并没有给我任何关于这幅画的信息,连接列表到底有什么

也许是这样:

[Index, NumberOfConnections, [ListOfIndex]]
这样,每个岛都会有一个唯一的ID,连接列表也会有ID 但画图听起来还是有点难,最终桥梁只能是水平或垂直的


无论如何,任何人都可以想出一种更好的表示方法,使其最容易实现程序的最终目标。

对于绘制桥,可以使用ASCII 179表示单竖桥,186表示双竖桥,196表示单横桥,205表示双横桥。不过,这取决于使用哪个扩展ASCII集。它在最常见的情况下工作


对于内部表示,我将在一个方向使用-1和-2表示单桥和双桥,在另一个方向使用-3和-4表示。您可以使用几乎任何不是0-8的符号,但这还有一个额外的好处,即只需将桥添加到岛上,将-3,-4转换为-1,-2,以检查解决方案。如果总和为0,则该岛已求解。

对于绘制桥梁,可以使用ASCII 179表示单纵桥,186表示双纵桥,196表示单横桥,205表示双横桥。不过,这取决于使用哪个扩展ASCII集。它在最常见的情况下工作


对于内部表示,我将在一个方向使用-1和-2表示单桥和双桥,在另一个方向使用-3和-4表示。您可以使用几乎任何不是0-8的符号,但这还有一个额外的好处,即只需将桥添加到岛上,将-3,-4转换为-1,-2,以检查解决方案。如果总和为0,则该岛已解。

多么酷的一个谜!我自己做了一些,我没有看到一个明显的方法使解决它们具有确定性,这对于一个谜题来说是一个很好的特性。像俄罗斯方块这样的游戏,其持续的游戏价值很大程度上来源于这样一个事实:即使是一个好的策略也可以不断改进,你不会感到厌倦。这有一个实际的分支:如果我编写这个,我将不再花更多的时间试图找到一个确定性算法。我将转而关注Prolog擅长的生成/测试范式

如果您知道您将要进行生成和测试,那么您已经知道您在优化方面的所有努力将走向何方:使生成器更智能,以便生成更好的候选对象,并使测试更快。所以我在看你们的董事会代表,我在问我自己:从中产生替代方案是否容易且快速?我们都知道答案是否定的,原因有几个:

从任何特定的岛上查找要连接的替代岛将是非常低效的:向前和向后搜索列表,然后根据当前偏移量索引所有其他列表。这是一个巨大的列表欺骗,这将是不便宜的

检测和预防桥梁交叉将是一件有趣的事情

更重要的是,在这种设计中,对桥进行编码的正确方法并不明显。岛屿可以被很长的距离隔开。你打算在每个连接单元中放置0/1/2吗?如果是这样,则表示存在数据重复问题;如果没有,你将有一些有趣的计算哪个位置应该持有桥计数

这只是一种直觉,但对于这样的异构数据结构,元素的类型完全取决于索引是奇数还是偶数,这让我觉得不受欢迎

我认为你所拥有的board布局是一种很好的输入格式,但我不认为它能很好地作为一种中间表示。这个游戏显然是一个图形问题。这表明两种经典图形数据结构之一可能更有用:邻接列表或边矩阵。这两种方法都可以加快桥梁布局方案的选择,但这不是obvi
对我来说也许是对一个做更多图论的人来说如何防止过桥。理想情况下,您的数据结构可以简单地防止发生跨桥。下一个最佳方案是防止生成器生成具有桥梁交叉点的候选解决方案;最糟糕的情况是在测试阶段让他们不及格。

多么酷的难题!我自己做了一些,我没有看到一个明显的方法使解决它们具有确定性,这对于一个谜题来说是一个很好的特性。像俄罗斯方块这样的游戏,其持续的游戏价值很大程度上来源于这样一个事实:即使是一个好的策略也可以不断改进,你不会感到厌倦。这有一个实际的分支:如果我编写这个,我将不再花更多的时间试图找到一个确定性算法。我将转而关注Prolog擅长的生成/测试范式

如果您知道您将要进行生成和测试,那么您已经知道您在优化方面的所有努力将走向何方:使生成器更智能,以便生成更好的候选对象,并使测试更快。所以我在看你们的董事会代表,我在问我自己:从中产生替代方案是否容易且快速?我们都知道答案是否定的,原因有几个:

从任何特定的岛上查找要连接的替代岛将是非常低效的:向前和向后搜索列表,然后根据当前偏移量索引所有其他列表。这是一个巨大的列表欺骗,这将是不便宜的

检测和预防桥梁交叉将是一件有趣的事情

更重要的是,在这种设计中,对桥进行编码的正确方法并不明显。岛屿可以被很长的距离隔开。你打算在每个连接单元中放置0/1/2吗?如果是这样,则表示存在数据重复问题;如果没有,你将有一些有趣的计算哪个位置应该持有桥计数

这只是一种直觉,但对于这样的异构数据结构,元素的类型完全取决于索引是奇数还是偶数,这让我觉得不受欢迎


我认为你所拥有的board布局是一种很好的输入格式,但我不认为它能很好地作为一种中间表示。这个游戏显然是一个图形问题。这表明两种经典图形数据结构之一可能更有用:邻接列表或边矩阵。这两种方法中的任何一种都可以加快桥梁布局方案的选择,但对于我来说,也许对于那些从事图论研究的人来说,这并不明显,因为他们知道如何防止桥梁交叉。理想情况下,您的数据结构可以简单地防止发生跨桥。下一个最佳方案是防止生成器生成具有桥梁交叉点的候选解决方案;最糟糕的情况是在测试阶段让他们不及格。

我同意这是一个很好的难题。这里是ECLiPSe中的一个中间解决方案,ECLiPSe是一种带有约束的Prolog方言

其思想是,对于电路板的每个字段,有四个变量N、E、S、W表示北、东等,它们的值为0..2,并表示该字段边缘上的连接数。对于节点字段,这些连接的总和必须达到给定的数目。对于空字段,连接必须经过N=S、E=W,而不是交叉N=S=0或E=W=0

您的示例正确地解决了以下问题:

?- hashi(stackoverflow).
3 = 6 = = = 6 = 3 
|   X       X   | 
| 1 X       X   | 
| | X       X   | 
2 | X     1 X   | 
| | X     | X   | 
| | X     | X   | 
1 | 3 - - 2 X   | 
  3 = = = = 4   1 
但是WikipediaOne没有,因为还没有连接约束

:- lib(ic).  % uses the integer constraint library

hashi(Name) :-
        board(Name, Board),
        dim(Board, [Imax,Jmax]),
        dim(NESW, [Imax,Jmax,4]),   % 4 variables N,E,S,W for each field
        ( foreachindex([I,J],Board), param(Board,NESW,Imax,Jmax) do
            Sum is Board[I,J],
            N is NESW[I,J,1],
            E is NESW[I,J,2],
            S is NESW[I,J,3],
            W is NESW[I,J,4],
            ( I > 1    -> N #= NESW[I-1,J,3] ; N = 0 ),
            ( I < Imax -> S #= NESW[I+1,J,1] ; S = 0 ),
            ( J > 1    -> W #= NESW[I,J-1,2] ; W = 0 ),
            ( J < Jmax -> E #= NESW[I,J+1,4] ; E = 0 ),
            ( Sum > 0 ->
                [N,E,S,W] #:: 0..2,
                N+E+S+W #= Sum
            ;
                N = S, E = W,
                (N #= 0) or (E #= 0)
            )
        ),

        % find a solution
        labeling(NESW),
        print_board(Board, NESW).


print_board(Board, NESW) :-
        ( foreachindex([I,J],Board), param(Board,NESW) do
            ( J > 1 -> true ; nl ),
            Sum is Board[I,J],
            ( Sum > 0 ->
                write(Sum)
            ; 
                NS is NESW[I,J,1],
                EW is NESW[I,J,2],
                symbol(NS, EW, Char),
                write(Char)
            ),
            write(' ')
        ),
        nl.

symbol(0, 0, ' ').
symbol(0, 1, '-').
symbol(0, 2, '=').
symbol(1, 0, '|').
symbol(2, 0, 'X').


% Examples

board(stackoverflow,
     []([](3, 0, 6, 0, 0, 0, 6, 0, 3),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0),
        [](0, 1, 0, 0, 0, 0, 0, 0, 0),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0),
        [](2, 0, 0, 0, 0, 1, 0, 0, 0),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0),
        [](1, 0, 3, 0, 0, 2, 0, 0, 0),
        [](0, 3, 0, 0, 0, 0, 4, 0, 1))
    ).
board(wikipedia,
     []([](2, 0, 4, 0, 3, 0, 1, 0, 2, 0, 0, 1, 0),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 1),
        [](0, 0, 0, 0, 2, 0, 3, 0, 2, 0, 0, 0, 0),
        [](2, 0, 3, 0, 0, 2, 0, 0, 0, 3, 0, 1, 0),
        [](0, 0, 0, 0, 2, 0, 5, 0, 3, 0, 4, 0, 0),
        [](1, 0, 5, 0, 0, 2, 0, 1, 0, 0, 0, 2, 0),
        [](0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 4, 0, 2),
        [](0, 0, 4, 0, 4, 0, 0, 3, 0, 0, 0, 3, 0),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        [](2, 0, 2, 0, 3, 0, 0, 0, 3, 0, 2, 0, 3),
        [](0, 0, 0, 0, 0, 2, 0, 4, 0, 4, 0, 3, 0),
        [](0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0),
        [](3, 0, 0, 0, 0, 3, 0, 1, 0, 2, 0, 0, 2))
    ).

不错的拼图,我同意。这里是ECLiPSe中的一个中间解决方案,ECLiPSe是一种带有约束的Prolog方言

其思想是,对于电路板的每个字段,有四个变量N、E、S、W表示北、东等,它们的值为0..2,并表示该字段边缘上的连接数。对于节点字段,这些连接的总和必须达到给定的数目。对于空字段,连接必须经过N=S、E=W,而不是交叉N=S=0或E=W=0

您的示例正确地解决了以下问题:

?- hashi(stackoverflow).
3 = 6 = = = 6 = 3 
|   X       X   | 
| 1 X       X   | 
| | X       X   | 
2 | X     1 X   | 
| | X     | X   | 
| | X     | X   | 
1 | 3 - - 2 X   | 
  3 = = = = 4   1 
但是WikipediaOne没有,因为还没有连接约束

:- lib(ic).  % uses the integer constraint library

hashi(Name) :-
        board(Name, Board),
        dim(Board, [Imax,Jmax]),
        dim(NESW, [Imax,Jmax,4]),   % 4 variables N,E,S,W for each field
        ( foreachindex([I,J],Board), param(Board,NESW,Imax,Jmax) do
            Sum is Board[I,J],
            N is NESW[I,J,1],
            E is NESW[I,J,2],
            S is NESW[I,J,3],
            W is NESW[I,J,4],
            ( I > 1    -> N #= NESW[I-1,J,3] ; N = 0 ),
            ( I < Imax -> S #= NESW[I+1,J,1] ; S = 0 ),
            ( J > 1    -> W #= NESW[I,J-1,2] ; W = 0 ),
            ( J < Jmax -> E #= NESW[I,J+1,4] ; E = 0 ),
            ( Sum > 0 ->
                [N,E,S,W] #:: 0..2,
                N+E+S+W #= Sum
            ;
                N = S, E = W,
                (N #= 0) or (E #= 0)
            )
        ),

        % find a solution
        labeling(NESW),
        print_board(Board, NESW).


print_board(Board, NESW) :-
        ( foreachindex([I,J],Board), param(Board,NESW) do
            ( J > 1 -> true ; nl ),
            Sum is Board[I,J],
            ( Sum > 0 ->
                write(Sum)
            ; 
                NS is NESW[I,J,1],
                EW is NESW[I,J,2],
                symbol(NS, EW, Char),
                write(Char)
            ),
            write(' ')
        ),
        nl.

symbol(0, 0, ' ').
symbol(0, 1, '-').
symbol(0, 2, '=').
symbol(1, 0, '|').
symbol(2, 0, 'X').


% Examples

board(stackoverflow,
     []([](3, 0, 6, 0, 0, 0, 6, 0, 3),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0),
        [](0, 1, 0, 0, 0, 0, 0, 0, 0),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0),
        [](2, 0, 0, 0, 0, 1, 0, 0, 0),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0),
        [](1, 0, 3, 0, 0, 2, 0, 0, 0),
        [](0, 3, 0, 0, 0, 0, 4, 0, 1))
    ).
board(wikipedia,
     []([](2, 0, 4, 0, 3, 0, 1, 0, 2, 0, 0, 1, 0),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 1),
        [](0, 0, 0, 0, 2, 0, 3, 0, 2, 0, 0, 0, 0),
        [](2, 0, 3, 0, 0, 2, 0, 0, 0, 3, 0, 1, 0),
        [](0, 0, 0, 0, 2, 0, 5, 0, 3, 0, 4, 0, 0),
        [](1, 0, 5, 0, 0, 2, 0, 1, 0, 0, 0, 2, 0),
        [](0, 0, 0, 0, 0, 0, 2, 0, 2, 0, 4, 0, 2),
        [](0, 0, 4, 0, 4, 0, 0, 3, 0, 0, 0, 3, 0),
        [](0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
        [](2, 0, 2, 0, 3, 0, 0, 0, 3, 0, 2, 0, 3),
        [](0, 0, 0, 0, 0, 2, 0, 4, 0, 4, 0, 3, 0),
        [](0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0),
        [](3, 0, 0, 0, 0, 3, 0, 1, 0, 2, 0, 0, 2))
    ).

对于绘图,您可以考虑使用Unicode和字符。对于该图,您可以考虑使用Unicode和字符。你刚才给了我一个非常清晰的概念,一个易于使用的表示,甚至是一个中途实现!但请允许我问你一些问题,因为我有点困惑,当你说Eclipse是一种带有约束的prolog方言时,你不是说prolog没有约束,对吗?因为包括clpfd库允许例如SICStus中的prolog使用这样的函数,对吗?非常感谢你的回答,尽管我也不太明白维基百科的例子为什么不起作用:你应该能够在SICStus+clpfd中做几乎相同的事情,但是你不会有数组表示法的便利。我认为这个程序有问题。因为wikipedia board的答案是否定的。我知道连通性约束不是在这里制定的
,但对borad wikipedia的当前计划的答案应该是肯定的!可能是给定的解决方案没有连接。我说得对吗?如果不满足,则表示当前约束无法满足,如果我们添加连接约束,也将不满足。@OmG-请再次检查,该程序为wikipedia board提供了270个解决方案,与缺少的约束一致。很好!你刚才给了我一个非常清晰的概念,一个易于使用的表示,甚至是一个中途实现!但请允许我问你一些问题,因为我有点困惑,当你说Eclipse是一种带有约束的prolog方言时,你不是说prolog没有约束,对吗?因为包括clpfd库允许例如SICStus中的prolog使用这样的函数,对吗?非常感谢你的回答,尽管我也不太明白维基百科的例子为什么不起作用:你应该能够在SICStus+clpfd中做几乎相同的事情,但是你不会有数组表示法的便利。我认为这个程序有问题。因为wikipedia board的答案是否定的。我知道这里没有开发连接约束,但是borad wikipedia当前程序的答案应该是肯定的!可能是给定的解决方案没有连接。我说得对吗?如果不满足,则表示当前约束无法满足,如果我们添加连接约束,也将不满足。@OmG-请再次检查,该程序为wikipedia board提供了270个解决方案,与缺少的约束一致。