我现在开始使用SICStus Prolog学习prolog中的限制。虽然我知道如何解决使用它的简单问题,但我有一个练习,我必须解决一个拼图游戏。但是我不知道如何解决这个问题,因为我会有几个不同的元素(不同的元素),有没有人能给我一个例子来说明如何在prolog中表示一个片段列表以及我应该使用什么样的限制? / p>
答案 0 :(得分:4)
第一次尝试,我会沿着这些方向做点什么:
对于每个字段,使用5个变量。第一个变量表示进入该领域的拼图。每件作品都有自己的ID。对于您在上面的评论中提到的拼图,有12个部分,因此每个变量的域名为{1..12}:
P #:: 1..12,
其他四个变量表示每个字段的四个边缘,以及放置在字段中的拼图块是否有凹痕或标签。在您的拼图中,每个“向上”或“向下”侧面都有一个标签和凹痕,因此您可以表示标签是左侧还是右侧。同样,布尔决策变量。
[EL,EU,ER,ED] #:: 0..1,
拼图可以像这样编码:
piece(1, [](0,1,1,0)).
例如,您在评论中提供的网站拼图描述中的A部分。
现在您可以定义两个相邻字段之间的邻居关系。由于你有布尔变量,并且标签需要满足凹痕,你设置一个约束(而不是“限制”),要求总和为1:
R1 + L2 #= 1,
即,第一场中作品的右边缘必须与第二场中作品的左边缘相匹配。
您还可以为边界边缘设置类似的约束。
您设置了一个约束,要求所有字段必须包含不同的部分:
alldifferent(Fields),
(其中Fields是包含P变量的列表)
你需要一个表示拼图方向的变量:
O #:: 0..1,
你只需要一个,因为在你的拼图中,所有棋子都必须朝向同一个方向。
最后,您需要为每个字段组合片段,方向和边缘变量的约束,以便在为字段选择片段(并且方向已知)时,您可以适当地设置边缘变量:
once(piece(N, [](L,U,V,D))),
( ((P#=N) #/\ (O#=0)) #=> ((EL#=L) #/\ (EU#= U) #/\ (ER#=R) #/\ (ED#= D)) ),
( ((P#=N) #/\ (O#=1)) #=> ((EL#=R) #/\ (EU#=1-D) #/\ (ER#=L) #/\ (ED#=1-U)) ),
(语法不适用于Sicstus,但ECLiPSe。虽然FD约束库应该足够相似)
请注意,当翻转一块时,我必须反转下边缘的编码。这允许我保持上/下边缘的“和等于一”约束,但是次优,因为它阻止我容易地将信息从边缘变量传播到片段变量(片段可以获得与另一个倒置时)。但是我太懒了,不能为第一次尝试改变编码。
(编辑:这应该很容易通过反转下边缘的编码来修复,例如:piece(1, [](0,1,1,1)).
,并使用需要相邻片段的上下边缘的约束来获得相同的值而不是总和之一。)
全部抛出并简单地标记P变量(在首先实例化方向变量之后)给出了两个解决方案:
?- rapids(S,O).
S = []([](5, 2, 8, 6), [](11, 10, 1, 4), [](3, 9, 12, 7))
O = 0
Yes (0.01s cpu, solution 1, maybe more)
S = []([](5, 3, 11, 12), [](8, 9, 7, 4), [](2, 10, 6, 1))
O = 1
Yes (0.04s cpu, solution 2, maybe more)
No (0.05s cpu)
我使用矩阵代替列表,以使拼图字段的行更加突出。矩阵还允许更快地访问不同行中的相邻字段。