### 使用复杂棋盘表示的AI:模拟单个可能的移动

我目前正在使用C#开发一款基于回合制的网格策略游戏(类似于《魔法门之英雄无敌》和《奇迹时代》),首先专注于战斗部分。
游戏中网格上有多个单位,轮流进行攻击、射击、施放魔法、移动等操作。

在编程AI方面遇到了点困难。我想要一个能够提前规划的AI,我认为寻找最佳行动的过程可以是这样的:

  1. 生成可能移动的列表
  2. 对于每个可能的移动:
    • 模拟移动
    • 评估状态

然后我会在这个Minimax算法中重复这个过程,有点类似于创建一个国际象棋AI。

与制作国际象棋AI的最大区别在于“棋盘表示”(或状态);这个游戏有很多复杂性,仅仅用整数数组来存储状态是不够的。有多个单位,包含各种字段,用于存储生命值、攻击类型、移动方式和特殊能力等…

所以我面临的问题是在“模拟移动”部分。我需要创建一个类似于(previous state, move) => state的函数。我通常会复制之前的状态,并在复制上执行移动。但是我找不到一种高效的方式来复制将要执行移动的状态,因为它非常复杂,包含许多不同的对象。而且移动不应该改变之前的状态。

有什么好的代码结构可以模拟移动而不影响当前棋盘吗?

可能可以通过为每个移动添加一个Undo()方法来实现,但随着逻辑变得复杂,追踪所有被更改的内容会变得困难。

我希望能找到某种软件设计模式,或者如果有人做过类似的事情,我想知道他们是否也遇到了这个问题。

Stack Overflow上的很多问题都讨论了棋盘游戏AI,但似乎总是关于国际象棋/井字游戏,具有简单的棋盘表示。


回答:

这看起来像是大多数约束编程求解器也会处理的问题。一般来说,这些求解器实现了CommandMemento模式的混合。具体操作如下:

你需要一个表示游戏状态的类(这里称为State)。例如,包含棋盘的状态、当前可以移动的玩家、每个玩家的资源(如果这是游戏的一部分)等。最好使这种状态成为ICloneable的实例:这样可以克隆状态并在其上进行假设性的处理。

因此:

     ICloneable          ^          |+-------------------+|       State       |+-------------------+|+ Clone() : Object |+-------------------+

接下来,你有一个表示状态上可能移动的类IMoveIMove可以Alter状态。在大多数情况下,这种方法会返回一个IAlterResult:一个存储一些数据的对象,例如用于撤销移动的数据。

+------------------------------+|            IMove             |+------------------------------+|+ Alter(State) : IAlterResult |+------------------------------+

+---------------------+|    IAlterResult     |+---------------------+|+ Undo(State) : void |+---------------------+

例如,如果你从棋盘上移走一个棋子,IAlterResult会存储该棋子之前的位置,这样在撤销移动时,IAlterResult可以将棋子(例如)放回棋盘上。

每次你进行一个IMove,你会将对应的IAlterResult推入一个Stack<IAlterResult>中。这个堆栈因此会跟踪已完成的移动,并且可以通过迭代地弹出IAlterResult并执行Undo命令,将State恢复到原始状态。

大多数约束编程求解器将State的副本与IAlterResults交错。一个堆栈看起来像这样。

+---------------+| AlterResult5  || AlterResult4  || Copy2         || AlterResult3  || AlterResult2  || AlterResult1  || Original copy |+---------------+

如果你希望回溯四个状态,你可以首先查看是否有可用的副本,简单地弹出前两个改变(不执行Undo),然后使用Copy2并撤销AlterResult3AlterResult2。如果撤销操作需要大量计算结果,这可以提高一些效率。

你也可以在IAlterMove中存储你所做的移动 – 如果移动无法撤销,或难以撤销 – 你可以简单地回溯到最后保存的副本并执行堆栈中的所有移动。所以如果你想要例如撤销最后一个移动(AlterResult5),但那个结果无法撤销,你可以使用Copy2并重新执行存储在AlterResult4中的移动。

Related Posts

L1-L2正则化的不同系数

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

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

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

f1_score metric in lightgbm

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

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

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

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

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

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

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

发表回复

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