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 能量自增进程
🖼️ 效果图

Last updated