Recursion 如何在Prolog中的递归函数中使用动态列表

Recursion 如何在Prolog中的递归函数中使用动态列表,recursion,prolog,alpha-beta-pruning,Recursion,Prolog,Alpha Beta Pruning,我有一个动态列表,存储字段坐标和该字段的所有者 :-dynamic board/2. 此列表中的一个条目如下所示: board(e4,[w]). 还有22个董事会职位看起来都很相似 我正在尝试为棋盘的游戏实现AI。 我正在使用alpha-beta算法,因此必须创建新的“板”来评估这种情况 我有一份原始董事会的副本要处理,但现在有一个功能,可以根据当前的移动调整董事会,如下所示: move(position,boardOld,boardNew) starting_board([[b-

我有一个动态列表,存储字段坐标和该字段的所有者

:-dynamic
    board/2.
此列表中的一个条目如下所示:

board(e4,[w]).
还有22个董事会职位看起来都很相似

我正在尝试为棋盘的游戏实现AI。 我正在使用alpha-beta算法,因此必须创建新的“板”来评估这种情况

我有一份原始董事会的副本要处理,但现在有一个功能,可以根据当前的移动调整董事会,如下所示:

move(position,boardOld,boardNew)
starting_board([[b-r, b-b, b-n, b-q, b-k, b-n, b-b, b-r],
                [b-p, b-p, b-p, b-p, b-p, b-p, b-p, b-p],
                [nil, nil, nil, nil, nil, nil, nil, nil],
                [nil, nil, nil, nil, nil, nil, nil, nil],
                [nil, nil, nil, nil, nil, nil, nil, nil],
                [nil, nil, nil, nil, nil, nil, nil, nil],
                [w-p, w-p, w-p, w-p, w-p, w-p, w-p, w-p],
                [w-r, w-b, w-n, w-q, w-k, w-n, w-b, w-r]]).    
这被称为递归,因此在第二次调用boardOld等于boardNew,而boardNew是分析中具有下一步的oldBoard

我的想法是在每次迭代中复制旧板,但我不知道如何这样做

copy_predicate_clauses(boardOld(A,B),boardNew(A,B)).
将旧板添加到新板,因为列表已存在

我正在使用以下算法(无法复制,因为它是图像):

PDF第445页(书籍第407页)

该算法在初始化时将位置获取为“Board”,我不知道如何使用我的列表执行此操作,此外,move子句(move,Position,Position1)返回带有当前移动的新板。这被称为递归,我不知道如何在不覆盖以前的板的情况下为position1创建板

编辑//

好的,我明白了。但我已经在游戏中大量使用了这个谓词,我不想完全改变它。我这样做:

findall((X,Y),board(X,Y),CurrentBoard).
CurrentBoard给了我一个这样的列表

[(e4,[w,w]),(g4,[s,w]),(b7,[r,w,s])]
现在我不能用我的方法来决定可能的行动

我有这样的事实

move(e4,d5).
move(d5,e6).
说明哪些移动(从,到)是可能的,现在我尝试了这个

findall((X,Y),listMoves(CurrentBoard,X,Y),possibleMoves).
用这样的东西。我在这一点上苦苦挣扎。如何生成包含可能移动的列表。我必须以某种方式从currentBoard获得X坐标,检查该坐标(该坐标上的棋子)列表的标题是否属于我,并检查Y坐标(to)是否自由

listMoves([Coordinate|[Head|Tail]], X, Y) :-
     move(X,Y),
     ownField(X,Coordinate,Head),
如果您阅读(打印)第401页,作者将基本游戏算法概述如下:

本节其余部分使用“位置”一词的方式让我相信它们指的是整个游戏状态。对于像国际象棋这样的游戏,这将是棋盘本身的状态:棋子在哪里

在此基础上,作者继续讨论了
move/2
move/3
,这两个定义如下:

通过
choose_move/3
可以方便地将move的选择与move/3的执行分开

然后在第403页

如果
move
可能从当前位置移动,则谓词
move(Position,move)
为真

综上所述,这一切都向我表明,他们希望一个
位置
是完整的棋盘(我认为你在下棋,是吗?)和
移动
是合法的移动,以便定义
移动/2
,然后
移动/3
就是
移动/2
,返回结果棋盘。换句话说,最初的棋盘可能看起来像这样:

move(position,boardOld,boardNew)
starting_board([[b-r, b-b, b-n, b-q, b-k, b-n, b-b, b-r],
                [b-p, b-p, b-p, b-p, b-p, b-p, b-p, b-p],
                [nil, nil, nil, nil, nil, nil, nil, nil],
                [nil, nil, nil, nil, nil, nil, nil, nil],
                [nil, nil, nil, nil, nil, nil, nil, nil],
                [nil, nil, nil, nil, nil, nil, nil, nil],
                [w-p, w-p, w-p, w-p, w-p, w-p, w-p, w-p],
                [w-r, w-b, w-n, w-q, w-k, w-n, w-b, w-r]]).    
这就是我可以设置它的方式;毫无疑问,可能有更有效或更明智的方法,但关键是,您的
位置
值应始终包含董事会的整个状态,如下所示。事实上,它可能需要包括其他信息,比如轮到谁了,或者如果你想记录每个动作,你也可以把它放在那里

从这里,您可以编写
move/2
move/3
。我倾向于(出于说教的目的)只写
move/3
,并用它来构建
move/2
,但为了提高效率,您可能需要单独完成它们。我下棋已经够差了,我就到此为止(我不相信你会下棋)。但基本上,你想看到的是
move(StartPosition,move,NewPosition)
,给定一个起始位置,将
move
与移动的某种简短描述相结合,并在移动后将
NewPosition
与整个电路板状态相结合,因此你会得到一个前后快照

一旦你有了这些部件,我想你可以按原样使用书籍代码

我认为你基本上有一些误解:

  • 电路板不是具有多个解决方案的谓词。它是您创建的一个数据结构,代表了正在发生的一切
  • 动态谓词不构成列表。确实,您可以有多个解决方案,并且它们有一个顺序,对于您需要执行的各种操作来说,这不是一个方便的数据结构
  • 我真的不认为你想把这两个想法混在一起,看看会有什么结果

  • 由于缺乏详细信息,这很难回答(如果不是不可能的话)。我将编辑此问题并分享更多详细信息。您的问题是,您试图将动态存储视为列表(您自己这么说,但
    board/2
    是一个谓词,而不是一个列表),它并不真正适合此目的。你最好为你的董事会状态创建明确的列表并传递它们,因为在动态存储中操作实际列表要比操作事实容易得多。我带着一些想法编辑了我的线程……但在序言中,再次尝试
    possibleMoves
    是一个原子,而不是一个变量。:)