在Prolog中搜索游戏

我想问问是否有人能帮我,我已经在8块拼图程序上做了很多工作,现在我想进一步扩展它,但我遇到了困难。

所以这里是棋盘。

游戏棋盘

这个游戏的想法是尝试将一个棋子,例如目前位于a1的P1,移动到b1。棋盘上最多可以有5个其他棋子,每个棋子一次只能占据和移动一个空间。

我想做的是计算所需的移动步骤,例如将位于B1位置的P1移动到A3位置(或者任何结束位置),如果它一次只能移动一个空间。两个棋子不能占据同一个空间,如果t区有棋子,棋子也不能穿越到另一侧。我该如何编写代码,以便当我输入类似于

?- pmove([at(a1,p3),at(b1,p1),at(b3,p2)],          [at(a1,p1),at(b1,p3),at(b3,p2)],M). 

输入所有位置的起始状态以及它们应该结束的位置时,它会输出类似于以下内容的结果:

M = [move(p3,a1,t),move(p1,b1,b2),move(p3,t,b1),      move(p1,b2,t),move(p1,t,a1)] 

显示从开始到结束移动棋子所需的所有步骤,以及其他棋子到达其位置所需的移动。我认为使用广度优先搜索来解决这个问题是最好的,但我不是很确定接下来该怎么做。


回答:

这是一个很棒的问题,通过解决它将是提高Prolog技能的绝佳方式!所以我赞赏你选择了这个问题。

这个回答是一个草图,而不是完整的解决方案,我希望这足以满足你的需求。

接下来你要做的是编写一个谓词,表达从一个状态到另一个状态的单个有效移动,像这样:

singlemove(StartBoardState, Move, NextBoardState) :- ...

所以我们将把at(Place, Piece)的列表视为棋盘状态,把像move(Piece,From,To)这样的结构视为移动;前者将与StartBoardStateNextBoardState统一,后者将与Move统一。

你的游戏规则应该编码在singlemove/3中。你可以将其视为给定棋盘状态、每个有效移动和结果状态之间的关系。

我认为一旦你有了这个,至少一种低效的解决方案就会变得显而易见,使用蛮力搜索。一旦你让它工作起来(速度慢,可能只适用于两步游戏),你就可以开始看到如何通过使搜索更智能来提高性能。

如何实现singlemove/3

从问题中,这个游戏的规则是:

…它一次只能移动一个空间。两个棋子不能占据同一个空间,如果t区有棋子,棋子也不能穿越到另一侧。

所以首先,让我们陈述一些关于棋盘的事实。空间叫什么名字?

boardspace(b1).boardspace(b2).boardspace(b3).boardspace(t).boardspace(a1).boardspace(a2).boardspace(a3).

现在我们需要一些位置信息。

:- op(300, xfx, left_of).:- op(300, xfx, right_of).    b1 left_of b2.b2 left_of b3.a1 left_of a2.a2 left_of a3.row(b1, upper).row(b2, upper).row(b3, upper).row(a1, lower).row(a2, lower).row(a3, lower).X right_of Y :- Y left_of X.adjacent(X, Y) :- X left_of Y.adjacent(X, Y) :- X right_of Y.adjacent(t, X) :- boardspace(X), X \= t.adjacent(X, t) :- boardspace(X), X \= t.

我还不确定是否需要所有这些,但这似乎是一个合理的开始。现在让我们处理规则。

因此,游戏有三个规则:

  1. 每回合只能移动一个棋子。
  2. 一个棋子每回合只能移动一个空间。
  3. 两个棋子不能占据同一个空间。
  4. 如果t区有棋子,棋子不能“穿越到另一侧”。

我觉得#1通过有一个singlemove/3谓词就已经得到了充分处理。我们调用它一次,就得到一个移动。

对于#2,我们可以根据当前棋子旁边的空间来构建附近空间的列表。假设p1要移动,并且我有一个如上定义的棋盘,member(at(StartLocation, p1), Board), adjacent(StartLocation, EndLocation)EndLocationp1可以移动的地方统一。让我们试试:

?- Board = [at(a1,p3),at(b1,p1),at(b3,p2)],    member(at(StartLocation, p1), Board),    adjacent(StartLocation, EndLocation).Board = [at(a1, p3), at(b1, p1), at(b3, p2)],StartLocation = b1,EndLocation = b2 ;Board = [at(a1, p3), at(b1, p1), at(b3, p2)],StartLocation = b1,EndLocation = t ;false.

所以这似乎是正确的;b1的相邻位置是b2和t。

现在让我们编码下一条规则,两个棋子不能占据同一个空间。

unoccupied(Board, Location) :-    \+ member(at(Location, _), Board).

现在我们可以将这两件事结合起来,作为singlemove/3的一个良好开端:

singlemove(Board,       move(Piece,StartLocation,EndLocation),       [at(EndLocation,Piece)|RemainingLocations]) :-    select(at(StartLocation,Piece), Board, RemainingLocations),    adjacent(StartLocation, EndLocation),    unoccupied(Board, EndLocation).

让我们试试:

?- Board = [at(a1,p3),at(a2,p1),at(a3,p2)],    singlemove(Board, Move, NextBoard).Board = [at(a1, p3), at(a2, p1), at(a3, p2)],Move = move(p3, a1, t),NextBoard = [at(t, p3), at(a2, p1), at(a3, p2)] ;Board = [at(a1, p3), at(a2, p1), at(a3, p2)],Move = move(p1, a2, t),NextBoard = [at(t, p1), at(a1, p3), at(a3, p2)] ;Board = [at(a1, p3), at(a2, p1), at(a3, p2)],Move = move(p2, a3, t),NextBoard = [at(t, p2), at(a1, p3), at(a2, p1)] ;false.

这有什么有趣的地方吗?我使用select/3将列表分成候选和剩余部分。我在子句的头部构建结果。但除此之外,我只是在拿你的规则,翻译成Prolog,并列出它们。所以你可以看到你只需要再实现一个谓词来实现你的第四条规则,它将直接放在unoccupied/2之后,以进一步过滤掉无效的移动。

希望你能理解这个过程。我的数据模型可能有点过多,但话说回来,你可能会发现你需要的比最初看起来的更多。而且搜索策略很弱。但这是整体解决方案的基础情况。归纳情况将很有趣——我再次建议你尝试使用内置的深度优先策略,看看它有多糟糕,然后再使用BFS。你可能会想使用trace/0,看看你什么时候陷入陷阱,以及你如何通过更好的显式推理来规避它。但我认为这是一个好的开始,我希望这对你有帮助。

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注