First Machine!

在进程模块框架下的第一个可运行的 AI

我们已经写了很多的代码了,如果一直是概念设计以及抽象的代码编写,很快我们就会丧失兴趣。所以我们需要看到什么 :D

编写好了进程模块之后,我们已经可以实现我们第一个简单的 AI了。它的目标就是完成游戏教程中的任务:

  • Spawn 生 Upgrader, Harvester

  • Harvester 采能量, 补充 Spawn

  • Upgrader 采能量, 升级 Controller

但是我们依赖信号集以及我们的进程模块可以省去教程那样每 tick 查询来校验 Creep 是否存在,能量够不够生成新的 Creep等等。

所以,我们要做的是高阶的教程 AI!它是属于触发型的,每 tick 不用不停的查询。满足条件的时候自动唤醒执行,不满足条件的时候自动阻塞,不再执行。

当 Creep 死亡后,会创建 spawn 这个 Creep 的进程,这个进程会申请能量,申请不到就会阻塞,不再运行。当能量够的时候,会自动唤醒,并进行生成。

而一旦 Creep 出生后,会自动激活固定的执行进程。我们后边在编写具体的模块的时候,通常也是需要固定的 Creep 来执行任务。除非是例如传输模块,需要自适应,复杂一些。

但是,目前我们这个体系是 vulnerable 对于外界的干预的,例如:别人的 Creep 来从我们的 Spawn 中拿了些东西出来,那么 Spawn 中的资源就与我们的信号量值不同步。再比如:我们的 Spawn 被 Nuker 摧毁了。这些问题目前还解决不了。

🌞 Apollo AI

import { PMI, ProcessModule } from "./modules/proc/proc";

interface ApolloInterface {
    proc: ProcessModule
}

export const Apollo: ApolloInterface = {
    proc: new PMI()
}

🤖 AI Body

这只是一个 AI 雏形,所以很多地方还很粗糙。但是我们已经实现了我们的目标。

甚至,这个雏形当中也包含着我们未来模块的雏形。

import { Apollo } from './apollo'
import { errorMapper } from './modules/errorMapper'
import { OK_STOP_CURRENT, OK_STOP_CUSTOM, OK_STOP_NEXT } from './modules/proc/proc'
import { assertWithMsg } from './utils'

// Upgrader 信号量
const upgraderOccupiedSignal = {}
const upgraderSlotSignal = {}
// Harvester 信号量
const harvesterOccupiedSignal = {}
const harvesterSlotSignal = {}
// Spawn 容量信号量
const capacitySignal = {}
// Spawn 能量信号量
const energySignal = {};

(function() {
    // 创建信号量
    for (const name in Game.spawns) {
        // Upgrader
        upgraderOccupiedSignal[name] = Apollo.proc.signal.createSignal((`${name}_upgrader` in Game.creeps)? 1: 0)
        upgraderSlotSignal[name] = Apollo.proc.signal.createSignal((`${name}_upgrader` in Game.creeps)? 0: 1)

        // Harvester
        harvesterOccupiedSignal[name] = Apollo.proc.signal.createSignal((`${name}_harvester` in Game.creeps)? 1: 0)
        harvesterSlotSignal[name] = Apollo.proc.signal.createSignal((`${name}_harvester` in Game.creeps)? 0: 1)
        
        capacitySignal[name] = Apollo.proc.signal.createSignal(Game.spawns[name].store.getFreeCapacity())
        energySignal[name] = Apollo.proc.signal.createSignal(Game.spawns[name].store[RESOURCE_ENERGY])
    }

    // 创建生 Creep 进程
    for (const name in Game.spawns) {
        // 生 Harvester
        Apollo.proc.createProc([
            ["_START", () => Apollo.proc.signal.Swait(
                {signalId: energySignal[name], lowerbound: 150, request: 150}, 
                {signalId: harvesterSlotSignal[name], lowerbound: 1, request: 1}
            )], 
            () => {
                const spawn = Game.spawns[name]
                if (spawn.spawning || (spawn as any).isSpawning) return OK_STOP_CURRENT
                else {
                    assertWithMsg(spawn.spawnCreep([WORK, CARRY, MOVE], `${name}_harvester`) === OK);
                    (spawn as any).isSpawning = true // Hack ;D
                    return OK_STOP_NEXT
                }
            }, 
            () => Apollo.proc.signal.Ssignal(
                {signalId: capacitySignal[name], request: 150}, 
                {signalId: harvesterOccupiedSignal[name], request: 1}
            ), 
            () => [OK_STOP_CUSTOM, "_START"]
        ], `${name} => Harvester`)
        // 生 Upgrader
        Apollo.proc.createProc([
            ["_START", () => Apollo.proc.signal.Swait(
                {signalId: energySignal[name], lowerbound: 150, request: 150}, 
                {signalId: upgraderSlotSignal[name], lowerbound: 1, request: 1}
            )], 
            () => {
                const spawn = Game.spawns[name]
                if (spawn.spawning || (spawn as any).isSpawning) return OK_STOP_CURRENT
                else {
                    assertWithMsg(spawn.spawnCreep([WORK, CARRY, MOVE], `${name}_upgrader`) === OK);
                    (spawn as any).isSpawning = true // Hack ;D
                    return OK_STOP_NEXT
                }
            }, 
            () => Apollo.proc.signal.Ssignal(
                {signalId: capacitySignal[name], request: 150}, 
                {signalId: upgraderOccupiedSignal[name], request: 1}
            ), 
            () => [OK_STOP_CUSTOM, "_START"]
        ], `${name} => Upgrader`)
    }

    for (const name in Game.spawns) {
        // Upgrader 进程流程
        Apollo.proc.createProc([
            ["_START", () => Apollo.proc.signal.Swait(
                {signalId: upgraderOccupiedSignal[name], lowerbound: 1, request: 0}
            )], 
            () => {
                const creep = Game.creeps[`${name}_upgrader`]
                if (!creep) return [OK_STOP_CUSTOM, "_START"]

                if (creep.store.getFreeCapacity(RESOURCE_ENERGY) === 0) return OK

                const source = creep.pos.findClosestByRange(FIND_SOURCES)
                if (creep.harvest(source) === ERR_NOT_IN_RANGE) creep.moveTo(source)
                return OK_STOP_CURRENT
            }, 
            () => {
                const creep = Game.creeps[`${name}_upgrader`]
                if (!creep) return [OK_STOP_CUSTOM, "_START"]

                if (creep.store.getUsedCapacity(RESOURCE_ENERGY) === 0) return [OK_STOP_CUSTOM, "_START"]

                const controller = creep.room.controller
                if (creep.upgradeController(controller) === ERR_NOT_IN_RANGE) creep.moveTo(controller)
                return OK_STOP_CURRENT
            }
        ], `${name}'s Upgrader Work`)

        // Harvester 进程流程
        Apollo.proc.createProc([
            ["_START", () => Apollo.proc.signal.Swait(
                {signalId: harvesterOccupiedSignal[name], lowerbound: 1, request: 0}
            )], 
            () => {
                const creep = Game.creeps[`${name}_harvester`]
                if (!creep) return [OK_STOP_CUSTOM, "_START"]

                if (creep.store.getFreeCapacity(RESOURCE_ENERGY) === 0) return OK

                const source = creep.pos.findClosestByRange(FIND_SOURCES)
                if (creep.harvest(source) === ERR_NOT_IN_RANGE) creep.moveTo(source)
                return OK_STOP_CURRENT
            }, 
            () => {
                const creep = Game.creeps[`${name}_harvester`]
                if (!creep) return [OK_STOP_CUSTOM, "_START"]

                if (creep.store.getUsedCapacity(RESOURCE_ENERGY) === 0) return [OK_STOP_CUSTOM, "_START"]

                const spawn = Game.spawns[name]
                const amount = Math.min(creep.store.getUsedCapacity(RESOURCE_ENERGY), spawn.store.getFreeCapacity(RESOURCE_ENERGY))
                if (creep.transfer(spawn, RESOURCE_ENERGY, amount) === ERR_NOT_IN_RANGE) creep.moveTo(spawn)
                else {
                    // 放到下一 tick 更新会更好
                    Apollo.proc.signal.Ssignal(
                        {signalId: capacitySignal[name], request: -amount}, 
                        {signalId: energySignal[name], request: amount}
                    )
                }
                return OK_STOP_CURRENT
            }
        ], `${name}'s Harvester Work`)
    }

    // 监视死亡的 Creep 进程
    Apollo.proc.createProc([
        () => {
            // 清除死亡的 Creep Memory
            for (const name in Memory.creeps) {
                if (!(name in Game.creeps)) {
                    const desc = name.split('_')
                    const spawnName = desc[0], role = desc[1]
                    if (role === "harvester") {
                        Apollo.proc.signal.Ssignal(
                            {signalId: harvesterOccupiedSignal[spawnName], request: -1}, 
                            {signalId: harvesterSlotSignal[spawnName], request: 1}
                        )
                    } else if (role === "upgrader") {
                        Apollo.proc.signal.Ssignal(
                            {signalId: upgraderOccupiedSignal[spawnName], request: -1}, 
                            {signalId: upgraderSlotSignal[spawnName], request: 1}
                        )
                    }
                    delete Memory.creeps[name]
                }
            }
            return OK_STOP_CURRENT
        }
    ], "监视死亡 Creep")

    for (const name in Game.spawns)
        // 监视自增长的 Spawn Energy
        Apollo.proc.createProc([
            () => {
                const spawn = Game.spawns[name]
                if (spawn.store.getFreeCapacity(RESOURCE_ENERGY) > 0)
                    Apollo.proc.signal.Ssignal(
                        {signalId: capacitySignal[name], request: -1}, 
                        {signalId: energySignal[name], request: 1}
                    )
                return OK_STOP_CURRENT
            }
        ], `监视${name}自增 Energy`)
})()

export const loop = errorMapper(() => {
    Apollo.proc.tick()
})

这个雏形还有很多可以改进的地方以及问题(每个房间只能有一个 Spawn,同一时间只能有一个 Creep transfer 能量到 Spawn等等),但是它包含了基本所需的进程:

  • 生 Harvester & Upgrader 的进程

  • Harvester & Upgrader 工作进程

  • 监视死亡的 Creep,触发再生进程

  • 监视 Spawn 能量自增进程

🖼️ 效果图

Harvester & Upgrader

Last updated