我试图写一个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
这是一个真正的哈希板的代表。
现在的重点是能够绘制相同的东西,但如果有的话也会画桥梁。在我想到自己制定限制之前,我必须能够做到这一点,因为以一种糟糕的代表方式去做会使我的工作变得更加困难。
我开始考虑这样的解决方案:
如果董事会的每个元素都是一个列表:
[NumberOfConnections, [ListOfConnections]]
但是这给了我没有关于绘图的信息,连接列表到底有什么?
也许这个:
[Index, NumberOfConnections, [ListOfIndex]]
这样每个“孤岛”都会有一个唯一的ID,连接列表会有ID 但绘制仍然听起来有点难,最后桥梁只能是水平或垂直的
无论如何,任何人都可以想出一种更好的表现方式,这使得最容易实现该计划的最终目标?
答案 0 :(得分:3)
很好的谜题,我同意。这是ECLiPSe中的一个中途解决方案,一个带约束的Prolog方言(http://eclipseclp.org)。
我们的想法是,对于电路板的每个字段,有四个变量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
但维基百科没有,因为还没有连接约束!
:- 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))
).
答案 1 :(得分:1)
多么酷的谜题!我自己做了一些,我没有看到一个明显的方法来解决它们的确定性,这是一个很好的解决方案。像俄罗斯方块这样的游戏从你不会感到无聊的事实中获得了很多持续的游戏价值 - 即使是一个好的策略也可以不断完善。这有一个实际的结果:如果我编码这个,我会花费更多的时间来寻找确定性算法。我会专注于Prolog擅长的生成/测试范例。
如果您知道自己将要进行生成和测试,那么您已经知道优化所需的全部工作:使您的生成器更加智能化(因此可以生成更好的候选者)并快速进行测试。所以我正在看你的董事会代表,我问自己:从这里生成替代品会变得容易和快速吗?我们都知道答案是否定的,原因如下:
寻找从任何特定岛屿连接的替代岛屿将是非常低效的:向前和向后搜索列表,然后通过当前偏移索引所有其他列表。这是一个庞大的列表,不会便宜。
检测和防止过桥会很有意思。
更重要的是,使用此设计,编码网桥的正确方法并不明显。群岛之间可以分开很远的距离 - 你打算在每个连接小区中放置一个0/1/2吗?如果是这样,您就会遇到数据重复问题;如果没有,你将有一些乐趣来计算哪个位置应该保持桥数。
这只是一种直觉,但是拥有像这样的异构数据结构,其中元素的“种类”完全由指数是奇数还是偶数决定,让我觉得不受欢迎。
我认为您在电路板布局方面所拥有的是一种很棒的输入格式,但我不认为它能够很好地为您提供中间表示 。游戏显然是一个图形问题。这表明两个经典图形数据结构中的一个可能更有用:邻接列表或边缘矩阵。这些中的任何一个都将加快选择桥梁布局的替代方案,但对我来说(可能对那些做更多图论的人)如何防止桥梁交叉并不明显。理想情况下,您的数据结构只会阻止桥接交叉的发生。接下来最好的是阻止发电机产生具有桥接交叉的候选解决方案;最糟糕的是在测试阶段完全失败。
答案 2 :(得分:0)
对于绘制桥梁,可以将ASCII 179用于单个垂直桥,186用于双垂直桥,196用于单个水平桥,205用于双水平桥。这取决于正在使用的扩展ASCII集。它最常用。
对于内部表示,我在一个方向上使用-1和-2表示单桥和双桥,在另一个方向上使用-3和-4。你可以使用任何不是0-8的符号,但这有一个额外的好处,就是简单地将桥梁添加到岛上(将(-3,-4)转换为(-1,-2))来检查解。如果总和为0,则解决该岛。