.. SPDX-License-Identifier: GFDL-1.3-or-later FreeKill 的 AI 系统 =================== 概述 ---- 备选算法: - MCTS - 神杀算法 -------------- MCTS实现 -------- 实现该算法的最大难点在于如何模拟。 首先是树中各个节点的保存,我们自然无法给某个节点都分配一个Room对象。由于节点是通过根节点进行相应的决策拓展而来的,所以其实节点内部的数据可以保存为各个决策的数组。 然后节点首先要能知道自己的双亲节点和孩子节点,这个分别用一个值和一个数组表示就行了。 想要实现模拟的话,重点是如何创造一个一模一样的Room出来。指望lua提供完全clone一个coroutine所有内容或许不是很现实。以下是一种备选方案: 1. 首先Room初始化的时候也初始化一个AI用的Room 2. Room内要能够录像,记录所有的request结果和random生成的值。为此可能要自定义一个random函数对自带的math.random进行封装。 3. 在录像的时候,AI Room也跟着录像的内容进行更新。AI Room本质上也就是一个Room而已,或者可以是Room的子类,反正他的内容就是用这个方式和真Room即时同步的。 4. 在AI即将处理问题的时候,首先获得所有可行选项。根据算法,需要对某个节点进行randomplay。 5. randomplay的话如果直接用AI Room,那么回溯的时候如何回到先前的状态呢? 1. 考虑新建一个新的AI Room,然后重放录像以达到开始状态。这样每次randomplay之前都要先回复一下状态,而随着录像的加长这个过程也可能变长,导致AI越来越慢 2. 考虑真Room的所有字段全部复制给AI Room一份。但有个问题在于如何把程序控制流和栈也跳转到一样的地方。所以这个是很难实现的。 3. 所以考虑用方案1。为了缓解太慢的情况,可以把1和2结合起来。约定好在某个时间点(比如GameLogic:action中的那个死循环执行)就与Room交换数据,然后这时候复盘录像的起始时间点修改。这样的话为了从randomplay恢复状态,就有必要将此时交换的数据额外保存一份。为了能让Logic平安跑到那个时间点,从人凑齐直到那个时间点的录像也要保存一份。 6. 解决了模拟和回溯的问题的话,就可以考虑实现该算法了。 那么为了模拟,首先得实现一个RandomAI才行。 然后还有一项前置工作是服务器端的录像功能。得先做好这个才能进行AI编写啊。所以AI暂且推迟一下好了。