@v12-engine/engine 0.1.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abilities/amounts.d.ts +28 -0
- package/dist/abilities/amounts.js +74 -0
- package/dist/abilities/amounts.js.map +1 -0
- package/dist/abilities/buffs.d.ts +106 -6
- package/dist/abilities/buffs.js +443 -0
- package/dist/abilities/buffs.js.map +1 -0
- package/dist/abilities/castTold.d.ts +2 -0
- package/dist/abilities/castTold.js +66 -0
- package/dist/abilities/castTold.js.map +1 -0
- package/dist/abilities/chance.d.ts +38 -0
- package/dist/abilities/chance.js +77 -0
- package/dist/abilities/chance.js.map +1 -0
- package/dist/abilities/charges.d.ts +18 -0
- package/dist/abilities/charges.js +101 -0
- package/dist/abilities/charges.js.map +1 -0
- package/dist/abilities/control.d.ts +28 -0
- package/dist/abilities/control.js +113 -0
- package/dist/abilities/control.js.map +1 -0
- package/dist/abilities/dispel.js +51 -0
- package/dist/abilities/dispel.js.map +1 -0
- package/dist/abilities/effectFields.d.ts +1 -1
- package/dist/abilities/effectFields.js +676 -0
- package/dist/abilities/effectFields.js.map +1 -0
- package/dist/abilities/effectIndex.d.ts +27 -0
- package/dist/abilities/effectIndex.js +64 -0
- package/dist/abilities/effectIndex.js.map +1 -0
- package/dist/abilities/effects/buff.d.ts +9 -3
- package/dist/abilities/effects/buff.js +84 -0
- package/dist/abilities/effects/buff.js.map +1 -0
- package/dist/abilities/effects/conditions.d.ts +2 -0
- package/dist/abilities/effects/conditions.js +41 -0
- package/dist/abilities/effects/conditions.js.map +1 -0
- package/dist/abilities/effects/core.js +54 -0
- package/dist/abilities/effects/core.js.map +1 -0
- package/dist/abilities/effects/damage.d.ts +1 -1
- package/dist/abilities/effects/damage.js +43 -0
- package/dist/abilities/effects/damage.js.map +1 -0
- package/dist/abilities/effects/heal.d.ts +2 -2
- package/dist/abilities/effects/heal.js +51 -0
- package/dist/abilities/effects/heal.js.map +1 -0
- package/dist/abilities/effects/missile.d.ts +13 -1
- package/dist/abilities/effects/missile.js +104 -0
- package/dist/abilities/effects/missile.js.map +1 -0
- package/dist/abilities/effects/morph.d.ts +5 -0
- package/dist/abilities/effects/morph.js +43 -0
- package/dist/abilities/effects/morph.js.map +1 -0
- package/dist/abilities/effects/moves.d.ts +16 -4
- package/dist/abilities/effects/moves.js +212 -0
- package/dist/abilities/effects/moves.js.map +1 -0
- package/dist/abilities/effects/passives.d.ts +19 -9
- package/dist/abilities/effects/passives.js +131 -0
- package/dist/abilities/effects/passives.js.map +1 -0
- package/dist/abilities/effects/proxy.d.ts +5 -0
- package/dist/abilities/effects/proxy.js +60 -0
- package/dist/abilities/effects/proxy.js.map +1 -0
- package/dist/abilities/effects/sight.d.ts +2 -2
- package/dist/abilities/effects/sight.js +39 -0
- package/dist/abilities/effects/sight.js.map +1 -0
- package/dist/abilities/effects/spawn.d.ts +5 -2
- package/dist/abilities/effects/spawn.js +40 -0
- package/dist/abilities/effects/spawn.js.map +1 -0
- package/dist/abilities/effects/timed.d.ts +2 -2
- package/dist/abilities/effects/timed.js +96 -0
- package/dist/abilities/effects/timed.js.map +1 -0
- package/dist/abilities/effects/trade.d.ts +1 -1
- package/dist/abilities/effects/trade.js +25 -0
- package/dist/abilities/effects/trade.js.map +1 -0
- package/dist/abilities/effects.d.ts +97 -23
- package/dist/abilities/effects.js +860 -0
- package/dist/abilities/effects.js.map +1 -0
- package/dist/abilities/land.d.ts +38 -4
- package/dist/abilities/land.js +189 -0
- package/dist/abilities/land.js.map +1 -0
- package/dist/abilities/recast.d.ts +45 -0
- package/dist/abilities/recast.js +118 -0
- package/dist/abilities/recast.js.map +1 -0
- package/dist/abilities/scaled.js +40 -0
- package/dist/abilities/scaled.js.map +1 -0
- package/dist/abilities/screen.d.ts +6 -0
- package/dist/abilities/screen.js +83 -0
- package/dist/abilities/screen.js.map +1 -0
- package/dist/abilities/shapes.d.ts +66 -0
- package/dist/abilities/shapes.js +122 -0
- package/dist/abilities/shapes.js.map +1 -0
- package/dist/abilities/text.d.ts +14 -2
- package/dist/abilities/text.js +204 -0
- package/dist/abilities/text.js.map +1 -0
- package/dist/abilities/triggers.d.ts +98 -0
- package/dist/abilities/triggers.js +351 -0
- package/dist/abilities/triggers.js.map +1 -0
- package/dist/abilityChanges.d.ts +131 -0
- package/dist/abilityChanges.js +345 -0
- package/dist/abilityChanges.js.map +1 -0
- package/dist/ai/area.d.ts +4 -1
- package/dist/ai/brain.d.ts +4 -1
- package/dist/ai/brain.js +844 -0
- package/dist/ai/brain.js.map +1 -0
- package/dist/ai/budget.js +74 -0
- package/dist/ai/budget.js.map +1 -0
- package/dist/ai/checks.d.ts +9 -1
- package/dist/ai/checks.js +242 -0
- package/dist/ai/checks.js.map +1 -0
- package/dist/ai/computer.d.ts +11 -4
- package/dist/ai/computer.js +251 -0
- package/dist/ai/computer.js.map +1 -0
- package/dist/ai/defence.d.ts +4 -0
- package/dist/ai/defence.js +430 -0
- package/dist/ai/defence.js.map +1 -0
- package/dist/ai/define.d.ts +3 -2
- package/dist/ai/define.js +31 -0
- package/dist/ai/define.js.map +1 -0
- package/dist/ai/economy.d.ts +10 -7
- package/dist/ai/economy.js +780 -0
- package/dist/ai/economy.js.map +1 -0
- package/dist/ai/geometry.js +38 -0
- package/dist/ai/geometry.js.map +1 -0
- package/dist/ai/goals.d.ts +1 -0
- package/dist/ai/goals.js +560 -0
- package/dist/ai/goals.js.map +1 -0
- package/dist/ai/knowledge.d.ts +72 -7
- package/dist/ai/knowledge.js +804 -0
- package/dist/ai/knowledge.js.map +1 -0
- package/dist/ai/lanes.d.ts +10 -2
- package/dist/ai/lanes.js +715 -0
- package/dist/ai/lanes.js.map +1 -0
- package/dist/ai/melee/build.js +264 -0
- package/dist/ai/melee/build.js.map +1 -0
- package/dist/ai/melee/goals.d.ts +7 -0
- package/dist/ai/melee/goals.js +720 -0
- package/dist/ai/melee/goals.js.map +1 -0
- package/dist/ai/melee/heroes.js +68 -0
- package/dist/ai/melee/heroes.js.map +1 -0
- package/dist/ai/melee/index.d.ts +1 -1
- package/dist/ai/melee/index.js +51 -0
- package/dist/ai/melee/index.js.map +1 -0
- package/dist/ai/melee/production.js +29 -0
- package/dist/ai/melee/production.js.map +1 -0
- package/dist/ai/melee/surrender.js +64 -0
- package/dist/ai/melee/surrender.js.map +1 -0
- package/dist/ai/micro.d.ts +9 -0
- package/dist/ai/micro.js +1316 -0
- package/dist/ai/micro.js.map +1 -0
- package/dist/ai/mission.d.ts +9 -2
- package/dist/ai/mission.js +538 -0
- package/dist/ai/mission.js.map +1 -0
- package/dist/ai/missionRules.d.ts +1 -1
- package/dist/ai/missionRules.js +26 -0
- package/dist/ai/missionRules.js.map +1 -0
- package/dist/ai/module.d.ts +6 -2
- package/dist/ai/module.js +37 -0
- package/dist/ai/module.js.map +1 -0
- package/dist/ai/options.d.ts +3 -3
- package/dist/ai/plans/build.d.ts +20 -1
- package/dist/ai/plans/build.js +608 -0
- package/dist/ai/plans/build.js.map +1 -0
- package/dist/ai/plans/placement.d.ts +4 -1
- package/dist/ai/plans/placement.js +295 -0
- package/dist/ai/plans/placement.js.map +1 -0
- package/dist/ai/plans/plans.d.ts +9 -0
- package/dist/ai/plans/plans.js +110 -0
- package/dist/ai/plans/plans.js.map +1 -0
- package/dist/ai/plans/production.d.ts +14 -2
- package/dist/ai/plans/production.js +434 -0
- package/dist/ai/plans/production.js.map +1 -0
- package/dist/ai/plans/purse.d.ts +6 -3
- package/dist/ai/plans/purse.js +139 -0
- package/dist/ai/plans/purse.js.map +1 -0
- package/dist/ai/plans/tech.d.ts +6 -0
- package/dist/ai/plans/tech.js +122 -0
- package/dist/ai/plans/tech.js.map +1 -0
- package/dist/ai/profiles.d.ts +4 -4
- package/dist/{profiles-B3NS9jwk.js → ai/profiles.js} +3 -98
- package/dist/ai/profiles.js.map +1 -0
- package/dist/ai/reach.js +54 -0
- package/dist/ai/reach.js.map +1 -0
- package/dist/ai/report.d.ts +8 -0
- package/dist/ai/report.js +87 -0
- package/dist/ai/report.js.map +1 -0
- package/dist/ai/roles.d.ts +4 -0
- package/dist/ai/roles.js +207 -0
- package/dist/ai/roles.js.map +1 -0
- package/dist/ai/rules.d.ts +8 -2
- package/dist/ai/rules.js +416 -0
- package/dist/ai/rules.js.map +1 -0
- package/dist/ai/scouting.d.ts +6 -0
- package/dist/ai/scouting.js +324 -0
- package/dist/ai/scouting.js.map +1 -0
- package/dist/ai/settings.js +99 -0
- package/dist/ai/settings.js.map +1 -0
- package/dist/ai/squads.d.ts +21 -2
- package/dist/ai/squads.js +916 -0
- package/dist/ai/squads.js.map +1 -0
- package/dist/ai/strength.js +320 -0
- package/dist/ai/strength.js.map +1 -0
- package/dist/ai/task.d.ts +3 -1
- package/dist/ai/task.js +17 -0
- package/dist/ai/task.js.map +1 -0
- package/dist/ai/tasks/attack.js +24 -0
- package/dist/ai/tasks/attack.js.map +1 -0
- package/dist/ai/tasks/capture.d.ts +3 -3
- package/dist/ai/tasks/capture.js +37 -0
- package/dist/ai/tasks/capture.js.map +1 -0
- package/dist/ai/tasks/creep.js +29 -0
- package/dist/ai/tasks/creep.js.map +1 -0
- package/dist/ai/tasks/defend.js +61 -0
- package/dist/ai/tasks/defend.js.map +1 -0
- package/dist/ai/tasks/drop.js +81 -0
- package/dist/ai/tasks/drop.js.map +1 -0
- package/dist/ai/tasks/escort.js +59 -0
- package/dist/ai/tasks/escort.js.map +1 -0
- package/dist/ai/tasks/gather.js +48 -0
- package/dist/ai/tasks/gather.js.map +1 -0
- package/dist/ai/tasks/harass.js +39 -0
- package/dist/ai/tasks/harass.js.map +1 -0
- package/dist/ai/tasks/hold.js +32 -0
- package/dist/ai/tasks/hold.js.map +1 -0
- package/dist/ai/tasks/index.js +34 -0
- package/dist/ai/tasks/index.js.map +1 -0
- package/dist/ai/tasks/move.js +26 -0
- package/dist/ai/tasks/move.js.map +1 -0
- package/dist/ai/tasks/patrol.js +59 -0
- package/dist/ai/tasks/patrol.js.map +1 -0
- package/dist/ai/tasks/retreat.js +28 -0
- package/dist/ai/tasks/retreat.js.map +1 -0
- package/dist/ai/tasks/scout.js +73 -0
- package/dist/ai/tasks/scout.js.map +1 -0
- package/dist/ai/tasks/shared.d.ts +1 -1
- package/dist/ai/tasks/shared.js +137 -0
- package/dist/ai/tasks/shared.js.map +1 -0
- package/dist/ai/team.d.ts +18 -1
- package/dist/ai/team.js +492 -0
- package/dist/ai/team.js.map +1 -0
- package/dist/ai/threat.d.ts +6 -0
- package/dist/ai/threat.js +206 -0
- package/dist/ai/threat.js.map +1 -0
- package/dist/ai/view.d.ts +21 -7
- package/dist/ai/view.js +677 -0
- package/dist/ai/view.js.map +1 -0
- package/dist/ai/waves.d.ts +6 -0
- package/dist/ai/waves.js +410 -0
- package/dist/ai/waves.js.map +1 -0
- package/dist/ai.js +54 -6239
- package/dist/aiTesting.d.ts +6 -3
- package/dist/aiTesting.js +229 -0
- package/dist/aiTesting.js.map +1 -0
- package/dist/areas.d.ts +4 -1
- package/dist/areas.js +295 -0
- package/dist/areas.js.map +1 -0
- package/dist/attacks.d.ts +3 -3
- package/dist/attacks.js +127 -0
- package/dist/attacks.js.map +1 -0
- package/dist/burning.d.ts +267 -0
- package/dist/burning.js +795 -0
- package/dist/burning.js.map +1 -0
- package/dist/bytes.js +21 -0
- package/dist/bytes.js.map +1 -0
- package/dist/callings.d.ts +71 -0
- package/dist/callings.js +157 -0
- package/dist/callings.js.map +1 -0
- package/dist/cameraOrders.d.ts +78 -0
- package/dist/cameraOrders.js +114 -0
- package/dist/cameraOrders.js.map +1 -0
- package/dist/camps.d.ts +70 -4
- package/dist/camps.js +501 -0
- package/dist/camps.js.map +1 -0
- package/dist/capture.d.ts +15 -3
- package/dist/capture.js +304 -0
- package/dist/capture.js.map +1 -0
- package/dist/cellLayers.d.ts +78 -13
- package/dist/cellLayers.js +275 -0
- package/dist/cellLayers.js.map +1 -0
- package/dist/cellPatches.d.ts +24 -0
- package/dist/cellPatches.js +73 -0
- package/dist/cellPatches.js.map +1 -0
- package/dist/cinematics.d.ts +10 -5
- package/dist/cinematics.js +161 -0
- package/dist/cinematics.js.map +1 -0
- package/dist/clock.d.ts +2 -2
- package/dist/clock.js +78 -0
- package/dist/clock.js.map +1 -0
- package/dist/commands.d.ts +36 -4
- package/dist/commands.js +636 -0
- package/dist/commands.js.map +1 -0
- package/dist/components.d.ts +31 -10
- package/dist/components.js +74 -0
- package/dist/components.js.map +1 -0
- package/dist/computers.d.ts +4 -4
- package/dist/computers.js +194 -0
- package/dist/computers.js.map +1 -0
- package/dist/concealment.d.ts +25 -2
- package/dist/concealment.js +409 -0
- package/dist/concealment.js.map +1 -0
- package/dist/coreComponents.js +130 -0
- package/dist/coreComponents.js.map +1 -0
- package/dist/createGame.d.ts +306 -0
- package/dist/createGame.js +405 -0
- package/dist/createGame.js.map +1 -0
- package/dist/dashing.d.ts +18 -2
- package/dist/dashing.js +41 -0
- package/dist/dashing.js.map +1 -0
- package/dist/debug.d.ts +8 -0
- package/dist/debug.js +5 -3
- package/dist/debug2.js +76 -0
- package/dist/debug2.js.map +1 -0
- package/dist/debugCommands.d.ts +6 -6
- package/dist/debugCommands.js +537 -0
- package/dist/debugCommands.js.map +1 -0
- package/dist/debugging.d.ts +1 -0
- package/dist/defaultSets.d.ts +183 -0
- package/dist/defaultSets.js +216 -0
- package/dist/defaultSets.js.map +1 -0
- package/dist/definitions.d.ts +6 -0
- package/dist/definitions.js +78 -0
- package/dist/definitions.js.map +1 -0
- package/dist/defs.d.ts +1895 -165
- package/dist/defs.js +380 -0
- package/dist/defs.js.map +1 -0
- package/dist/denying.d.ts +4 -4
- package/dist/denying.js +106 -0
- package/dist/denying.js.map +1 -0
- package/dist/destructibles.d.ts +2 -2
- package/dist/destructibles.js +36 -0
- package/dist/destructibles.js.map +1 -0
- package/dist/determinism.d.ts +4 -4
- package/dist/determinism.js +247 -0
- package/dist/determinism.js.map +1 -0
- package/dist/dialog.d.ts +17 -2
- package/dist/dialog.js +108 -0
- package/dist/dialog.js.map +1 -0
- package/dist/effectRefs.js +28 -0
- package/dist/effectRefs.js.map +1 -0
- package/dist/effects.d.ts +27 -11
- package/dist/effects.js +110 -0
- package/dist/effects.js.map +1 -0
- package/dist/endings.d.ts +1 -1
- package/dist/endings.js +78 -0
- package/dist/endings.js.map +1 -0
- package/dist/engagement.d.ts +40 -6
- package/dist/engagement.js +530 -0
- package/dist/engagement.js.map +1 -0
- package/dist/engineEvents.d.ts +72 -9
- package/dist/engineEvents.js +520 -0
- package/dist/engineEvents.js.map +1 -0
- package/dist/entity.d.ts +275 -26
- package/dist/entity.js +441 -0
- package/dist/entity.js.map +1 -0
- package/dist/entityIndex.d.ts +10 -3
- package/dist/entityIndex.js +149 -0
- package/dist/entityIndex.js.map +1 -0
- package/dist/errors.d.ts +6 -2
- package/dist/errors.js +59 -0
- package/dist/errors.js.map +1 -0
- package/dist/events.d.ts +11 -5
- package/dist/events.js +158 -0
- package/dist/events.js.map +1 -0
- package/dist/exactMath.js +273 -0
- package/dist/exactMath.js.map +1 -0
- package/dist/features.d.ts +1 -1
- package/dist/features.js +127 -0
- package/dist/features.js.map +1 -0
- package/dist/fields.d.ts +25 -5
- package/dist/fields.js +140 -0
- package/dist/fields.js.map +1 -0
- package/dist/fixture.d.ts +3 -3
- package/dist/fixture.js +512 -0
- package/dist/fixture.js.map +1 -0
- package/dist/fog.d.ts +26 -3
- package/dist/fog.js +333 -0
- package/dist/fog.js.map +1 -0
- package/dist/foliage.d.ts +476 -0
- package/dist/foliage.js +1478 -0
- package/dist/foliage.js.map +1 -0
- package/dist/foliageDebug.d.ts +57 -0
- package/dist/foliageDebug.js +172 -0
- package/dist/foliageDebug.js.map +1 -0
- package/dist/foliageRegrowth.d.ts +20 -0
- package/dist/foliageRegrowth.js +332 -0
- package/dist/foliageRegrowth.js.map +1 -0
- package/dist/formState.d.ts +67 -0
- package/dist/formState.js +65 -0
- package/dist/formState.js.map +1 -0
- package/dist/formation.d.ts +46 -8
- package/dist/formation.js +297 -0
- package/dist/formation.js.map +1 -0
- package/dist/forms.d.ts +58 -0
- package/dist/forms.js +146 -0
- package/dist/forms.js.map +1 -0
- package/dist/friendlyFire.d.ts +2 -2
- package/dist/friendlyFire.js +37 -0
- package/dist/friendlyFire.js.map +1 -0
- package/dist/game.d.ts +141 -48
- package/dist/game.js +834 -0
- package/dist/game.js.map +1 -0
- package/dist/gameData.d.ts +96 -22
- package/dist/gameData.js +1690 -0
- package/dist/gameData.js.map +1 -0
- package/dist/gameFiles.d.ts +3 -3
- package/dist/gameFiles.js +14 -0
- package/dist/gameFiles.js.map +1 -0
- package/dist/gates.js +92 -0
- package/dist/gates.js.map +1 -0
- package/dist/generate.d.ts +28 -1
- package/dist/generate.js +1062 -0
- package/dist/generate.js.map +1 -0
- package/dist/generateWorker.d.ts +7 -3
- package/dist/generateWorker.js +86 -0
- package/dist/generateWorker.js.map +1 -0
- package/dist/grid.d.ts +9 -0
- package/dist/grid.js +102 -0
- package/dist/grid.js.map +1 -0
- package/dist/ground.d.ts +17 -1
- package/dist/ground.js +375 -0
- package/dist/ground.js.map +1 -0
- package/dist/groundObjects.d.ts +70 -0
- package/dist/groundObjects.js +371 -0
- package/dist/groundObjects.js.map +1 -0
- package/dist/groundSpeed.d.ts +20 -3
- package/dist/groundSpeed.js +117 -0
- package/dist/groundSpeed.js.map +1 -0
- package/dist/hero.d.ts +126 -10
- package/dist/hero.js +432 -0
- package/dist/hero.js.map +1 -0
- package/dist/holds.d.ts +94 -0
- package/dist/holds.js +305 -0
- package/dist/holds.js.map +1 -0
- package/dist/home.d.ts +1 -1
- package/dist/home.js +30 -0
- package/dist/home.js.map +1 -0
- package/dist/hooks.d.ts +339 -57
- package/dist/hooks.js +105 -0
- package/dist/hooks.js.map +1 -0
- package/dist/hotReload.d.ts +6 -5
- package/dist/hotReload.js +145 -0
- package/dist/hotReload.js.map +1 -0
- package/dist/ids.d.ts +3 -3
- package/dist/ids.js +30 -0
- package/dist/ids.js.map +1 -0
- package/dist/idsFile.d.ts +36 -0
- package/dist/idsFile.js +134 -0
- package/dist/idsFile.js.map +1 -0
- package/dist/index.d.ts +105 -68
- package/dist/index.js +194 -4701
- package/dist/inspect.d.ts +29 -2
- package/dist/{inspect-D9dWHmZ0.js → inspect.js} +21 -12
- package/dist/inspect.js.map +1 -0
- package/dist/inventory.d.ts +14 -1
- package/dist/inventory.js +84 -0
- package/dist/inventory.js.map +1 -0
- package/dist/items.d.ts +136 -8
- package/dist/items.js +585 -0
- package/dist/items.js.map +1 -0
- package/dist/lanes.d.ts +12 -3
- package/dist/lanes.js +496 -0
- package/dist/lanes.js.map +1 -0
- package/dist/life.d.ts +6 -2
- package/dist/life.js +287 -0
- package/dist/life.js.map +1 -0
- package/dist/log.d.ts +26 -3
- package/dist/log.js +192 -0
- package/dist/log.js.map +1 -0
- package/dist/manaBurn.d.ts +2 -2
- package/dist/manaBurn.js +75 -0
- package/dist/manaBurn.js.map +1 -0
- package/dist/map/file.d.ts +56 -3
- package/dist/map/file.js +264 -0
- package/dist/map/file.js.map +1 -0
- package/dist/map/images.js +61 -0
- package/dist/map/images.js.map +1 -0
- package/dist/map/look.d.ts +1 -1
- package/dist/map/look.js +30 -0
- package/dist/map/look.js.map +1 -0
- package/dist/map/pathProfile.js +183 -0
- package/dist/map/pathProfile.js.map +1 -0
- package/dist/map/resize.d.ts +4 -0
- package/dist/map/resize.js +136 -0
- package/dist/map/resize.js.map +1 -0
- package/dist/map/schema.js +257 -0
- package/dist/map/schema.js.map +1 -0
- package/dist/map/terrain.js +214 -0
- package/dist/map/terrain.js.map +1 -0
- package/dist/map/types.d.ts +92 -13
- package/dist/map/types.js +251 -0
- package/dist/map/types.js.map +1 -0
- package/dist/mapCheck/cells.js +94 -0
- package/dist/mapCheck/cells.js.map +1 -0
- package/dist/mapCheck/rules/foliage.d.ts +9 -0
- package/dist/mapCheck/rules/foliage.js +83 -0
- package/dist/mapCheck/rules/foliage.js.map +1 -0
- package/dist/mapCheck/rules/names.js +39 -0
- package/dist/mapCheck/rules/names.js.map +1 -0
- package/dist/mapCheck/rules/objects.d.ts +1 -1
- package/dist/mapCheck/rules/objects.js +56 -0
- package/dist/mapCheck/rules/objects.js.map +1 -0
- package/dist/mapCheck/rules/paths.js +32 -0
- package/dist/mapCheck/rules/paths.js.map +1 -0
- package/dist/mapCheck/rules/players.js +20 -0
- package/dist/mapCheck/rules/players.js.map +1 -0
- package/dist/mapCheck/rules/races.js +56 -0
- package/dist/mapCheck/rules/races.js.map +1 -0
- package/dist/mapCheck/rules/random.js +47 -0
- package/dist/mapCheck/rules/random.js.map +1 -0
- package/dist/mapCheck/rules/regions.js +30 -0
- package/dist/mapCheck/rules/regions.js.map +1 -0
- package/dist/mapCheck/rules/targets.js +31 -0
- package/dist/mapCheck/rules/targets.js.map +1 -0
- package/dist/mapCheck/rules/walking.js +45 -0
- package/dist/mapCheck/rules/walking.js.map +1 -0
- package/dist/mapCheck.d.ts +2 -2
- package/dist/mapCheck.js +63 -0
- package/dist/mapCheck.js.map +1 -0
- package/dist/mapFields.d.ts +25 -2
- package/dist/mapFields.js +134 -0
- package/dist/mapFields.js.map +1 -0
- package/dist/mapFile.js +176 -0
- package/dist/mapFile.js.map +1 -0
- package/dist/mapMeasure.d.ts +8 -0
- package/dist/mapMeasure.js +283 -0
- package/dist/mapMeasure.js.map +1 -0
- package/dist/mapMerge.d.ts +1 -0
- package/dist/mapMerge.js +172 -0
- package/dist/mapMerge.js.map +1 -0
- package/dist/mapObjectives.js +138 -0
- package/dist/mapObjectives.js.map +1 -0
- package/dist/mapObjects.js +80 -0
- package/dist/mapObjects.js.map +1 -0
- package/dist/math.d.ts +37 -7
- package/dist/math.js +155 -0
- package/dist/math.js.map +1 -0
- package/dist/messages.d.ts +71 -4
- package/dist/messages.js +29 -0
- package/dist/messages.js.map +1 -0
- package/dist/missionRules.d.ts +10 -2
- package/dist/{missionRules-C-bGxm0Q.js → missionRules.js} +30 -150
- package/dist/missionRules.js.map +1 -0
- package/dist/mods.d.ts +39 -20
- package/dist/mods.js +271 -0
- package/dist/mods.js.map +1 -0
- package/dist/moves.d.ts +87 -0
- package/dist/moves.js +539 -0
- package/dist/moves.js.map +1 -0
- package/dist/names.d.ts +7 -0
- package/dist/names.js +122 -0
- package/dist/names.js.map +1 -0
- package/dist/navigation/agentParams.js +14 -0
- package/dist/navigation/agentParams.js.map +1 -0
- package/dist/navigation/air.d.ts +7 -3
- package/dist/navigation/air.js +401 -0
- package/dist/navigation/air.js.map +1 -0
- package/dist/navigation/builds.d.ts +34 -2
- package/dist/navigation/builds.js +287 -0
- package/dist/navigation/builds.js.map +1 -0
- package/dist/navigation/groundAreas.d.ts +59 -1
- package/dist/navigation/groundAreas.js +407 -0
- package/dist/navigation/groundAreas.js.map +1 -0
- package/dist/navigation/navigation.d.ts +98 -9
- package/dist/navigation/recast.d.ts +194 -18
- package/dist/navigation/recast.js +1379 -0
- package/dist/navigation/recast.js.map +1 -0
- package/dist/navigation/sidePaths.d.ts +8 -3
- package/dist/navigation/sidePaths.js +241 -0
- package/dist/navigation/sidePaths.js.map +1 -0
- package/dist/navigation/sized.js +243 -0
- package/dist/navigation/sized.js.map +1 -0
- package/dist/nearest.js +32 -0
- package/dist/nearest.js.map +1 -0
- package/dist/noise.js +59 -0
- package/dist/noise.js.map +1 -0
- package/dist/objectives.d.ts +12 -3
- package/dist/objectives.js +144 -0
- package/dist/objectives.js.map +1 -0
- package/dist/orders/basic.d.ts +5 -5
- package/dist/orders/basic.js +417 -0
- package/dist/orders/basic.js.map +1 -0
- package/dist/orders/build.js +278 -0
- package/dist/orders/build.js.map +1 -0
- package/dist/orders/cast.d.ts +13 -3
- package/dist/orders/cast.js +558 -0
- package/dist/orders/cast.js.map +1 -0
- package/dist/orders/facts.js +16 -0
- package/dist/orders/facts.js.map +1 -0
- package/dist/orders/harvest.d.ts +21 -5
- package/dist/orders/harvest.js +540 -0
- package/dist/orders/harvest.js.map +1 -0
- package/dist/orders/idle.d.ts +3 -1
- package/dist/orders/idle.js +234 -0
- package/dist/orders/idle.js.map +1 -0
- package/dist/orders/items.js +92 -0
- package/dist/orders/items.js.map +1 -0
- package/dist/orders/queue.js +108 -0
- package/dist/orders/queue.js.map +1 -0
- package/dist/orders/waiting.d.ts +39 -0
- package/dist/orders/waiting.js +149 -0
- package/dist/orders/waiting.js.map +1 -0
- package/dist/pack.schema.js +13 -0
- package/dist/pack.schema.js.map +1 -0
- package/dist/pack.schema.json +7622 -841
- package/dist/packs/director/index.d.ts +13 -5
- package/dist/packs/director/index.js +204 -0
- package/dist/packs/director/index.js.map +1 -0
- package/dist/packs/heroes/campaign.d.ts +3 -2
- package/dist/packs/heroes/campaign.js +241 -0
- package/dist/packs/heroes/campaign.js.map +1 -0
- package/dist/packs/heroes/carry.d.ts +6 -0
- package/dist/packs/heroes/carry.js +112 -0
- package/dist/packs/heroes/carry.js.map +1 -0
- package/dist/packs/heroes/commands.js +115 -0
- package/dist/packs/heroes/commands.js.map +1 -0
- package/dist/packs/heroes/fallen.d.ts +43 -0
- package/dist/packs/heroes/fallen.js +61 -0
- package/dist/packs/heroes/fallen.js.map +1 -0
- package/dist/packs/heroes/hire.js +60 -0
- package/dist/packs/heroes/hire.js.map +1 -0
- package/dist/packs/heroes/index.d.ts +7 -6
- package/dist/packs/heroes/index.js +130 -0
- package/dist/packs/heroes/index.js.map +1 -0
- package/dist/packs/heroes/loop.d.ts +95 -0
- package/dist/packs/heroes/loop.js +250 -0
- package/dist/packs/heroes/loop.js.map +1 -0
- package/dist/packs/heroes/making.d.ts +6 -3
- package/dist/packs/heroes/making.js +55 -0
- package/dist/packs/heroes/making.js.map +1 -0
- package/dist/packs/heroes/respawn.d.ts +3 -3
- package/dist/packs/heroes/respawn.js +87 -0
- package/dist/packs/heroes/respawn.js.map +1 -0
- package/dist/packs/heroes/sets.d.ts +32 -0
- package/dist/packs/heroes/sets.js +51 -0
- package/dist/packs/heroes/sets.js.map +1 -0
- package/dist/packs/heroes/spells.d.ts +16 -0
- package/dist/packs/heroes/spells.js +269 -0
- package/dist/packs/heroes/spells.js.map +1 -0
- package/dist/packs/items/defaults.d.ts +43 -0
- package/dist/packs/items/defaults.js +1504 -0
- package/dist/packs/items/defaults.js.map +1 -0
- package/dist/packs/items/index.d.ts +27 -0
- package/dist/packs/items/index.js +214 -0
- package/dist/packs/items/index.js.map +1 -0
- package/dist/packs/moba/index.d.ts +3 -3
- package/dist/packs/moba/index.js +174 -0
- package/dist/packs/moba/index.js.map +1 -0
- package/dist/packs/pack.d.ts +15 -15
- package/dist/packs/pack.js +14 -0
- package/dist/packs/pack.js.map +1 -0
- package/dist/packs.d.ts +15 -7
- package/dist/packs.js +222 -0
- package/dist/packs.js.map +1 -0
- package/dist/paths.d.ts +2 -0
- package/dist/paths.js +109 -0
- package/dist/paths.js.map +1 -0
- package/dist/plain.js +57 -0
- package/dist/plain.js.map +1 -0
- package/dist/player.d.ts +33 -10
- package/dist/player.js +159 -0
- package/dist/player.js.map +1 -0
- package/dist/portals.d.ts +1 -1
- package/dist/portals.js +194 -0
- package/dist/portals.js.map +1 -0
- package/dist/prefabs.d.ts +25 -2
- package/dist/prefabs.js +239 -0
- package/dist/prefabs.js.map +1 -0
- package/dist/presetBuildings.js +58 -0
- package/dist/presetBuildings.js.map +1 -0
- package/dist/presetFoliage.d.ts +27 -0
- package/dist/presetFoliage.js +98 -0
- package/dist/presetFoliage.js.map +1 -0
- package/dist/presetGround.js +65 -0
- package/dist/presetGround.js.map +1 -0
- package/dist/proxies.d.ts +12 -0
- package/dist/proxies.js +79 -0
- package/dist/proxies.js.map +1 -0
- package/dist/query.d.ts +56 -0
- package/dist/query.js +88 -0
- package/dist/query.js.map +1 -0
- package/dist/races.d.ts +15 -14
- package/dist/races.js +172 -0
- package/dist/races.js.map +1 -0
- package/dist/randomGroups.js +91 -0
- package/dist/randomGroups.js.map +1 -0
- package/dist/registry.d.ts +117 -53
- package/dist/registry.js +323 -0
- package/dist/registry.js.map +1 -0
- package/dist/remains.d.ts +11 -9
- package/dist/remains.js +188 -0
- package/dist/remains.js.map +1 -0
- package/dist/replication.d.ts +12 -7
- package/dist/replication.js +100 -0
- package/dist/replication.js.map +1 -0
- package/dist/rng.d.ts +2 -0
- package/dist/rng.js +40 -0
- package/dist/rng.js.map +1 -0
- package/dist/rules.d.ts +186 -99
- package/dist/rules.js +179 -0
- package/dist/rules.js.map +1 -0
- package/dist/rulesCheck.d.ts +5 -4
- package/dist/rulesCheck.js +693 -0
- package/dist/rulesCheck.js.map +1 -0
- package/dist/runes.d.ts +2 -2
- package/dist/runes.js +80 -0
- package/dist/runes.js.map +1 -0
- package/dist/save.d.ts +37 -12
- package/dist/save.js +532 -0
- package/dist/save.js.map +1 -0
- package/dist/scenario.d.ts +2 -2
- package/dist/scenario.js +99 -0
- package/dist/scenario.js.map +1 -0
- package/dist/scheduler.d.ts +3 -0
- package/dist/scheduler.js +156 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/score.d.ts +19 -19
- package/dist/score.js +709 -0
- package/dist/score.js.map +1 -0
- package/dist/sequences.d.ts +10 -6
- package/dist/sequences.js +206 -0
- package/dist/sequences.js.map +1 -0
- package/dist/server/doorman.js +154 -0
- package/dist/server/doorman.js.map +1 -0
- package/dist/server/gameServer.d.ts +16 -1
- package/dist/server/gameServer.js +643 -0
- package/dist/server/gameServer.js.map +1 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/lobby.js +199 -0
- package/dist/server/lobby.js.map +1 -0
- package/dist/server/log.js +18 -0
- package/dist/server/log.js.map +1 -0
- package/dist/server/match.js +267 -0
- package/dist/server/match.js.map +1 -0
- package/dist/server/mode.d.ts +18 -9
- package/dist/server/mode.js +220 -0
- package/dist/server/mode.js.map +1 -0
- package/dist/{navigation-worker.js → server/navigation-worker.js} +1 -1
- package/dist/server/navigation-worker.js.map +1 -0
- package/dist/server/navigation.js +39 -0
- package/dist/server/navigation.js.map +1 -0
- package/dist/server/ops.d.ts +4 -0
- package/dist/server/ops.js +137 -0
- package/dist/server/ops.js.map +1 -0
- package/dist/server/packs.d.ts +3 -3
- package/dist/server/packs.js +32 -0
- package/dist/server/packs.js.map +1 -0
- package/dist/server/ratings.d.ts +5 -0
- package/dist/server/ratings.js +104 -0
- package/dist/server/ratings.js.map +1 -0
- package/dist/server/relay.d.ts +12 -2
- package/dist/server/relay.js +144 -0
- package/dist/server/relay.js.map +1 -0
- package/dist/server/rooms.d.ts +9 -2
- package/dist/server/rooms.js +442 -0
- package/dist/server/rooms.js.map +1 -0
- package/dist/server/seat.js +116 -0
- package/dist/server/seat.js.map +1 -0
- package/dist/server/seatSettings.d.ts +3 -3
- package/dist/server/seatSettings.js +63 -0
- package/dist/server/seatSettings.js.map +1 -0
- package/dist/server/secret.js +13 -0
- package/dist/server/secret.js.map +1 -0
- package/dist/server/start.d.ts +13 -6
- package/dist/server/start.js +76 -0
- package/dist/server/start.js.map +1 -0
- package/dist/server/websocket.d.ts +9 -1
- package/dist/server/websocket.js +306 -0
- package/dist/server/websocket.js.map +1 -0
- package/dist/server.js +15 -2861
- package/dist/session/can.js +26 -0
- package/dist/session/can.js.map +1 -0
- package/dist/session/commandReplay.d.ts +8 -2
- package/dist/session/commandReplay.js +293 -0
- package/dist/session/commandReplay.js.map +1 -0
- package/dist/session/debug.js +151 -0
- package/dist/session/debug.js.map +1 -0
- package/dist/session/delay.js +62 -0
- package/dist/session/delay.js.map +1 -0
- package/dist/session/delta.d.ts +14 -1
- package/dist/session/delta.js +407 -0
- package/dist/session/delta.js.map +1 -0
- package/dist/session/entityState.js +415 -0
- package/dist/session/entityState.js.map +1 -0
- package/dist/session/local.d.ts +47 -5
- package/dist/session/local.js +670 -0
- package/dist/session/local.js.map +1 -0
- package/dist/session/memory.d.ts +20 -2
- package/dist/session/memory.js +131 -0
- package/dist/session/memory.js.map +1 -0
- package/dist/session/protocol.d.ts +325 -31
- package/dist/session/protocol.js +55 -0
- package/dist/session/protocol.js.map +1 -0
- package/dist/session/remote.d.ts +74 -12
- package/dist/session/remote.js +1394 -0
- package/dist/session/remote.js.map +1 -0
- package/dist/session/replay.d.ts +11 -2
- package/dist/session/replay.js +160 -0
- package/dist/session/replay.js.map +1 -0
- package/dist/session/seed.js +12 -0
- package/dist/session/seed.js.map +1 -0
- package/dist/session/share.d.ts +5 -1
- package/dist/session/share.js +94 -0
- package/dist/session/share.js.map +1 -0
- package/dist/session/socket.d.ts +1 -1
- package/dist/session/socket.js +29 -0
- package/dist/session/socket.js.map +1 -0
- package/dist/session/wire.d.ts +5 -0
- package/dist/session/wire.js +84 -0
- package/dist/session/wire.js.map +1 -0
- package/dist/session/worker.d.ts +77 -12
- package/dist/session/worker.js +499 -0
- package/dist/session/worker.js.map +1 -0
- package/dist/shared.d.ts +31 -5
- package/dist/shared.js +166 -0
- package/dist/shared.js.map +1 -0
- package/dist/sides.d.ts +9 -3
- package/dist/sides.js +149 -0
- package/dist/sides.js.map +1 -0
- package/dist/sight.js +85 -0
- package/dist/sight.js.map +1 -0
- package/dist/simulate-worker.js +1 -1
- package/dist/simulate.d.ts +12 -8
- package/dist/{simulate-oXBiw1Ff.js → simulate.js} +10 -11
- package/dist/simulate.js.map +1 -0
- package/dist/smart.js +77 -0
- package/dist/smart.js.map +1 -0
- package/dist/spatial.d.ts +32 -16
- package/dist/spatial.js +234 -0
- package/dist/spatial.js.map +1 -0
- package/dist/spawning.d.ts +1 -0
- package/dist/spawning.js +224 -0
- package/dist/spawning.js.map +1 -0
- package/dist/stances.d.ts +6 -3
- package/dist/stances.js +169 -0
- package/dist/stances.js.map +1 -0
- package/dist/start.d.ts +10 -10
- package/dist/start.js +75 -0
- package/dist/start.js.map +1 -0
- package/dist/stats.d.ts +42 -16
- package/dist/stats.js +290 -0
- package/dist/stats.js.map +1 -0
- package/dist/summoned.d.ts +52 -0
- package/dist/summoned.js +38 -0
- package/dist/summoned.js.map +1 -0
- package/dist/summons.d.ts +10 -13
- package/dist/summons.js +271 -0
- package/dist/summons.js.map +1 -0
- package/dist/systems/abilities.js +224 -0
- package/dist/systems/abilities.js.map +1 -0
- package/dist/systems/combat.d.ts +35 -1
- package/dist/systems/combat.js +677 -0
- package/dist/systems/combat.js.map +1 -0
- package/dist/systems/combatTold.d.ts +3 -1
- package/dist/systems/combatTold.js +85 -0
- package/dist/systems/combatTold.js.map +1 -0
- package/dist/systems/construction.js +80 -0
- package/dist/systems/construction.js.map +1 -0
- package/dist/systems/economy.d.ts +0 -6
- package/dist/systems/economy.js +128 -0
- package/dist/systems/economy.js.map +1 -0
- package/dist/systems/fog.d.ts +1 -1
- package/dist/systems/fog.js +120 -0
- package/dist/systems/fog.js.map +1 -0
- package/dist/systems/life.js +32 -0
- package/dist/systems/life.js.map +1 -0
- package/dist/systems/movement.d.ts +24 -12
- package/dist/systems/movement.js +1247 -0
- package/dist/systems/movement.js.map +1 -0
- package/dist/systems/orders.js +64 -0
- package/dist/systems/orders.js.map +1 -0
- package/dist/systems/production.js +322 -0
- package/dist/systems/production.js.map +1 -0
- package/dist/systems/productionQueue.js +120 -0
- package/dist/systems/productionQueue.js.map +1 -0
- package/dist/systems/victory.d.ts +1 -1
- package/dist/systems/victory.js +56 -0
- package/dist/systems/victory.js.map +1 -0
- package/dist/talk.d.ts +6 -6
- package/dist/talk.js +153 -0
- package/dist/talk.js.map +1 -0
- package/dist/targeting.d.ts +142 -0
- package/dist/targeting.js +49 -0
- package/dist/targeting.js.map +1 -0
- package/dist/tech.d.ts +20 -3
- package/dist/tech.js +239 -0
- package/dist/tech.js.map +1 -0
- package/dist/techTree.d.ts +7 -0
- package/dist/techTree.js +90 -0
- package/dist/techTree.js.map +1 -0
- package/dist/terrain.d.ts +59 -7
- package/dist/terrain.js +389 -0
- package/dist/terrain.js.map +1 -0
- package/dist/terrainSave.d.ts +6 -0
- package/dist/terrainSave.js +74 -0
- package/dist/terrainSave.js.map +1 -0
- package/dist/testing.d.ts +8 -7
- package/dist/testing.js +13 -739
- package/dist/testing.js.map +1 -1
- package/dist/tiles.d.ts +1 -1
- package/dist/tiles.js +43 -0
- package/dist/tiles.js.map +1 -0
- package/dist/timers.d.ts +29 -4
- package/dist/timers.js +242 -0
- package/dist/timers.js.map +1 -0
- package/dist/tomes.d.ts +4 -4
- package/dist/tomes.js +94 -0
- package/dist/tomes.js.map +1 -0
- package/dist/trade.js +103 -0
- package/dist/trade.js.map +1 -0
- package/dist/transport.d.ts +6 -1
- package/dist/transport.js +281 -0
- package/dist/transport.js.map +1 -0
- package/dist/tunables.d.ts +10 -3
- package/dist/tunables.js +56 -0
- package/dist/tunables.js.map +1 -0
- package/dist/unitResources.d.ts +67 -0
- package/dist/unitResources.js +266 -0
- package/dist/unitResources.js.map +1 -0
- package/dist/upkeep.d.ts +6 -1
- package/dist/upkeep.js +26 -0
- package/dist/upkeep.js.map +1 -0
- package/dist/veterancy.d.ts +8 -3
- package/dist/veterancy.js +131 -0
- package/dist/veterancy.js.map +1 -0
- package/dist/vision.d.ts +36 -0
- package/dist/vision.js +133 -0
- package/dist/vision.js.map +1 -0
- package/dist/walls.d.ts +1 -1
- package/dist/walls.js +278 -0
- package/dist/walls.js.map +1 -0
- package/dist/world.d.ts +606 -88
- package/dist/world.js +1263 -0
- package/dist/world.js.map +1 -0
- package/package.json +2 -2
- package/dist/ai.js.map +0 -1
- package/dist/attributes.d.ts +0 -66
- package/dist/brain-BvVdUhX8.js +0 -7659
- package/dist/brain-BvVdUhX8.js.map +0 -1
- package/dist/builds-C6LfgvEU.js +0 -460
- package/dist/builds-C6LfgvEU.js.map +0 -1
- package/dist/coreComponents-syuh2fPG.js +0 -5149
- package/dist/coreComponents-syuh2fPG.js.map +0 -1
- package/dist/debugCommands-CzjrSm66.js +0 -14739
- package/dist/debugCommands-CzjrSm66.js.map +0 -1
- package/dist/determinism-BCymQwiE.js +0 -518
- package/dist/determinism-BCymQwiE.js.map +0 -1
- package/dist/game-DiJEN-Ks.js +0 -5800
- package/dist/game-DiJEN-Ks.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/inspect-D9dWHmZ0.js.map +0 -1
- package/dist/local-Dkovq42k.js +0 -1379
- package/dist/local-Dkovq42k.js.map +0 -1
- package/dist/missionRules-C-bGxm0Q.js.map +0 -1
- package/dist/navigation-worker.js.map +0 -1
- package/dist/packs-g5thvmGn.js +0 -3199
- package/dist/packs-g5thvmGn.js.map +0 -1
- package/dist/profiles-B3NS9jwk.js.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/simulate-oXBiw1Ff.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brain.js","names":[],"sources":["../../src/ai/brain.ts"],"sourcesContent":["import type { PlayerId } from '#engine/entity'\nimport { type Logger, rootLogger } from '#engine/log'\nimport { AI_PHASES, type AiModule, type AiModuleDef, type ModuleKey } from './module'\nimport type { AiArea, AreaKey, AreaMemories } from './area'\nimport type { Registry } from '#engine/registry'\nimport { plainParts } from '#engine/plain'\nimport { Rng } from '#engine/rng'\nimport { order } from '#engine/scheduler'\nimport type { GameSession, PlayerCommand, PlayerState, SessionEvent, Snapshot } from '#engine/session/protocol'\nimport type { AiOptions } from './options'\nimport type { BrainSeat } from '#engine/computers'\nimport { ThinkBudget, type ThinkBudgetOptions } from './budget'\nimport type { ComputerPlayer } from './computer'\nimport { type GoalDef, Goals, type PickingOptions } from './goals'\nimport { Knowledge, type KnowledgeOptions } from './knowledge'\nimport { Plans } from './plans/plans'\nimport { Purse } from './plans/purse'\nimport { type AiProfile, resolveProfile } from './profiles'\nimport { type AiDecision, type AiReport, type AiStats, Decisions, explainGoal, type KnowledgeReport, type ThreatReport } from './report'\nimport type { AiConditionTest, AiRule } from './rules'\nimport { CHURN_WINDOW, checkBrain, orderKey } from './checks'\nimport { debugChecks } from '#engine/debug'\nimport { type AiSettings, resolveSettings } from './settings'\nimport { Squads } from './squads'\nimport type { TeamBoard } from './team'\nimport type { Strength, StrengthFormula } from './strength'\nimport type { ThreatMap } from './threat'\nimport { AiView } from './view'\n\n/*\n * A brain is what a computer player is made of (AI plan, A1). It sits in a seat (`computer.ts` gives it a session of\n * its own: it sees what that player sees, and nothing else), and once in a while it thinks: it takes one picture of the\n * game (the seat's snapshot), and runs its modules over it, in order. A module is one part of a think with a name: the\n * economy, the army, a mission's rules. Each reads the picture and acts by sending commands, as a mouse would, through\n * the brain's `send`. What a module remembers between thinks is plain data kept under its id in the brain's memory,\n * which a save keeps and a load gives back (`save`, `restore`); the brain's own numbers (`rng`, seeded from the world's\n * seed and the player) are in it too, so a game loaded from a save goes on exactly as it would have.\n *\n * A brain is defined once (`defineBrain`: its id, its modules) and made once for each seat (`createBrain`), as a\n * system is made for each world: two brains of one definition never share state. A brain has its view of the game\n * (A2), its knowledge (A3), its strength and its threat map (A4), its goals (B1), its purse and its plans (B3), its\n * squads (C1), its micro (C2), its economy and its defence (C4), its scouting (C3), its team (C5), its waves (D2): each of them is a field on the brain that the modules\n * reach through the `ai` they are handed.\n *\n * A think may be sliced over ticks by its budget (`budget.ts`, F2): so many modules a tick, so many units looked at a\n * tick as the modules say, counted in work and never in milliseconds, so that the game is the same on every machine.\n * The think's picture is taken once and every tick of it reads the same one; what ran so far is kept as passes (the\n * module, the units the tick had looked at before it), which is how a save taken in the middle of a think is run on\n * from in a loaded game (`BrainMemory.thinking`).\n */\n\n/** A module definition's id, where it is known before the module is made: an object's, or a `defineModule`'s */\nconst idOf = (def: AiModuleDef): string | undefined => (typeof def === 'function' ? def.id : def.id)\n\n/**\n * What a computer player is made of, defined once (`defineBrain`) and seated as often as wanted\n * (`world.computers.seat`). A game lists the brains it seats in `Rules.brains`, so that a save that remembers a\n * seat by its brain's id finds it again when loaded\n *\n * @beta\n */\nexport interface BrainDef {\n\t/** Unique among the brains of a game, prefixed as components are (the engine's `core.melee`, a game's `mygame.raiders`): what a save remembers a seat by */\n\tid: string\n\t/** The parts of a think, in the order they run where `before` and `after` say nothing */\n\tmodules: AiModuleDef[]\n\t/**\n\t * Modules of the list left out, by id (`meleeBrain.with({ without: ['scouting', 'melee.heroes'] })`): a variant of a\n\t * brain with a part fewer. A module whose id is not known before it is made (a plain function, not `defineModule`)\n\t * cannot be left out by name\n\t */\n\twithout?: string[]\n\t/**\n\t * Rules of its own (`when(…).do(…)`, B2), checked each think by its `rules()` module: the melee brain's reads them\n\t * (`meleeBrain.with({ rules: [...] })`); a brain of one's own gives them to its `rules()` module itself. Code,\n\t * registered again with the brain\n\t */\n\trules?: AiRule[]\n\t/**\n\t * Seconds between thinks, where the seat's options say none (`AiOptions.every`): 1 when left out. A function of the\n\t * seat's options, for a brain that thinks faster on a harder difficulty\n\t */\n\tevery?: number | ((options: AiOptions) => number)\n\t/**\n\t * What its picture of the game carries (`ComputerSeatOptions.snapshot`): `lean` when left out, what the kit reads (the\n\t * entities, the players, the items, the deaths, damage, healing, pings, chat and the game's own events) and none of\n\t * what a screen needs, which is cheaper. `full` for a brain of a game's own that reads the rest (projectiles, corpses,\n\t * reveals, the other events)\n\t */\n\tsnapshot?: 'lean' | 'full'\n\t/**\n\t * How it is seated at a player (`world.computers.seat`): what `defineBrain` gives it, the seat of the engine's kit\n\t * (`createBrainSystem`, with what the map says of the player under the options). The engine's core seats a brain\n\t * through it, and loads nothing of the kit for it\n\t */\n\tseat?: BrainSeat\n\t/**\n\t * How it remembers what it saw (`Brain.knowledge`): how long, how much, what counts as the enemy's army. `false` for\n\t * a brain that remembers nothing between thinks and would rather not pay for it (a tower defense's waves)\n\t */\n\tknowledge?: KnowledgeOptions | false\n\t/**\n\t * How strong a unit is, in place of the kit's formula (`defaultStrength`): what `odds`, the threat map and\n\t * `UnitSet.strength()` are made of. The seat's `AiOptions.strength` goes over it; this one is code the game registers\n\t * again, so it is there after a load\n\t */\n\tstrength?: StrengthFormula\n\t/** Conditions of its own, by id, for the data form (`{ is: id, args }`): over the game's of the same id (`GameAi.conditions`) */\n\tconditions?: Record<string, AiConditionTest>\n\t/**\n\t * What it could do, scored each think and run with what each claims (`defineGoal`, `ai.goals`): picked by the\n\t * `goals()` module in its place among the modules, or by a module of the brain's own. Code, registered again with\n\t * the brain: there after a load, where a goal in the seat's options is not\n\t */\n\tgoals?: GoalDef[]\n\t/** How it picks among its goals where a goal does not say for itself: the least score, the margin, the commitment */\n\tpicking?: PickingOptions\n\t/**\n\t * Its own settings (`AiSettings`, keyed by area: how it plans, how its squads go and the tasks they may be on, how it\n\t * fights, runs its economy, defends, scouts, plays with its team, its waves): the bottom layer, under the game's\n\t * defaults, the difficulty, the personality, the map's and the seat's (`resolveProfile`); what its modules were made\n\t * with under all of them. Code, registered again with the brain\n\t */\n\tsettings?: AiSettings\n\t/**\n\t * This brain and more: goals, modules, rules and `without` added to its own, its settings layered over its own,\n\t * anything else in place of its own (`meleeBrain.with({ goals: [raid] })`, `meleeBrain.with({ id: 'mygame.camp', rules, without: ['scouting'] })`).\n\t * Given by `defineBrain`\n\t */\n\twith?(more: BrainExtension): BrainDef\n}\n\n/**\n * What `BrainDef.with` adds to a brain: goals, modules, rules and modules left out beside its own, settings over its\n * own, the rest in place of its own\n *\n * @beta\n */\nexport type BrainExtension = Partial<Omit<BrainDef, 'with'>>\n\n/**\n * A word the game told a computer player (`world.computers.tell`), waiting for a module to take it\n *\n * @beta\n */\nexport interface Told {\n\t/** The word: what the game and the brain agreed on (`'raid'`) */\n\tword: string\n\t/** What came with it: plain data */\n\tdata?: unknown\n\t/** The tick it was told at */\n\ttick: number\n}\n\n/**\n * What a brain remembers between thinks, as a save keeps it: its own numbers, each module's memory with its version,\n * the words it was told and not yet taken\n *\n * @beta\n */\nexport interface BrainMemory {\n\t/** The version of this shape */\n\tv: 2\n\t/** Where the brain's own numbers are in their sequence (`Rng.save`) */\n\trng: number\n\t/** How many times it has thought: what `AiModule.everyThinks` counts in */\n\tthinks: number\n\t/** Whether it sits still (`world.computers.pause`) */\n\tpaused?: boolean\n\t/** The words it was told and no module has taken yet */\n\ttold: Told[]\n\t/** What each module remembers, under its id, with the version it was saved as */\n\tmodules: Record<string, { v: number; data: unknown }>\n\t/**\n\t * What each of its areas remembers, by the area's id (`AiArea`): its knowledge, goals, plans and squads, its economy,\n\t * scouting and the rest of the kit's, a game's own. Left out by an area with nothing to remember\n\t */\n\tareas?: AreaMemories\n\t/** What it decided last (`Brain.decisions`): the last fifty lines, for whoever reads a save */\n\tdecisions?: AiDecision[]\n\t/**\n\t * The orders it sent lately (`Brain.sentTo`), by unit: what keeps it from telling a unit again what it is doing, in a\n\t * game loaded from the save too. Left out while there are none\n\t */\n\torders?: { unit: number; key: string; tick: number }[]\n\t/**\n\t * What its seat heard since it last thought (`GameSession.pendingEvents`): the damage, deaths and pings its next think\n\t * reads, which a game loaded from the save would not have heard otherwise\n\t */\n\theard?: SessionEvent[]\n\t/**\n\t * A think under way across ticks as the game was saved (`BrainContext.budget`): the rest of this memory is the brain\n\t * as that think began, and this is how far it had got, over what picture: each pass run so far, as the module (its\n\t * index), the units the tick had looked at before it (`ThinkBudget.units`) and how many words it had been told by then\n\t * (`told`). A game loaded from the save runs the\n\t * same passes again under the same budget over the same picture, sending nothing (their commands are in the game\n\t * already), and goes on from there as the saved game did. `slice` is the form before passes: so many modules run once each\n\t */\n\tthinking?: { snapshot: Snapshot; passes?: [module: number, units: number, heard: number][]; slice?: number }\n}\n\n/**\n * What follows a brain's play from outside it (`Brain.watch`): the commands it sends, its thinks as each ends, what it\n * decides. For tests and tools (`watchComputer`, `recordDecisions`, the invariants of `ai/checks.ts`): a watcher reads,\n * it never changes what the brain does\n *\n * @beta\n */\nexport interface BrainWatcher {\n\t/** A command it sent, as it goes (`Brain.send`): not one held back past its profile's cap */\n\tsent?(command: PlayerCommand, brain: Brain): void\n\t/** A think ended, all its ticks where it was sliced: the picture it read is still `brain.view` */\n\tthought?(brain: Brain): void\n\t/** It wrote a decision down (`Brain.decide`) */\n\tdecided?(decision: AiDecision, brain: Brain): void\n}\n\n/**\n * What a seat hands a brain as it is made, besides its session and its options (`createComputerSystem`'s `make`)\n *\n * @beta\n */\nexport interface BrainContext {\n\t/** What the brain's own numbers start from: the world's seed and the player, as the seat works it out */\n\tseed?: number\n\t/**\n\t * How much of a think one tick may do (`ThinkBudgetOptions`: modules a tick, units looked at a tick, the milliseconds a\n\t * module is warned over), or the modules a tick as a number. A think with more goes on at the next tick, over the same\n\t * picture of the game, so that no tick does all of it. Everything in one tick when left out\n\t */\n\tbudget?: number | ThinkBudgetOptions\n\t/** Whether to time each module this think: on while the world's scheduler profiles. Timing never decides anything */\n\tprofiling?: () => boolean\n\t/**\n\t * The board of its team (`TeamBoard`), shared by the computers of one team and kept by the world (`core.ai.teams`):\n\t * read anew each time, for a load replaces it. A brain with none plays alone\n\t */\n\tteam?: () => TeamBoard\n\t/**\n\t * Whether every think is checked against the invariants (`checkBrain`): its world's (`world.checks.ai`), as its seat\n\t * says. The process's (`debugChecks.ai`) for a brain made by hand\n\t */\n\tchecks?: boolean\n}\n\n/**\n * What of what its seat heard since it last thought a save keeps (`BrainMemory.heard`): the facts a brain reads (deaths,\n * damage, healing, pings, chat, the game's own events). What is for the screen is left out: a builder's hammer blow\n * comes of a timer no save keeps, and the answers to its own commands name the seat's numbering of them, which a loaded\n * game's seat starts afresh\n */\nconst HEARD: ReadonlySet<SessionEvent['type']> = new Set(['death', 'damage', 'heal', 'ping', 'chat', 'game'])\n\n/** How much of each new think goes into a module's averaged time. Small: the panel shows a steady number */\nconst SMOOTHING = 0.1\n\n/** A timing averaged over thinks, the new one weighed in: the first stands as it is */\nconst averaged = (before: number | undefined, took: number): number => (before === undefined ? took : before + (took - before) * SMOOTHING)\n\n/**\n * A computer player made of modules (`defineBrain`), seated at a world (`world.computers.seat`). What\n * a module is handed as it thinks (`ai`): the picture of the game (`snapshot`), the seat it plays through (`session`),\n * its options, its numbers (`rng`), its log, and `send` for its commands. A brain is a `ComputerPlayer`: `think`, and\n * `save` and `restore` for what it remembers\n *\n * @beta\n */\nexport class Brain implements ComputerPlayer {\n\t/** Its modules, in the order they run */\n\treadonly modules: readonly AiModule[]\n\t/**\n\t * Its own numbers, for whatever it leaves to chance (which base to scout first): seeded from the world's and the\n\t * player's, kept in its memory, so that the same game plays the same way. Never `Math.random`\n\t */\n\treadonly rng: Rng\n\t/** Where it says what it does: the world's log, with the player it plays and its brain on each line */\n\treadonly log: Logger\n\t/** Whether it sits still (`world.computers.pause`). Kept in a save */\n\tpaused = false\n\t/**\n\t * What it sees, as of its last think: the snapshot of its seat, plain data, and the only input a brain has. Taken once\n\t * a think: every module of the think reads the same picture\n\t */\n\tsnapshot: Snapshot\n\t/**\n\t * Its reading of the picture (`AiView`): the snapshot indexed once a think and asked as sets (`view.mine.idle()`,\n\t * `view.enemies.near(home, 20)`), with what unit types are for (`view.roles`) and the map's facts. What a module\n\t * reads the game through\n\t */\n\tview: AiView\n\t/**\n\t * What it remembers of the enemy, the map and itself, with when (`Knowledge`): their bases and army as last seen,\n\t * when each place was last looked at, its own losses and income. Brought up to date as each think begins, after the\n\t * view; kept in a save\n\t */\n\treadonly knowledge: Knowledge\n\t/**\n\t * What it could do (`Goals`): its goals scored each think from their considerations, the best given what they\n\t * claim of its units and money, each run while it holds its claim. Its definition's goals and the seat's; a module\n\t * adds its own (`ai.goals.add`). Picked by the `goals()` module, or by one of the brain's own in its place\n\t */\n\treadonly goals: Goals\n\t/**\n\t * What it has to spend this think (`Purse`): its resources less what builders on their way will pay and what is\n\t * held (a goal's money claim, a want saving up), rebuilt from the picture as each think begins. The one ledger:\n\t * everything that spends, spends through it. `view.me.purse` is what is free\n\t */\n\treadonly purse: Purse\n\t/**\n\t * Its plans (`Plans`): the build plan (`plans.build`: wants, the steps to each through the tech tree, spots,\n\t * builders, saving up), the production plan (`plans.production`: the composition, counters, supply ahead,\n\t * research), the game's build order over both, and `plans.want` to add to them. Run by the `build()` and\n\t * `production()` modules, or by one of the brain's own\n\t */\n\treadonly plans: Plans\n\t/**\n\t * Its squads (`Squads`): named groups of its units, each with a task (attack there, defend here, gather and go), made\n\t * by its goals, rules and modules out of what they hold, and watched by the `squads()` module think after think:\n\t * orders sent once and watched, regroups, retreats on the odds, reinforcements. Kept in a save\n\t */\n\treadonly squads: Squads\n\t/**\n\t * How it plays (`AiProfile`), resolved (`resolveProfile`): how well it plays (the engine's, the game's, the\n\t * difficulty's, the personality's, the seat's `every` and `budget`) and its settings, the brain's, the game's, the\n\t * difficulty's, the personality's, the map's and the seat's each over the one before (a flag's function left out)\n\t */\n\treadonly profile: AiProfile\n\t/** Its settings' layers, lowest first (`ResolvedProfile.layers`): what `settings` is worked out from */\n\tprivate readonly layers: readonly AiSettings[]\n\t/** How many times it has thought */\n\tthinks = 0\n\t/**\n\t * What it decided, the last fifty (`decide`): the goals picked and stopped, with the reason that weighed most, and\n\t * what its modules write down. Kept in a save\n\t */\n\treadonly decisions = new Decisions()\n\t/** The board of its team, shared by its team's computers: what its seat was given (`BrainContext.team`), for its team play */\n\treadonly teamBoard: BrainContext['team']\n\t/** Its areas, in the order they were made (`area`) */\n\tprivate readonly areaList: AiArea[] = []\n\t/** What a save remembered of areas not made yet, by id: taken by each as a module first asks for it */\n\tprivate waiting: AreaMemories = {}\n\t/** What was worked out for this think, by key (`cache`): thrown away with the picture */\n\tprivate readonly memo = new Map<string, unknown>()\n\n\t/**\n\t * How strong things are (`Strength`): a unit, a group, one force against another (`odds`), what answers what\n\t * (`counterFor`). The view's: the kit's formula unless the brain or the seat was given one\n\t */\n\tget strength(): Strength {\n\t\treturn this.view.strength\n\t}\n\n\t/**\n\t * Where the strength is (`ThreatMap`): the friendly and the enemy strength that reaches each cell of a coarse grid\n\t * over the map, by layer. Made from the think's picture when first asked; `threat.at(undefined)` is all zeros\n\t */\n\tget threat(): ThreatMap {\n\t\treturn this.view.threat\n\t}\n\t/** Commands it sent this think */\n\tsent = 0\n\t/** Commands it would have sent this think past what its profile lets a think send (`commandsPerThink`): not sent */\n\theld = 0\n\t/**\n\t * What its play adds up to since it was seated (`AiStats`): commands sent, held and refused, seconds its fighters\n\t * stood idle, orders sent again for the same thing, seconds its building waited on money. For the report and\n\t * `simulate`, never for a decision; not kept in a save\n\t */\n\treadonly stats: AiStats = { commands: 0, held: 0, refused: 0, idleArmySeconds: 0, churn: 0, blockedOnMoney: 0, thinks: 0 }\n\t/** The tick of the think before, for what is counted by the seconds between thinks */\n\tprivate lastThink: number | undefined\n\t/** The last order each unit was sent, with the tick: what an order sent again for the same thing is counted against (`AiStats.churn`) */\n\tprivate readonly lastOrders = new Map<number, { key: string; tick: number }>()\n\t/** The tick each unit was last told to cast: an order told again after a cast between is not counted as churn */\n\tprivate readonly castAt = new Map<number, number>()\n\t/**\n\t * How much of a think one tick may do (`ThinkBudget`): so many modules, so many units looked at as the modules say\n\t * (`budget.spent(n)`), the think going on at the next tick from where it stopped (`budget.later()`). Counted in work,\n\t * never in milliseconds, so that the game is the same on every machine. What the seat was given (`BrainContext.budget`)\n\t */\n\treadonly budget: ThinkBudget\n\t/** Milliseconds each module took, averaged over thinks, by id: while the world profiles (`BrainContext.profiling`) */\n\treadonly moduleTiming: Record<string, number> = {}\n\t/** The most milliseconds each module took in one pass, by id: always, as the warning (`ThinkBudgetOptions.warnAfterMs`) is */\n\treadonly moduleLongest: Record<string, number> = {}\n\t/** Milliseconds taking the picture of the game took (the seat's `snapshot()`), averaged: while the world profiles */\n\tsnapshotTiming: number | undefined\n\t/** Milliseconds reading the picture into the view took (`AiView`, the ledger with it), averaged: while the world profiles */\n\tviewTiming: number | undefined\n\t/** Milliseconds bringing the knowledge up to date took, averaged: while the world profiles */\n\tknowledgeTiming: number | undefined\n\t/** Ticks the last think took, all its passes: 1 unless its budget sliced it */\n\tthinkTicks = 0\n\t/** Ticks of the think under way so far */\n\tprivate ticksSoFar = 0\n\t/** The modules the log has warned of taking over `warnAfterMs`, once each */\n\tprivate readonly warned = new Set<string>()\n\t/**\n\t * The passes of the think under way: each module run, with the units the tick had looked at before it. What a save in\n\t * the middle of the think keeps (`BrainMemory.thinking`), kept only where the budget may slice\n\t */\n\tprivate passes: [module: number, units: number, heard: number][] = []\n\tprivate heard: Told[] = []\n\t/** Who follows its play from outside (`watch`): none in a game, so nothing is paid for it there */\n\tprivate watchers: BrainWatcher[] = []\n\t/** The module a think under way goes on from, at the next tick: none between thinks (`budget`) */\n\tprivate slice: number | undefined\n\t/**\n\t * What it remembered as the think under way began, for a think sliced across ticks: what a save taken before the\n\t * think ends keeps, with how far it got (`BrainMemory.thinking`)\n\t */\n\tprivate began: BrainMemory | undefined\n\t/** The words told it since the think under way began: a save taken before it ends keeps them beside what it began with */\n\tprivate toldDuring: Told[] = []\n\t/** A think a save was taken in the middle of: run again to where it was, then on, at the next tick (`restore`) */\n\tprivate resume: { passes: [module: number, units: number, heard: number][]; snapshot: Snapshot } | undefined\n\t/** While the modules a loaded game had run already are run again: nothing is sent, said or watched */\n\tprivate replaying = false\n\t/** While a pass is run again: how many of the words it was told the pass had heard then, the rest told it after */\n\tprivate replayHeard: number | undefined\n\tprivate readonly profiling: () => boolean\n\n\tconstructor(\n\t\t/** What it was made from */\n\t\treadonly def: BrainDef,\n\t\t/** The seat it plays through: the snapshots it sees, the commands it sends */\n\t\treadonly session: GameSession,\n\t\t/** What it was seated with: a brain's own options read theirs from it */\n\t\treadonly options: AiOptions,\n\t\tcontext: BrainContext = {}\n\t) {\n\t\tthis.rng = new Rng(context.seed ?? 1)\n\t\tthis.log = (session.log ?? rootLogger).child(() => ({ fields: { ai: session.playerId, brain: def.id } }))\n\t\tthis.snapshot = session.snapshot()\n\t\t// the race it plays as it is seated: the game's layer for it (`GameAi.races`)\n\t\tconst race = this.snapshot.players.find((player) => player.id === session.playerId)?.race\n\t\tconst { profile, layers, unknown } = resolveProfile(options, session.registry, def.settings, race)\n\t\tthis.profile = profile\n\t\tthis.layers = layers\n\t\tif (unknown.length)\n\t\t\tthis.log.warn('ai', `the computer player is seated with a ${unknown.join(' and a ')} the game does not have: the defaults stand in`)\n\t\tthis.budget = new ThinkBudget(context.budget)\n\t\tthis.profiling = context.profiling ?? (() => false)\n\t\tthis.view = new AiView(this.snapshot, session, { roles: options.roles, strength: options.strength ?? def.strength })\n\t\tthis.knowledge = new Knowledge(def.knowledge || {}, session.terrain, def.knowledge !== false)\n\t\tthis.goals = new Goals(this, def.picking)\n\t\tthis.goals.add(...(def.goals ?? []))\n\t\tthis.purse = new Purse(session.registry)\n\t\tthis.purse.begin(this.view)\n\t\tthis.plans = new Plans(this, profile.plans ?? {})\n\t\tthis.squads = new Squads(this, profile.squads?.tasks)\n\t\tthis.teamBoard = context.team\n\t\t// what every module shares, kept and reported as every area is: its other areas are made as a module asks (`area`)\n\t\tfor (const area of [this.knowledge, this.goals, this.plans, this.squads]) this.register(area)\n\t\t// the seat's goals, beside its definition's\n\t\tthis.goals.add(...(options.goals ?? []))\n\t\t// the modules left out by name (`without`) are never made: a module made configures the brain as it is made\n\t\tconst left = new Set(def.without ?? [])\n\t\tconst kept = def.modules.filter((module) => {\n\t\t\tconst id = idOf(module)\n\t\t\treturn id === undefined || !left.has(id)\n\t\t})\n\t\tfor (const id of left)\n\t\t\tif (!def.modules.some((module) => idOf(module) === id))\n\t\t\t\tthis.log.warn('ai', `the brain \"${def.id}\" leaves out a module \"${id}\" it does not have`)\n\t\tconst made = kept.map((module) => (typeof module === 'function' ? module(this) : module))\n\t\tconst ids = new Set<string>()\n\t\tfor (const module of made) {\n\t\t\tif (ids.has(module.id)) throw new Error(`The brain \"${def.id}\" has two modules called \"${module.id}\"`)\n\t\t\tids.add(module.id)\n\t\t}\n\t\t// in their order: phase by phase (one with none in the phase of the one listed before it), as listed within each,\n\t\t// then by what each says it comes before and after\n\t\tlet phase = 0\n\t\tconst phased = made.map((module, listed) => {\n\t\t\tif (module.phase) phase = AI_PHASES.indexOf(module.phase)\n\t\t\treturn { module, phase, listed }\n\t\t})\n\t\tphased.sort((a, b) => a.phase - b.phase || a.listed - b.listed)\n\t\tthis.modules = order(phased.map(({ module }) => ({ name: module.id, before: module.before, after: module.after, module }))).map(\n\t\t\t(each) => each.module\n\t\t)\n\t\t// the engine's own checks on (tests, development): every think checked against the invariants, the first broken one thrown\n\t\tif (context.checks ?? debugChecks.ai) checkBrain(this)\n\t}\n\n\t/**\n\t * An area of its (`AiArea`): its economy (`ai.area(Economy)`), its scouting, a game's own (`defineArea`), made the\n\t * first time it is asked for, with what a save remembered of it. What its modules share beyond what every module\n\t * does (`knowledge`, `goals`, `plans`, `squads`, `purse`)\n\t */\n\tarea<T extends AiArea>(key: AreaKey<T>): T {\n\t\tconst known = this.areaList.find((area) => area.id === key.id)\n\t\tif (known) return known as T\n\t\tconst made = key.make(this)\n\t\tif (made.id !== key.id) throw new Error(`The area \"${key.id}\" made an area called \"${made.id}\"`)\n\t\tthis.register(made)\n\t\treturn made\n\t}\n\n\t/**\n\t * An area made already, by id: none where no module has asked for it. For a part of the kit that reads another's\n\t * area where there is one and needs nothing of it where there is none (whether a squad's fights are the micro's)\n\t */\n\tmade<T extends AiArea>(id: string): T | undefined {\n\t\treturn this.areaList.find((area) => area.id === id) as T | undefined\n\t}\n\n\t/** Its areas, in the order they were made: what its memory and its report are made of */\n\tareas(): readonly AiArea[] {\n\t\treturn this.areaList\n\t}\n\n\t/** Puts an area among its own, with what a save remembered of it */\n\tprivate register(area: AiArea): void {\n\t\tthis.areaList.push(area)\n\t\tif (!(area.id in this.waiting)) return\n\t\tconst memory = this.waiting[area.id]\n\t\tdelete this.waiting[area.id]\n\t\tif (!area.restore(memory))\n\t\t\tthis.log.info('ai', area.afresh ?? `the save remembers the computer player's ${area.id} in a shape this brain does not read: it starts afresh`)\n\t}\n\n\t/**\n\t * Follows its play from outside (`BrainWatcher`): the commands it sends, its thinks as each ends, what it decides.\n\t * Returns the way to stop. What `watchComputer` and `recordDecisions` of `@v12-engine/engine/testing` stand on\n\t */\n\twatch(watcher: BrainWatcher): () => void {\n\t\tthis.watchers = [...this.watchers, watcher]\n\t\treturn () => {\n\t\t\tthis.watchers = this.watchers.filter((each) => each !== watcher)\n\t\t}\n\t}\n\n\t/** The player it plays */\n\tget playerId(): PlayerId {\n\t\treturn this.session.playerId\n\t}\n\n\t/** The game's definitions, as the seat has them */\n\tget registry(): Registry {\n\t\treturn this.session.registry\n\t}\n\n\t/** The tick of its picture of the game */\n\tget tick(): number {\n\t\treturn this.snapshot.tick\n\t}\n\n\t/** Seconds into the game, as of its picture of it */\n\tget time(): number {\n\t\treturn this.snapshot.tick / this.session.tickRate\n\t}\n\n\t/** The player it plays, as its picture shows them: resources, supply, tech (`own`). Nothing once out of the game. `view.me` is the same, never nothing */\n\tget me(): PlayerState | undefined {\n\t\treturn this.view.player(this.session.playerId)\n\t}\n\n\t/** A module of its, by key with its shape (`MELEE_ARMY`, `moduleKey`), or by id */\n\tmodule<M extends AiModule>(key: ModuleKey<M>): M | undefined\n\t/** Finds one of the brain's modules by id, or nothing when it has none of that id */\n\tmodule(id: string): AiModule | undefined\n\tmodule(key: string | ModuleKey<AiModule>): AiModule | undefined {\n\t\tconst id = typeof key === 'string' ? key : key.id\n\t\treturn this.modules.find((module) => module.id === id)\n\t}\n\n\t/**\n\t * What its areas play by this think (`AiSettings`): its settings' layers worked out, a flag that is a function of the\n\t * brain asked now. Each area reads its own (`ai.settings.economy`), over what its module was made with\n\t */\n\tget settings(): AiSettings {\n\t\treturn this.cache('settings', () => resolveSettings(this.layers, this))\n\t}\n\n\t/**\n\t * What was worked out once for this think, by key: a consideration asked by several goals, a set a few modules\n\t * read (`ai.cache('intruders', () => ...)`). Thrown away as the next think takes its picture\n\t */\n\tcache<T>(key: string, compute: () => T): T {\n\t\tif (this.memo.has(key)) return this.memo.get(key) as T\n\t\tconst value = compute()\n\t\tthis.memo.set(key, value)\n\t\treturn value\n\t}\n\n\t/** Whether a think is under way across ticks (`budget`): the seat asks it to think on at the next tick */\n\tget busy(): boolean {\n\t\treturn this.slice !== undefined || this.resume !== undefined\n\t}\n\n\t/**\n\t * One think, or one tick of it. A new picture of the game is taken, and the modules run over it in their order, as\n\t * many a tick as the budget allows (`budget`): so many modules, so many units looked at as they say. A module's error\n\t * is the seat's to catch, as a system's is\n\t */\n\tthink(): void {\n\t\tif (this.paused) return\n\t\tconst resume = this.resume\n\t\tthis.resume = undefined\n\t\tif (this.slice === undefined) {\n\t\t\tconst timed = this.profiling()\n\t\t\tconst start = timed ? performance.now() : 0\n\t\t\t// the one picture of the game this think reads: what the seat's session cuts to its fog. Timed apart, for\n\t\t\t// it is the part of a think the brain does not choose (F1); a think a loaded game resumes reads the picture it began with\n\t\t\tconst snapshot = resume?.snapshot ?? this.session.snapshot()\n\t\t\tconst taken = timed ? performance.now() : 0\n\t\t\tif (timed) this.snapshotTiming = averaged(this.snapshotTiming, taken - start)\n\t\t\tif (snapshot.outcome !== 'playing') return\n\t\t\tthis.snapshot = snapshot\n\t\t\tthis.memo.clear()\n\t\t\t// the picture read once, for every module: what cost and did not change (the roles) comes over from the last\n\t\t\tconst before = this.view\n\t\t\tthis.view = new AiView(snapshot, this.session, { before, roles: this.options.roles, strength: this.options.strength ?? this.def.strength })\n\t\t\tconst read = timed ? performance.now() : 0\n\t\t\t// and remembered: what the picture before says of this one's dead, what is out of sight now and was not. A think a\n\t\t\t// loaded game goes on with remembered its picture already: its knowledge is as the saved game's was, with the picture read\n\t\t\tif (this.def.knowledge !== false) {\n\t\t\t\tif (resume) this.knowledge.resume(this.view)\n\t\t\t\telse this.knowledge.update(this.view, before)\n\t\t\t\tthis.view.me.income = this.knowledge.own.incomes()\n\t\t\t}\n\t\t\tconst remembered = timed ? performance.now() : 0\n\t\t\tif (timed && this.def.knowledge !== false) this.knowledgeTiming = averaged(this.knowledgeTiming, remembered - read)\n\t\t\t// the ledger of the think: what is free, less what the goals hold from the think before\n\t\t\tthis.purse.begin(this.view)\n\t\t\tconst holds = this.goals.all()\n\t\t\tfor (const id in holds) if (holds[id].money) this.purse.reserve(id, holds[id].money!)\n\t\t\tthis.view.me.purse = this.purse.free\n\t\t\t// a think a loaded game goes on with was counted by the saved game\n\t\t\tif (!resume) {\n\t\t\t\tthis.thinks++\n\t\t\t\tthis.count()\n\t\t\t}\n\t\t\tthis.sent = 0\n\t\t\tthis.held = 0\n\t\t\tthis.slice = 0\n\t\t\tthis.passes = []\n\t\t\tthis.ticksSoFar = 0\n\t\t\tif (timed) this.viewTiming = averaged(this.viewTiming, read - taken + (performance.now() - remembered))\n\t\t\t// a think its budget may slice over ticks remembers how it began, for a save taken before it ends: the brain as it\n\t\t\t// stands here, its picture read and remembered and nothing run yet, which is what the passes are run again over\n\t\t\tif (this.budget.slices) {\n\t\t\t\tthis.began = this.memory()\n\t\t\t\tthis.toldDuring = []\n\t\t\t}\n\t\t\tif (resume) {\n\t\t\t\t// the passes the saved game had run this think, again, over the same picture, memory and budget, each hearing the\n\t\t\t\t// words it heard then: what they sent is in the game already, and what they wrote down in the save's log; so\n\t\t\t\t// nothing goes out, and they leave the brain as they did\n\t\t\t\tthis.replaying = true\n\t\t\t\ttry {\n\t\t\t\t\tfor (const [module, units, heard] of resume.passes) {\n\t\t\t\t\t\tthis.slice = module\n\t\t\t\t\t\tthis.budget.begin(units)\n\t\t\t\t\t\tthis.replayHeard = heard\n\t\t\t\t\t\tthis.pass(module)\n\t\t\t\t\t}\n\t\t\t\t} finally {\n\t\t\t\t\tthis.replaying = false\n\t\t\t\t\tthis.replayHeard = undefined\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tthis.ticksSoFar++\n\t\tthis.budget.begin()\n\t\tlet ran = 0\n\t\tlet at = this.slice as number\n\t\twhile (at < this.modules.length && ran < this.budget.modulesPerTick && this.budget.room) {\n\t\t\tconst module = this.modules[at]\n\t\t\tconst every = module.everyThinks ?? 1\n\t\t\t// `thinks` counts this one already: the first think is round 0. A module not run this think costs the budget nothing\n\t\t\tif (every !== 1 && (this.thinks - 1) % every !== (module.offset ?? 0) % every) {\n\t\t\t\tat++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tran++\n\t\t\t// a module that stops for the tick ends it: it goes on first at the next\n\t\t\tconst more = this.pass(at)\n\t\t\tat = this.slice as number\n\t\t\tif (!more) break\n\t\t}\n\t\tthis.slice = at\n\t\tif (at >= this.modules.length) {\n\t\t\tthis.slice = undefined\n\t\t\tthis.began = undefined\n\t\t\tthis.passes = []\n\t\t\tthis.thinkTicks = this.ticksSoFar\n\t\t\t// what the think came to: the seconds since the last one spent waiting on money, where its building was\n\t\t\tif (this.plans.build.savingFor !== undefined) this.stats.blockedOnMoney += this.sinceLastThink()\n\t\t\tthis.lastThink = this.tick\n\t\t\tfor (const watcher of this.watchers) watcher.thought?.(this)\n\t\t}\n\t}\n\n\t/**\n\t * One pass of a module: run once, timed, and the think moved on past it, unless it stopped for the tick with more to\n\t * do (`budget.later()`), in which case it is where the think goes on at the next tick. Gives whether the think may go\n\t * on this tick\n\t */\n\tprivate pass(index: number): boolean {\n\t\tconst module = this.modules[index]\n\t\tconst { budget } = this\n\t\tconst before = budget.units\n\t\tif (budget.slices) this.passes.push([index, before, this.heard.length])\n\t\tbudget.deferred = false\n\t\tconst start = performance.now()\n\t\tmodule.think(this)\n\t\tconst took = performance.now() - start\n\t\tif (!(took <= (this.moduleLongest[module.id] ?? 0))) this.moduleLongest[module.id] = took\n\t\tif (this.profiling()) this.moduleTiming[module.id] = averaged(this.moduleTiming[module.id], took)\n\t\t// a module over its time is said once, and never cut: the budget is work, so that the game is the same everywhere\n\t\tif (budget.warnAfterMs !== undefined && took > budget.warnAfterMs && !this.warned.has(module.id) && !this.replaying) {\n\t\t\tthis.warned.add(module.id)\n\t\t\tthis.log.warn('ai', `the module \"${module.id}\" took ${took.toFixed(1)} ms, over ${budget.warnAfterMs}: said once`, {\n\t\t\t\tmodule: module.id,\n\t\t\t\tms: Math.round(took * 100) / 100\n\t\t\t})\n\t\t}\n\t\tif (budget.deferred) {\n\t\t\t// honoured where it looked at something this tick: one that did not and asks to go on would never end\n\t\t\tif (budget.units > before) return false\n\t\t\tif (!this.replaying && !this.warned.has(`later:${module.id}`)) {\n\t\t\t\tthis.warned.add(`later:${module.id}`)\n\t\t\t\tthis.log.warn('ai', `the module \"${module.id}\" asked to go on at the next tick having looked at nothing: taken to be done, said once`, {\n\t\t\t\t\tmodule: module.id\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tthis.slice = index + 1\n\t\treturn budget.room\n\t}\n\n\t/** Seconds between the think before and this one's picture: 0 for the first think */\n\tprivate sinceLastThink(): number {\n\t\treturn this.lastThink === undefined ? 0 : (this.tick - this.lastThink) / this.session.tickRate\n\t}\n\n\t/**\n\t * Counts, as a think begins, what its play adds up to (`stats`): the fighters that stood idle since the last think,\n\t * the commands the game refused since, and the orders it no longer needs to remember for the churn\n\t */\n\tprivate count(): void {\n\t\tconst { stats, view } = this\n\t\tstats.thinks++\n\t\tconst seconds = this.sinceLastThink()\n\t\tif (seconds > 0) stats.idleArmySeconds += view.mine.fighters().idle().length * seconds\n\t\tfor (const event of view.events.of('command')) if (event.refusal) stats.refused++\n\t\tconst forgotten = this.tick - CHURN_WINDOW * this.session.tickRate\n\t\tfor (const [id, last] of this.lastOrders) if (last.tick < forgotten || !view.mine.has(id)) this.lastOrders.delete(id)\n\t\tfor (const [id, tick] of this.castAt) if (tick < forgotten || !view.mine.has(id)) this.castAt.delete(id)\n\t}\n\n\t/**\n\t * Sends a command, as the player would: counted (`sent`), carried out at the next tick. Returns the command's number;\n\t * -1 for one past what its profile lets a think send (`commandsPerThink`, a person's hands), which is not sent\n\t */\n\tsend(command: PlayerCommand): number {\n\t\t// run again in a loaded game: what it sent is in the game already, and what it remembers of it (`sentTo`) is remembered again\n\t\tif (this.replaying) {\n\t\t\tthis.sent++\n\t\t\tthis.churn(command)\n\t\t\treturn -1\n\t\t}\n\t\tconst cap = this.profile.commandsPerThink\n\t\tif (cap !== undefined && this.sent >= cap) {\n\t\t\tthis.held++\n\t\t\tthis.stats.held++\n\t\t\treturn -1\n\t\t}\n\t\tthis.sent++\n\t\tthis.stats.commands++\n\t\tthis.churn(command)\n\t\tfor (const watcher of this.watchers) watcher.sent?.(command, this)\n\t\treturn this.session.send(command)\n\t}\n\n\t/**\n\t * Counts an order sent to a unit for the same thing as its last within a few seconds (`AiStats.churn`): a unit told\n\t * to attack-move there every think never arrives, it stops and starts. The squads send an order once and watch it;\n\t * this is how a test or `simulate` sees that they do\n\t */\n\tprivate churn(command: PlayerCommand): void {\n\t\tconst key = orderKey(command)\n\t\t// a queued order is what comes after, not what the unit is told to do now\n\t\tif ((command as { queue?: boolean }).queue) return\n\t\tif (key === undefined) {\n\t\t\t// a spell cast in between: the order before it is over, and the same told again after is no repeat\n\t\t\tif (command.type === 'cast') for (const id of (command as { entities: readonly number[] }).entities) this.castAt.set(id, this.tick)\n\t\t\treturn\n\t\t}\n\t\tconst window = CHURN_WINDOW * this.session.tickRate\n\t\tfor (const id of (command as { entities: readonly number[] }).entities) {\n\t\t\tconst last = this.lastOrders.get(id)\n\t\t\tconst cast = this.castAt.get(id)\n\t\t\tif (last && last.key === key && this.tick - last.tick <= window && !(cast !== undefined && cast >= last.tick)) this.stats.churn++\n\t\t\tthis.lastOrders.set(id, { key, tick: this.tick })\n\t\t}\n\t}\n\n\t/**\n\t * The last order it sent a unit lately (within a few seconds), as a key (`orderKey`: its kind, its target, its\n\t * place to the cell) with the tick: what keeps a module from telling a unit again what it is already doing, when the\n\t * unit changed hands (a squad of another goal) since. None for a unit told nothing lately\n\t */\n\tsentTo(unit: number): { key: string; tick: number } | undefined {\n\t\tconst last = this.lastOrders.get(unit)\n\t\treturn last && this.tick - last.tick <= CHURN_WINDOW * this.session.tickRate ? last : undefined\n\t}\n\n\t/**\n\t * The words the game told it and no module has taken yet (`world.computers.tell`), oldest first; those of one word\n\t * where given. Reading them takes nothing: `takeTold` does, as a told rule (`when.told`) does when it fires\n\t */\n\ttold(word?: string): readonly Told[] {\n\t\tconst heard = this.replayHeard === undefined ? this.heard : this.heard.slice(0, this.replayHeard)\n\t\treturn word === undefined ? heard : heard.filter((told) => told.word === word)\n\t}\n\n\t/**\n\t * Writes down a decision (`decisions`), with the reason that weighed most, in words: what the report's log and the\n\t * debug panel show, and the world's log at its `debug` level. `kind` says what it is about (`army`, `build`)\n\t */\n\tdecide(line: string, about: { kind?: string; goal?: string } = {}): void {\n\t\t// run again in a loaded game: the save's log has it already\n\t\tif (this.replaying) return\n\t\tconst decision: AiDecision = { tick: this.tick, kind: about.kind ?? 'other', line, ...(about.goal !== undefined && { goal: about.goal }) }\n\t\tthis.decisions.add(decision)\n\t\tfor (const watcher of this.watchers) watcher.decided?.(decision, this)\n\t\tif (this.log.says('debug'))\n\t\t\tthis.log.debug('ai', line, { player: this.playerId, kind: decision.kind, ...(about.goal !== undefined && { goal: about.goal }) })\n\t}\n\n\t/**\n\t * What it is thinking, as plain data (`AiReport`): its goals with their considerations, what it knows, its roles,\n\t * its economy, what it was told, what it decided, and each module's section. Made when asked: nothing in a think\n\t * waits for it. What `inspect` gives, and the debug panel's AI tab draws\n\t */\n\treport(): AiReport {\n\t\tconst { view } = this\n\t\tconst me = view.me\n\t\tconst sections: Record<string, unknown> = {}\n\t\tconst report: AiReport = {\n\t\t\tv: 1,\n\t\t\tbrain: this.def.id,\n\t\t\tplayer: this.playerId,\n\t\t\ttick: this.tick,\n\t\t\ttime: this.time,\n\t\t\ttickRate: this.session.tickRate,\n\t\t\tthinks: this.thinks,\n\t\t\t...(this.paused && { paused: true as const }),\n\t\t\tprofile: plainParts(this.options).kept as Record<string, unknown>,\n\t\t\tplaying: plainParts(this.profile).kept as AiProfile,\n\t\t\t...(this.goals.best !== undefined && { goal: this.goals.best }),\n\t\t\tgoals: this.goals.report(),\n\t\t\t...(this.def.knowledge !== false && { knowledge: this.knowledge.inspect() as unknown as KnowledgeReport }),\n\t\t\troles: view.inspect().roles as Record<string, string[]>,\n\t\t\teconomy: { resources: { ...me.resources }, income: { ...(me.income ?? {}) }, supply: [me.supplyUsed, me.supplyCap] },\n\t\t\tplans: this.plans.report(),\n\t\t\tsquads: this.squads.report(),\n\t\t\tthreat: threatOf(view.threat),\n\t\t\ttold: this.heard.map((told) => ({ word: told.word, tick: told.tick })),\n\t\t\tsent: this.sent,\n\t\t\t...(this.held && { held: this.held }),\n\t\t\t...(this.budget.slices && { budget: { ...this.budget.report(), ticks: this.thinkTicks } }),\n\t\t\tstats: { ...this.stats },\n\t\t\tdecisions: this.decisions.save(),\n\t\t\tsections\n\t\t}\n\t\t// what each area of its own says beside (its economy, its scouting, a game's own)\n\t\tfor (const area of this.areaList) area.reportTo?.(report)\n\t\tfor (const module of this.modules) {\n\t\t\tmodule.report?.(report)\n\t\t\tconst what = module.inspect?.()\n\t\t\tif (what !== undefined) sections[module.id] = what\n\t\t}\n\t\treturn report\n\t}\n\n\t/**\n\t * A goal's row of the report as a sentence: what it scored and why, whether it runs and with what, or what kept it\n\t * from running. Nothing for a goal the brain does not have\n\t */\n\texplain(goal: string): string | undefined {\n\t\tconst row = this.goals.report().find((each) => each.id === goal)\n\t\treturn row && explainGoal(row, (tick) => tick / this.session.tickRate, this.time)\n\t}\n\n\t/** Takes in a word from the game: a module takes it with `takeTold`. What the seat calls for `world.computers.tell` */\n\ttell(word: string, data: unknown, tick: number): void {\n\t\t// a paused brain thinks of nothing, so nothing of it would hear this one: it is answered here and now\n\t\tif (word === 'resume' && this.paused) {\n\t\t\tthis.paused = false\n\t\t\tthis.decide('resumes: told to', { kind: 'word' })\n\t\t\treturn\n\t\t}\n\t\tthis.heard.push({ word, ...(data !== undefined && { data }), tick })\n\t\tif (this.slice !== undefined && this.began) this.toldDuring.push({ word, ...(data !== undefined && { data }), tick })\n\t}\n\n\t/** Takes the words of one kind out of what it was told, oldest first, as many as `count` (all when left out): they are heard no more */\n\ttakeTold(word: string, count = Infinity): Told[] {\n\t\tconst taken: Told[] = []\n\t\t// a pass run again takes among the words it had heard then, not those told after it\n\t\tconst among = this.replayHeard ?? Infinity\n\t\tthis.heard = this.heard.filter((told, index) => {\n\t\t\tif (index >= among || told.word !== word || taken.length >= count) return true\n\t\t\ttaken.push(told)\n\t\t\treturn false\n\t\t})\n\t\tif (this.replayHeard !== undefined) this.replayHeard -= taken.length\n\t\treturn taken\n\t}\n\n\t/**\n\t * What it remembers, for a save: its numbers, each module's memory with its version, what it was told. A think under\n\t * way (`budget`) is kept as it began, with how far it got and its picture (`BrainMemory.thinking`): the game loaded\n\t * from the save runs it on to its end as the saved game does\n\t */\n\tsave(): BrainMemory {\n\t\tconst heard = this.session.can.computer?.pendingEvents().filter((event) => HEARD.has(event.type))\n\t\t// a think under way: the brain as it began, with how far it got over what picture, for a loaded game to run on\n\t\tif (this.slice !== undefined && this.began)\n\t\t\treturn {\n\t\t\t\t...this.began,\n\t\t\t\ttold: [...this.began.told, ...this.toldDuring.map((told) => ({ ...told }))],\n\t\t\t\t...(heard?.length && { heard }),\n\t\t\t\t// what the passes so far decided is in the log already; run again, they write nothing down\n\t\t\t\t...(this.decisions.list().length > 0 && { decisions: this.decisions.save() }),\n\t\t\t\tthinking: { snapshot: this.snapshot, passes: this.passes.map((pass) => [...pass] as [number, number, number]) }\n\t\t\t}\n\t\treturn { ...this.memory(), ...(heard?.length && { heard }) }\n\t}\n\n\t/** What its areas remember, by id: those that remember anything, and what a save had of areas not made since */\n\tprivate areaMemories(): AreaMemories {\n\t\tconst areas: AreaMemories = { ...this.waiting }\n\t\tfor (const area of this.areaList) if (area.remembers()) areas[area.id] = area.save()\n\t\treturn areas\n\t}\n\n\t/** What it remembers, but what its seat heard since it last thought */\n\tprivate memory(): BrainMemory {\n\t\tconst modules: BrainMemory['modules'] = {}\n\t\tfor (const module of this.modules) {\n\t\t\tconst data = module.save?.()\n\t\t\tif (data !== undefined) modules[module.id] = { v: module.version ?? 1, data }\n\t\t}\n\t\treturn {\n\t\t\tv: 2,\n\t\t\trng: this.rng.save(),\n\t\t\tthinks: this.thinks,\n\t\t\t...(this.paused && { paused: true }),\n\t\t\ttold: this.heard.map((told) => ({ ...told })),\n\t\t\tmodules,\n\t\t\tareas: this.areaMemories(),\n\t\t\t...(this.decisions.list().length > 0 && { decisions: this.decisions.save() }),\n\t\t\t...(this.lastOrders.size > 0 && { orders: [...this.lastOrders].map(([unit, last]) => ({ unit, ...last })) })\n\t\t}\n\t}\n\n\t/**\n\t * Takes back what `save` gave. A module's memory of another version goes through its `migrate`; without one, or with\n\t * nothing in the save for it, the module starts afresh, and the log says so once. Memory of a module the brain no\n\t * longer has is dropped, said too\n\t */\n\trestore(memory: BrainMemory): void {\n\t\tif (!memory || typeof memory !== 'object' || memory.v !== 2) {\n\t\t\tthis.log.warn('ai', 'the save remembers the computer player in a shape this brain does not read: it starts afresh', { brain: this.def.id })\n\t\t\treturn\n\t\t}\n\t\tthis.rng.restore(memory.rng)\n\t\tthis.thinks = memory.thinks ?? 0\n\t\t// a think the save was taken in the middle of: at the next tick, run again to where it was and on\n\t\tthis.resume = memory.thinking && {\n\t\t\tsnapshot: memory.thinking.snapshot,\n\t\t\t// the form before passes: so many modules run once each, the tick having looked at nothing before each\n\t\t\tpasses:\n\t\t\t\tmemory.thinking.passes ??\n\t\t\t\tArray.from({ length: memory.thinking.slice ?? 0 }, (_, module) => [module, 0, (memory.told ?? []).length] as [number, number, number])\n\t\t}\n\t\tthis.paused = !!memory.paused\n\t\tthis.heard = (memory.told ?? []).map((told) => ({ ...told }))\n\t\tthis.decisions.restore(memory.decisions)\n\t\tthis.lastOrders.clear()\n\t\tfor (const order of memory.orders ?? []) this.lastOrders.set(order.unit, { key: order.key, tick: order.tick })\n\t\tif (memory.heard?.length) this.session.can.computer?.restoreEvents(memory.heard)\n\t\t// before the modules: a module's own memory of an older shape may be brought into it (the melee player's `seen`). An\n\t\t// area not made yet takes its memory as a module first asks for it\n\t\tthis.waiting = { ...memory.areas }\n\t\tfor (const area of [...this.areaList]) {\n\t\t\tif (!(area.id in this.waiting)) continue\n\t\t\tconst kept = this.waiting[area.id]\n\t\t\tdelete this.waiting[area.id]\n\t\t\tif (!area.restore(kept))\n\t\t\t\tthis.log.info(\n\t\t\t\t\t'ai',\n\t\t\t\t\tarea.afresh ?? `the save remembers the computer player's ${area.id} in a shape this brain does not read: it starts afresh`\n\t\t\t\t)\n\t\t}\n\t\tconst read = new Set<string>()\n\t\tfor (const module of this.modules) {\n\t\t\t// under its id, or under one it was called before (`wasCalled`): a save made then\n\t\t\tlet kept = memory.modules?.[module.id]\n\t\t\tif (kept) read.add(module.id)\n\t\t\telse\n\t\t\t\tfor (const was of module.wasCalled ?? []) {\n\t\t\t\t\tkept = memory.modules?.[was]\n\t\t\t\t\tif (!kept) continue\n\t\t\t\t\tread.add(was)\n\t\t\t\t\tthis.log.info('ai', `the module \"${module.id}\" reads what the save kept as \"${was}\"`, { module: module.id, was })\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\tif (!kept) {\n\t\t\t\tif (module.save) this.log.info('ai', `the save has nothing for the module \"${module.id}\": it starts afresh`, { module: module.id })\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tconst version = module.version ?? 1\n\t\t\tlet data = kept.data\n\t\t\tif (kept.v !== version) {\n\t\t\t\tdata = module.migrate?.(kept.data, kept.v)\n\t\t\t\tif (data === undefined) {\n\t\t\t\t\tthis.log.info('ai', `the module \"${module.id}\" saved version ${kept.v} and reads ${version}: it starts afresh`, {\n\t\t\t\t\t\tmodule: module.id,\n\t\t\t\t\t\tfrom: kept.v\n\t\t\t\t\t})\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tmodule.restore?.(data)\n\t\t}\n\t\tfor (const id of Object.keys(memory.modules ?? {}))\n\t\t\tif (!this.module(id) && !read.has(id))\n\t\t\t\tthis.log.info('ai', `the save remembers a module \"${id}\" this brain does not have: dropped`, { module: id })\n\t}\n\n\t/** What it is thinking, for the debug tools: its report (`report`) */\n\tinspect(): AiReport {\n\t\treturn this.report()\n\t}\n\n\t/** Lets its modules go of what they hold outside the brain */\n\tdispose(): void {\n\t\tfor (const module of this.modules) module.dispose?.()\n\t}\n}\n\n/**\n * Makes a brain from its definition for a seat: the session it plays through, the options it is seated with. What\n * a brain's seat does (`createBrainSystem`); for a seat made another way (`createComputerSystem`'s `make`)\n *\n * @beta\n */\nexport function createBrain(def: BrainDef, session: GameSession, options: AiOptions, context: BrainContext = {}): Brain {\n\treturn new Brain(def, session, options, context)\n}\n\n/** The threat map as the report has it: its cells with something in them, on the ground, what it remembers counted */\nfunction threatOf(map: ThreatMap): ThreatReport {\n\tconst round = (x: number) => Math.round(x * 10) / 10\n\treturn {\n\t\tcell: map.cell,\n\t\tcols: map.cols,\n\t\trows: map.rows,\n\t\tcells: map.cells({ remembered: true }).map((cell) => [cell.col, cell.row, round(cell.friendly), round(cell.enemy)])\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//#region src/ai/budget.ts
|
|
2
|
+
/** Milliseconds a module takes before the log says so, where nothing was said */
|
|
3
|
+
var WARN_AFTER_MS = 4;
|
|
4
|
+
/**
|
|
5
|
+
* The budget of the think under way, as a module reads it (`ai.budget`): what this tick may still do, and the way to say
|
|
6
|
+
* what was looked at (`spent`) and to go on at the next tick (`later`). A module that walks units looks at them a few at
|
|
7
|
+
* a time:
|
|
8
|
+
*
|
|
9
|
+
* for (; this.cursor < squads.length; this.cursor++) {
|
|
10
|
+
* look(squads[this.cursor])
|
|
11
|
+
* if (!ai.budget.spent(squads[this.cursor].size) && this.cursor + 1 < squads.length) return ai.budget.later()
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* @beta
|
|
15
|
+
*/
|
|
16
|
+
var ThinkBudget = class {
|
|
17
|
+
/** Modules a tick may run: `Infinity` where unlimited */
|
|
18
|
+
modulesPerTick;
|
|
19
|
+
/** Units a tick's modules may look at, as they say: `Infinity` where unlimited */
|
|
20
|
+
unitsPerTick;
|
|
21
|
+
/** Milliseconds a module may take before the log says so: none for never */
|
|
22
|
+
warnAfterMs;
|
|
23
|
+
/** Units looked at this tick, as the modules said (`spent`) */
|
|
24
|
+
units = 0;
|
|
25
|
+
/** Whether the module running asked to go on at the next tick (`later`) */
|
|
26
|
+
deferred = false;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
const given = typeof options === "number" ? { modulesPerTick: options } : options ?? {};
|
|
29
|
+
this.modulesPerTick = given.modulesPerTick !== void 0 && given.modulesPerTick > 0 ? Math.floor(given.modulesPerTick) : Infinity;
|
|
30
|
+
this.unitsPerTick = given.unitsPerTick !== void 0 && given.unitsPerTick > 0 ? given.unitsPerTick : Infinity;
|
|
31
|
+
this.warnAfterMs = given.warnAfterMs === false ? void 0 : given.warnAfterMs ?? WARN_AFTER_MS;
|
|
32
|
+
}
|
|
33
|
+
/** Whether the budget may slice a think over ticks at all: one that limits nothing never does, and nothing is kept for it */
|
|
34
|
+
get slices() {
|
|
35
|
+
return this.modulesPerTick !== Infinity || this.unitsPerTick !== Infinity;
|
|
36
|
+
}
|
|
37
|
+
/** Whether this tick may look at more: a module that walks units stops where it may not, and says `later` */
|
|
38
|
+
get room() {
|
|
39
|
+
return this.units < this.unitsPerTick;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Says the module looked at so many units (one when left out). Gives whether this tick may look at more: where it may
|
|
43
|
+
* not, a module with more to do remembers where it was and says `later`
|
|
44
|
+
*/
|
|
45
|
+
spent(units = 1) {
|
|
46
|
+
this.units += units;
|
|
47
|
+
return this.room;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The module running stops here for this tick and is run again at the next, going on from where it remembers it was,
|
|
51
|
+
* over the same picture. Honoured where the module looked at something this tick (`spent`): one that looked at
|
|
52
|
+
* nothing and asks to go on would never end, so it is taken to be done, and the log says so
|
|
53
|
+
*/
|
|
54
|
+
later() {
|
|
55
|
+
this.deferred = true;
|
|
56
|
+
}
|
|
57
|
+
/** The budget as the report shows it: the limits, where there are any */
|
|
58
|
+
report() {
|
|
59
|
+
if (!this.slices) return void 0;
|
|
60
|
+
return {
|
|
61
|
+
...this.modulesPerTick !== Infinity && { modulesPerTick: this.modulesPerTick },
|
|
62
|
+
...this.unitsPerTick !== Infinity && { unitsPerTick: this.unitsPerTick }
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
/** @internal A new tick of the think: nothing looked at yet, or as much as a pass run again had before it (`units`) */
|
|
66
|
+
begin(units = 0) {
|
|
67
|
+
this.units = units;
|
|
68
|
+
this.deferred = false;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
//#endregion
|
|
72
|
+
export { ThinkBudget };
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","names":[],"sources":["../../src/ai/budget.ts"],"sourcesContent":["/*\n * The budget of a think (AI plan, F2): how much of a think may be done in one tick, so that twelve computers thinking\n * in the same second do not all think in the same tick. A think is a list of modules run in order over one picture of\n * the game (`brain.ts`); the budget says how many of them a tick may run (`modulesPerTick`) and how many units a tick's\n * modules may look at (`unitsPerTick`, as the modules say: `ai.budget.spent(n)`), and the think goes on at the next\n * tick from where it stopped, over the same picture. A think with no budget is done in one tick, as it always was.\n *\n * **Why work and never milliseconds.** A budget in milliseconds would make the game differ by machine: a slow one would\n * stop a think a module earlier, and an order would land a tick later than on a fast one; the same seed and the same\n * commands would no longer give the same game, and a replay would come apart (`determinism-plan`). Work (a module, a\n * unit looked at) is the same on every machine, so a sliced think is sliced the same way everywhere. Milliseconds are\n * measured all the same, for the report and the Stats panel, and a module over `warnAfterMs` is said in the log; it is\n * never cut short.\n *\n * **A sliced think is stale by design.** A module that runs on a later tick of a think acts on a picture a few ticks\n * old; that is why every task and goal of the kit reads its targets in the view again before sending (C1), and why a\n * brain's default is no slicing. A game sets a budget where its numbers ask for one (`AiOptions.budget`, or the\n * profile's), which the bench says is at eight or twelve computers.\n */\n\n/**\n * How much of a think one tick may do (`AiOptions.budget`, `AiProfile.budget`, `ComputerSeatOptions.budget`), counted in\n * work and never in milliseconds: the same on every machine, so that a game with a sliced think is the same game\n * everywhere. Nothing is limited where a field is left out\n *\n * @beta\n */\nexport interface ThinkBudgetOptions {\n\t/**\n\t * Modules of a brain a think may run in one tick: a think with more goes on at the next tick, over the same picture of\n\t * the game. All of them in one tick when left out\n\t */\n\tmodulesPerTick?: number\n\t/**\n\t * Units a tick's modules may look at, as the modules say (`ai.budget.spent(n)`): a module that walks the squads or the\n\t * bases stops once the tick has looked at this many, and goes on at the next tick from where it was (`later`). No\n\t * limit when left out. A module that says nothing of what it looks at is never stopped by it\n\t */\n\tunitsPerTick?: number\n\t/**\n\t * Milliseconds a module may take before the log says so (once a module, a warning): what to measure first. A module\n\t * is never cut short for it. 4 when left out; `false` for never\n\t */\n\twarnAfterMs?: number | false\n}\n\n/** Milliseconds a module takes before the log says so, where nothing was said */\nconst WARN_AFTER_MS = 4\n\n/**\n * The budget of the think under way, as a module reads it (`ai.budget`): what this tick may still do, and the way to say\n * what was looked at (`spent`) and to go on at the next tick (`later`). A module that walks units looks at them a few at\n * a time:\n *\n * for (; this.cursor < squads.length; this.cursor++) {\n * look(squads[this.cursor])\n * if (!ai.budget.spent(squads[this.cursor].size) && this.cursor + 1 < squads.length) return ai.budget.later()\n * }\n *\n * @beta\n */\nexport class ThinkBudget {\n\t/** Modules a tick may run: `Infinity` where unlimited */\n\treadonly modulesPerTick: number\n\t/** Units a tick's modules may look at, as they say: `Infinity` where unlimited */\n\treadonly unitsPerTick: number\n\t/** Milliseconds a module may take before the log says so: none for never */\n\treadonly warnAfterMs: number | undefined\n\t/** Units looked at this tick, as the modules said (`spent`) */\n\tunits = 0\n\t/** Whether the module running asked to go on at the next tick (`later`) */\n\tdeferred = false\n\n\tconstructor(options?: number | ThinkBudgetOptions) {\n\t\t// a number is the first form the budget had: modules a tick\n\t\tconst given: ThinkBudgetOptions = typeof options === 'number' ? { modulesPerTick: options } : (options ?? {})\n\t\tthis.modulesPerTick = given.modulesPerTick !== undefined && given.modulesPerTick > 0 ? Math.floor(given.modulesPerTick) : Infinity\n\t\tthis.unitsPerTick = given.unitsPerTick !== undefined && given.unitsPerTick > 0 ? given.unitsPerTick : Infinity\n\t\tthis.warnAfterMs = given.warnAfterMs === false ? undefined : (given.warnAfterMs ?? WARN_AFTER_MS)\n\t}\n\n\t/** Whether the budget may slice a think over ticks at all: one that limits nothing never does, and nothing is kept for it */\n\tget slices(): boolean {\n\t\treturn this.modulesPerTick !== Infinity || this.unitsPerTick !== Infinity\n\t}\n\n\t/** Whether this tick may look at more: a module that walks units stops where it may not, and says `later` */\n\tget room(): boolean {\n\t\treturn this.units < this.unitsPerTick\n\t}\n\n\t/**\n\t * Says the module looked at so many units (one when left out). Gives whether this tick may look at more: where it may\n\t * not, a module with more to do remembers where it was and says `later`\n\t */\n\tspent(units = 1): boolean {\n\t\tthis.units += units\n\t\treturn this.room\n\t}\n\n\t/**\n\t * The module running stops here for this tick and is run again at the next, going on from where it remembers it was,\n\t * over the same picture. Honoured where the module looked at something this tick (`spent`): one that looked at\n\t * nothing and asks to go on would never end, so it is taken to be done, and the log says so\n\t */\n\tlater(): void {\n\t\tthis.deferred = true\n\t}\n\n\t/** The budget as the report shows it: the limits, where there are any */\n\treport(): ThinkBudgetOptions | undefined {\n\t\tif (!this.slices) return undefined\n\t\treturn {\n\t\t\t...(this.modulesPerTick !== Infinity && { modulesPerTick: this.modulesPerTick }),\n\t\t\t...(this.unitsPerTick !== Infinity && { unitsPerTick: this.unitsPerTick })\n\t\t}\n\t}\n\n\t/** @internal A new tick of the think: nothing looked at yet, or as much as a pass run again had before it (`units`) */\n\tbegin(units = 0): void {\n\t\tthis.units = units\n\t\tthis.deferred = false\n\t}\n}\n"],"mappings":";;AA+CA,IAAM,gBAAgB;;;;;;;;;;;;;AActB,IAAa,cAAb,MAAyB;;CAExB;;CAEA;;CAEA;;CAEA,QAAQ;;CAER,WAAW;CAEX,YAAY,SAAuC;EAElD,MAAM,QAA4B,OAAO,YAAY,WAAW,EAAE,gBAAgB,QAAQ,IAAK,WAAW,CAAC;EAC3G,KAAK,iBAAiB,MAAM,mBAAmB,KAAA,KAAa,MAAM,iBAAiB,IAAI,KAAK,MAAM,MAAM,cAAc,IAAI;EAC1H,KAAK,eAAe,MAAM,iBAAiB,KAAA,KAAa,MAAM,eAAe,IAAI,MAAM,eAAe;EACtG,KAAK,cAAc,MAAM,gBAAgB,QAAQ,KAAA,IAAa,MAAM,eAAe;CACpF;;CAGA,IAAI,SAAkB;EACrB,OAAO,KAAK,mBAAmB,YAAY,KAAK,iBAAiB;CAClE;;CAGA,IAAI,OAAgB;EACnB,OAAO,KAAK,QAAQ,KAAK;CAC1B;;;;;CAMA,MAAM,QAAQ,GAAY;EACzB,KAAK,SAAS;EACd,OAAO,KAAK;CACb;;;;;;CAOA,QAAc;EACb,KAAK,WAAW;CACjB;;CAGA,SAAyC;EACxC,IAAI,CAAC,KAAK,QAAQ,OAAO,KAAA;EACzB,OAAO;GACN,GAAI,KAAK,mBAAmB,YAAY,EAAE,gBAAgB,KAAK,eAAe;GAC9E,GAAI,KAAK,iBAAiB,YAAY,EAAE,cAAc,KAAK,aAAa;EACzE;CACD;;CAGA,MAAM,QAAQ,GAAS;EACtB,KAAK,QAAQ;EACb,KAAK,WAAW;CACjB;AACD"}
|
package/dist/ai/checks.d.ts
CHANGED
|
@@ -19,10 +19,13 @@ export declare const AI_INVARIANTS: readonly AiInvariant[];
|
|
|
19
19
|
* @beta
|
|
20
20
|
*/
|
|
21
21
|
export interface AiViolation {
|
|
22
|
+
/** Which rule was broken */
|
|
22
23
|
invariant: AiInvariant;
|
|
24
|
+
/** The computer player whose think broke the rule */
|
|
23
25
|
player: PlayerId;
|
|
24
26
|
/** The tick of the think's picture of the game */
|
|
25
27
|
tick: number;
|
|
28
|
+
/** What exactly went wrong, in words */
|
|
26
29
|
message: string;
|
|
27
30
|
}
|
|
28
31
|
/**
|
|
@@ -70,6 +73,7 @@ export declare function orderKey(command: PlayerCommand): string | undefined;
|
|
|
70
73
|
* @beta
|
|
71
74
|
*/
|
|
72
75
|
export declare class BrainChecks implements BrainWatcher {
|
|
76
|
+
/** The brain the checks watch */
|
|
73
77
|
readonly brain: Brain;
|
|
74
78
|
private readonly options;
|
|
75
79
|
/** Every rule found broken, in the order found */
|
|
@@ -84,8 +88,12 @@ export declare class BrainChecks implements BrainWatcher {
|
|
|
84
88
|
private readonly since;
|
|
85
89
|
/** What was said stuck already: once a time it is stuck, not every think after */
|
|
86
90
|
private readonly said;
|
|
87
|
-
constructor(
|
|
91
|
+
constructor(
|
|
92
|
+
/** The brain the checks watch */
|
|
93
|
+
brain: Brain, options?: AiCheckOptions);
|
|
94
|
+
/** Records a command the brain sent in this think, for the checks of `commands` and `churn` at the end of it */
|
|
88
95
|
sent(command: PlayerCommand): void;
|
|
96
|
+
/** Ends a think: runs the checks that are on over what was sent and returns nothing; found violations are kept */
|
|
89
97
|
thought(): void;
|
|
90
98
|
/** A unit in at most one squad and one hold: two would give it two orders a think */
|
|
91
99
|
private squads;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { isCommand } from "../session/protocol.js";
|
|
2
|
+
//#region src/ai/checks.ts
|
|
3
|
+
/**
|
|
4
|
+
* Every invariant, in the order they are checked
|
|
5
|
+
*
|
|
6
|
+
* @beta
|
|
7
|
+
*/
|
|
8
|
+
var AI_INVARIANTS = [
|
|
9
|
+
"squads",
|
|
10
|
+
"purse",
|
|
11
|
+
"finite",
|
|
12
|
+
"commands",
|
|
13
|
+
"churn",
|
|
14
|
+
"ends"
|
|
15
|
+
];
|
|
16
|
+
var LIMITS = {
|
|
17
|
+
builder: 120,
|
|
18
|
+
task: 300,
|
|
19
|
+
reservation: 600,
|
|
20
|
+
want: 600
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* What an order to a unit is for, as a key: its kind, its target, its place to the cell. Two orders with the same key
|
|
24
|
+
* are the same order. None for a command that is not an order to units (a train, a ping, a game's own)
|
|
25
|
+
*/
|
|
26
|
+
function orderKey(command) {
|
|
27
|
+
if (isCommand(command, "order")) {
|
|
28
|
+
const { type, target, point } = command.order;
|
|
29
|
+
return `${type}:${target ?? ""}:${point ? `${Math.round(point.x)},${Math.round(point.z)}` : ""}`;
|
|
30
|
+
}
|
|
31
|
+
if (isCommand(command, "attack") && command.point) return `attack:${command.target ?? ""}:${Math.round(command.point.x)},${Math.round(command.point.z)}`;
|
|
32
|
+
}
|
|
33
|
+
/** The kind of order a unit carries out for a command: an attack on a target, an attack-move, or the order's own */
|
|
34
|
+
function kindOf(command) {
|
|
35
|
+
if (isCommand(command, "attack")) return command.target === void 0 ? "attackMove" : "attack";
|
|
36
|
+
return isCommand(command, "order") ? command.order.type : void 0;
|
|
37
|
+
}
|
|
38
|
+
/** Fields of the engine's commands that name an entity of the sender's own, one or a list (a hero apart: a dead one is in the player's list of heroes, not on the ground) */
|
|
39
|
+
var OWN_ONE = ["building", "entity"];
|
|
40
|
+
var OWN_LIST = ["entities", "buildings"];
|
|
41
|
+
/** Fields that name any entity the sender knows of: a target, a shop */
|
|
42
|
+
var KNOWN = ["target", "shop"];
|
|
43
|
+
/**
|
|
44
|
+
* Checks a brain against the invariants after each of its thinks, through what it is watched by (`Brain.watch`): what
|
|
45
|
+
* `checkBrain` makes, and keeps the broken rules in `violations`
|
|
46
|
+
*
|
|
47
|
+
* @beta
|
|
48
|
+
*/
|
|
49
|
+
var BrainChecks = class {
|
|
50
|
+
brain;
|
|
51
|
+
options;
|
|
52
|
+
/** Every rule found broken, in the order found */
|
|
53
|
+
violations = [];
|
|
54
|
+
invariants;
|
|
55
|
+
limits;
|
|
56
|
+
/** What it sent since its last think ended */
|
|
57
|
+
commands = [];
|
|
58
|
+
/** The last order each unit was sent, with the tick of the think */
|
|
59
|
+
orders = /* @__PURE__ */ new Map();
|
|
60
|
+
/** Since when each thing that has to end has been going on, by a key of what it is */
|
|
61
|
+
since = /* @__PURE__ */ new Map();
|
|
62
|
+
/** What was said stuck already: once a time it is stuck, not every think after */
|
|
63
|
+
said = /* @__PURE__ */ new Set();
|
|
64
|
+
constructor(brain, options = {}) {
|
|
65
|
+
this.brain = brain;
|
|
66
|
+
this.options = options;
|
|
67
|
+
this.invariants = new Set(options.invariants ?? AI_INVARIANTS);
|
|
68
|
+
this.limits = {
|
|
69
|
+
...LIMITS,
|
|
70
|
+
...options.limits
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** Records a command the brain sent in this think, for the checks of `commands` and `churn` at the end of it */
|
|
74
|
+
sent(command) {
|
|
75
|
+
this.commands.push(command);
|
|
76
|
+
}
|
|
77
|
+
/** Ends a think: runs the checks that are on over what was sent and returns nothing; found violations are kept */
|
|
78
|
+
thought() {
|
|
79
|
+
const sent = this.commands;
|
|
80
|
+
this.commands = [];
|
|
81
|
+
const found = [];
|
|
82
|
+
const say = (invariant, message) => found.push({
|
|
83
|
+
invariant,
|
|
84
|
+
player: this.brain.playerId,
|
|
85
|
+
tick: this.brain.tick,
|
|
86
|
+
message
|
|
87
|
+
});
|
|
88
|
+
if (this.invariants.has("squads")) this.squads(say);
|
|
89
|
+
if (this.invariants.has("purse")) this.purse(say);
|
|
90
|
+
if (this.invariants.has("finite")) this.finite(say);
|
|
91
|
+
if (this.invariants.has("commands")) for (const command of sent) this.command(command, say);
|
|
92
|
+
if (this.invariants.has("churn")) this.churn(sent, say);
|
|
93
|
+
if (this.invariants.has("ends")) this.ends(say);
|
|
94
|
+
for (const violation of found) {
|
|
95
|
+
this.violations.push(violation);
|
|
96
|
+
if (this.options.onViolation) this.options.onViolation(violation);
|
|
97
|
+
else throw new Error(`The computer player ${violation.player} broke "${violation.invariant}" at tick ${violation.tick}: ${violation.message}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** A unit in at most one squad and one hold: two would give it two orders a think */
|
|
101
|
+
squads(say) {
|
|
102
|
+
const { squads, goals } = this.brain;
|
|
103
|
+
const inSquad = /* @__PURE__ */ new Map();
|
|
104
|
+
for (const squad of squads.all()) for (const id of squad.data.units) {
|
|
105
|
+
const other = inSquad.get(id);
|
|
106
|
+
if (other !== void 0) say("squads", `unit ${id} is in the squads ${other} and ${squad.name}`);
|
|
107
|
+
else inSquad.set(id, squad.name);
|
|
108
|
+
}
|
|
109
|
+
const holder = /* @__PURE__ */ new Map();
|
|
110
|
+
const holds = goals.all();
|
|
111
|
+
for (const id in holds) for (const unit of holds[id].units) {
|
|
112
|
+
const other = holder.get(unit);
|
|
113
|
+
if (other !== void 0) say("squads", `unit ${unit} is held by ${other} and ${id}`);
|
|
114
|
+
else holder.set(unit, id);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** What the think spent and what its builders will pay never more than what it had: else the same gold went twice */
|
|
118
|
+
purse(say) {
|
|
119
|
+
const { purse, view } = this.brain;
|
|
120
|
+
const had = view.me.resources;
|
|
121
|
+
for (const kind of /* @__PURE__ */ new Set([...Object.keys(purse.spent), ...Object.keys(purse.promised)])) if ((purse.spent[kind] ?? 0) + (purse.promised[kind] ?? 0) > (had[kind] ?? 0)) say("purse", `it spent ${purse.spent[kind] ?? 0} ${kind} and its builders will pay ${purse.promised[kind] ?? 0}, of ${had[kind] ?? 0}`);
|
|
122
|
+
}
|
|
123
|
+
/** Every number a decision is made of is a number: a `NaN` compares false with everything and picks silently */
|
|
124
|
+
finite(say) {
|
|
125
|
+
const { goals, squads, view } = this.brain;
|
|
126
|
+
for (const goal of goals.report()) {
|
|
127
|
+
if (!Number.isFinite(goal.score)) say("finite", `the goal ${goal.id} scores ${goal.score}`);
|
|
128
|
+
for (const each of goal.considerations) if (!Number.isFinite(each.value) || !Number.isFinite(each.curved)) say("finite", `the goal ${goal.id}'s ${each.name} is ${each.value} (${each.curved} curved)`);
|
|
129
|
+
}
|
|
130
|
+
for (const squad of squads.all()) {
|
|
131
|
+
const strength = squad.strength();
|
|
132
|
+
if (!Number.isFinite(strength)) say("finite", `the squad ${squad.name} is ${strength} strong`);
|
|
133
|
+
}
|
|
134
|
+
const odds = view.strength.odds(view.mine.fighters(), view.enemies);
|
|
135
|
+
if (!Number.isFinite(odds)) say("finite", `its odds against what it sees are ${odds}`);
|
|
136
|
+
}
|
|
137
|
+
/** A command about what its picture of the game has: its own units by theirs, anyone else's as seen, places on the map */
|
|
138
|
+
command(command, say) {
|
|
139
|
+
const { view, session } = this.brain;
|
|
140
|
+
const fields = command;
|
|
141
|
+
const what = `its ${command.type} command`;
|
|
142
|
+
const own = (id, field) => {
|
|
143
|
+
if (typeof id !== "number") return;
|
|
144
|
+
if (!view.byId(id)) say("commands", `${what} names ${field} ${id}, which it does not see`);
|
|
145
|
+
else if (!view.mine.has(id)) say("commands", `${what} names ${field} ${id}, which is not its own`);
|
|
146
|
+
};
|
|
147
|
+
const known = (id, field) => {
|
|
148
|
+
if (typeof id === "number" && !view.byId(id)) say("commands", `${what} names a ${field} ${id} it does not see`);
|
|
149
|
+
};
|
|
150
|
+
const place = (at, field) => {
|
|
151
|
+
if (!at || typeof at !== "object") return;
|
|
152
|
+
const { x, z } = at;
|
|
153
|
+
const { width, depth } = session.terrain;
|
|
154
|
+
if (!Number.isFinite(x) || !Number.isFinite(z) || x < 0 || z < 0 || x > width || z > depth) say("commands", `${what} sends ${field} to (${x}, ${z}), off the map`);
|
|
155
|
+
};
|
|
156
|
+
for (const field of OWN_ONE) own(fields[field], field);
|
|
157
|
+
if (typeof fields.hero === "number" && !view.me.heroes.some((hero) => hero.id === fields.hero)) own(fields.hero, "hero");
|
|
158
|
+
for (const field of OWN_LIST) if (Array.isArray(fields[field])) for (const id of fields[field]) own(id, "unit");
|
|
159
|
+
for (const field of KNOWN) known(fields[field], field);
|
|
160
|
+
place(fields.point, "its point");
|
|
161
|
+
const order = fields.order;
|
|
162
|
+
if (order && typeof order === "object") {
|
|
163
|
+
known(order.target, "target");
|
|
164
|
+
place(order.point, `its ${String(order.type)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/** No order sent again for the same thing to a unit still at it: a unit told the same every think stops and starts */
|
|
168
|
+
churn(sent, say) {
|
|
169
|
+
const { view, tick, session } = this.brain;
|
|
170
|
+
const window = 5 * session.tickRate;
|
|
171
|
+
for (const command of sent) {
|
|
172
|
+
const key = orderKey(command);
|
|
173
|
+
if (key === void 0 || command.queue) continue;
|
|
174
|
+
for (const id of command.entities) {
|
|
175
|
+
const last = this.orders.get(id);
|
|
176
|
+
const busy = view.byId(id)?.own?.order === kindOf(command);
|
|
177
|
+
if (last && last.key === key && tick - last.tick <= window && busy) say("churn", `unit ${id} was sent ${key} again ${((tick - last.tick) / session.tickRate).toFixed(1)} s after, still at it`);
|
|
178
|
+
this.orders.set(id, {
|
|
179
|
+
key,
|
|
180
|
+
tick
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
for (const [id, last] of this.orders) if (tick - last.tick > window) this.orders.delete(id);
|
|
185
|
+
}
|
|
186
|
+
/** Builders, tasks, holds of money and blocked wants each end in time */
|
|
187
|
+
ends(say) {
|
|
188
|
+
const { view, squads, purse, plans, tick, session } = this.brain;
|
|
189
|
+
const going = /* @__PURE__ */ new Map();
|
|
190
|
+
for (const unit of view.mine) if (unit.own?.build) going.set(`builder:${unit.id}:${unit.own.build}`, {
|
|
191
|
+
limit: this.limits.builder,
|
|
192
|
+
what: `worker ${unit.id} on its way to build a ${unit.own.build}`
|
|
193
|
+
});
|
|
194
|
+
for (const squad of squads.all()) {
|
|
195
|
+
const task = squad.data.task;
|
|
196
|
+
if (!task || task.done !== void 0 || task.phase === "holding" || task.kind === "hold" || task.kind === "escort" && task.phase === "going" || squads.taskDef(task.kind)?.standing) continue;
|
|
197
|
+
going.set(`task:${squad.name}:${task.kind}:${task.since}:${task.phase}:${squad.data.units.join(",")}`, {
|
|
198
|
+
limit: this.limits.task,
|
|
199
|
+
what: `the squad ${squad.name} ${task.phase} on its ${task.kind}`,
|
|
200
|
+
from: task.phaseSince
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
for (const id in purse.holdings()) going.set(`hold:${id}`, {
|
|
204
|
+
limit: this.limits.reservation,
|
|
205
|
+
what: `money held for ${id}`
|
|
206
|
+
});
|
|
207
|
+
for (const want of plans.build.report().wants) if (want.status.startsWith("blocked") || !want.keep && /^no (spot|builder)/.test(want.status)) going.set(`want:${want.id}:${want.status}`, {
|
|
208
|
+
limit: this.limits.want,
|
|
209
|
+
what: `the want ${want.id} (${want.status})`
|
|
210
|
+
});
|
|
211
|
+
for (const key of [...this.since.keys()]) if (!going.has(key)) this.since.delete(key);
|
|
212
|
+
for (const key of [...this.said]) if (!going.has(key)) this.said.delete(key);
|
|
213
|
+
for (const [key, { limit, what, from }] of going) {
|
|
214
|
+
if (!this.since.has(key)) this.since.set(key, from ?? tick);
|
|
215
|
+
const seconds = (tick - this.since.get(key)) / session.tickRate;
|
|
216
|
+
if (seconds > limit && !this.said.has(key)) {
|
|
217
|
+
this.said.add(key);
|
|
218
|
+
say("ends", `${what} for ${Math.round(seconds)} s, past ${limit}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
/**
|
|
224
|
+
* Checks a brain against the invariants after each of its thinks from now on: throws at the first broken rule, or tells
|
|
225
|
+
* `onViolation` of each. Returns the checks (their `violations`) and the way to stop: what `watchComputer` does for a
|
|
226
|
+
* seat, and every brain does by itself while `debugChecks.ai` is on
|
|
227
|
+
*
|
|
228
|
+
* const checks = checkBrain(brain, { onViolation: (each) => found.push(each) })
|
|
229
|
+
*
|
|
230
|
+
* @beta
|
|
231
|
+
*/
|
|
232
|
+
function checkBrain(brain, options = {}) {
|
|
233
|
+
const checks = new BrainChecks(brain, options);
|
|
234
|
+
return {
|
|
235
|
+
checks,
|
|
236
|
+
stop: brain.watch(checks)
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
//#endregion
|
|
240
|
+
export { AI_INVARIANTS, BrainChecks, checkBrain, orderKey };
|
|
241
|
+
|
|
242
|
+
//# sourceMappingURL=checks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"checks.js","names":[],"sources":["../../src/ai/checks.ts"],"sourcesContent":["import type { PlayerId } from '#engine/entity'\nimport type { Vec2 } from '#engine/math'\nimport { isCommand, type PlayerCommand } from '#engine/session/protocol'\nimport type { Brain, BrainWatcher } from './brain'\n\n/*\n * What must always hold of a computer player, checked after every think (AI plan, E3). A brain can play badly and\n * still be right; these are the things that, broken, make it wrong in a way no single game shows until much later: a\n * unit told two things at once, gold spent twice, a `NaN` that quietly picks a goal, a command about something it\n * could not see, an order sent again and again to a unit already carrying it out, a builder or a squad that never\n * finishes. Each is one rule below, named, with the failure it catches:\n *\n * | Invariant | What must hold | Catches |\n * | ---------- | ------------------------------------------------------------------------------------ | ------------------------------------------ |\n * | `squads` | A unit is in at most one squad, and held by at most one goal, rule or module | Two orders a think, lost units |\n * | `purse` | What the think spent, with what builders on their way will pay, is what it had | Spending twice, a plan that waits for ever |\n * | `finite` | Every score, consideration, strength and odds is a finite number | `NaN` picking a goal silently |\n * | `commands` | Every command names entities and points its picture of the game has | A fog leak, a stale target |\n * | `churn` | An order is not sent again to a unit still carrying the same one out | Units that stop and start |\n * | `ends` | A builder, a squad's task, a hold of money and a stuck want each end in time | Builders, squads and plans stuck |\n *\n * The seventh, that a game saved and loaded makes the same decisions from then on, needs two worlds: it is\n * `checkComputerSave` in `@v12-engine/engine/testing`. Nothing here decides anything or touches the world: the checks\n * read the brain's picture of the game and what it sent, through `Brain.watch`. They are on in a test that asks\n * (`watchComputer`, `checkBrain`), and in every brain while the engine's own checks are (`debugChecks.ai`).\n */\n\n/**\n * The name of a rule every computer player keeps (`checkBrain`): see the table in `ai/checks.ts`\n *\n * @beta\n */\nexport type AiInvariant = 'squads' | 'purse' | 'finite' | 'commands' | 'churn' | 'ends'\n\n/**\n * Every invariant, in the order they are checked\n *\n * @beta\n */\nexport const AI_INVARIANTS: readonly AiInvariant[] = ['squads', 'purse', 'finite', 'commands', 'churn', 'ends']\n\n/**\n * A rule a computer player broke, at a think: which, whose, when, and what exactly, in words\n *\n * @beta\n */\nexport interface AiViolation {\n\t/** Which rule was broken */\n\tinvariant: AiInvariant\n\t/** The computer player whose think broke the rule */\n\tplayer: PlayerId\n\t/** The tick of the think's picture of the game */\n\ttick: number\n\t/** What exactly went wrong, in words */\n\tmessage: string\n}\n\n/**\n * Seconds a thing may go on before the `ends` invariant says it is stuck (`AiCheckOptions.limits`)\n *\n * @beta\n */\nexport interface AiEndLimits {\n\t/** A worker on its way to build, not yet begun: 120 when left out (the build plan gives up on a site after 60) */\n\tbuilder?: number\n\t/** A squad's task in one phase (going, gathering, fighting): 300 when left out. A squad holding a place or going with a unit never times out */\n\ttask?: number\n\t/** Money held under one name, think after think: 600 when left out */\n\treservation?: number\n\t/**\n\t * A want of the build plan that cannot be got to (a requirement out of reach), or one that does not stand finding no\n\t * spot or builder: 600 when left out. A standing want waiting for room is no fault\n\t */\n\twant?: number\n}\n\n/**\n * How a brain is checked (`checkBrain`, `watchComputer`)\n *\n * @beta\n */\nexport interface AiCheckOptions {\n\t/** The invariants to check: all of them when left out */\n\tinvariants?: readonly AiInvariant[]\n\t/** How long a thing may go on before it counts as stuck (`ends`) */\n\tlimits?: AiEndLimits\n\t/** Told of each broken rule as it is found. Where none is given, `checkBrain` throws at the first */\n\tonViolation?: (violation: AiViolation) => void\n}\n\n/** Seconds within which an order sent again for the same thing is churn: what `AiStats.churn` counts by too */\nexport const CHURN_WINDOW = 5\n\nconst LIMITS: Required<AiEndLimits> = { builder: 120, task: 300, reservation: 600, want: 600 }\n\n/**\n * What an order to a unit is for, as a key: its kind, its target, its place to the cell. Two orders with the same key\n * are the same order. None for a command that is not an order to units (a train, a ping, a game's own)\n */\nexport function orderKey(command: PlayerCommand): string | undefined {\n\tif (isCommand(command, 'order')) {\n\t\tconst { type, target, point } = command.order\n\t\treturn `${type}:${target ?? ''}:${point ? `${Math.round(point.x)},${Math.round(point.z)}` : ''}`\n\t}\n\tif (isCommand(command, 'attack') && command.point)\n\t\treturn `attack:${command.target ?? ''}:${Math.round(command.point.x)},${Math.round(command.point.z)}`\n\treturn undefined\n}\n\n/** The kind of order a unit carries out for a command: an attack on a target, an attack-move, or the order's own */\nfunction kindOf(command: PlayerCommand): string | undefined {\n\tif (isCommand(command, 'attack')) return command.target === undefined ? 'attackMove' : 'attack'\n\treturn isCommand(command, 'order') ? command.order.type : undefined\n}\n\n/** Fields of the engine's commands that name an entity of the sender's own, one or a list (a hero apart: a dead one is in the player's list of heroes, not on the ground) */\nconst OWN_ONE = ['building', 'entity'] as const\nconst OWN_LIST = ['entities', 'buildings'] as const\n/** Fields that name any entity the sender knows of: a target, a shop */\nconst KNOWN = ['target', 'shop'] as const\n\n/**\n * Checks a brain against the invariants after each of its thinks, through what it is watched by (`Brain.watch`): what\n * `checkBrain` makes, and keeps the broken rules in `violations`\n *\n * @beta\n */\nexport class BrainChecks implements BrainWatcher {\n\t/** Every rule found broken, in the order found */\n\treadonly violations: AiViolation[] = []\n\tprivate readonly invariants: ReadonlySet<AiInvariant>\n\tprivate readonly limits: Required<AiEndLimits>\n\t/** What it sent since its last think ended */\n\tprivate commands: PlayerCommand[] = []\n\t/** The last order each unit was sent, with the tick of the think */\n\tprivate readonly orders = new Map<number, { key: string; tick: number }>()\n\t/** Since when each thing that has to end has been going on, by a key of what it is */\n\tprivate readonly since = new Map<string, number>()\n\t/** What was said stuck already: once a time it is stuck, not every think after */\n\tprivate readonly said = new Set<string>()\n\n\tconstructor(\n\t\t/** The brain the checks watch */\n\t\treadonly brain: Brain,\n\t\tprivate readonly options: AiCheckOptions = {}\n\t) {\n\t\tthis.invariants = new Set(options.invariants ?? AI_INVARIANTS)\n\t\tthis.limits = { ...LIMITS, ...options.limits }\n\t}\n\n\t/** Records a command the brain sent in this think, for the checks of `commands` and `churn` at the end of it */\n\tsent(command: PlayerCommand): void {\n\t\tthis.commands.push(command)\n\t}\n\n\t/** Ends a think: runs the checks that are on over what was sent and returns nothing; found violations are kept */\n\tthought(): void {\n\t\tconst sent = this.commands\n\t\tthis.commands = []\n\t\tconst found: AiViolation[] = []\n\t\tconst say = (invariant: AiInvariant, message: string) => found.push({ invariant, player: this.brain.playerId, tick: this.brain.tick, message })\n\t\tif (this.invariants.has('squads')) this.squads(say)\n\t\tif (this.invariants.has('purse')) this.purse(say)\n\t\tif (this.invariants.has('finite')) this.finite(say)\n\t\tif (this.invariants.has('commands')) for (const command of sent) this.command(command, say)\n\t\tif (this.invariants.has('churn')) this.churn(sent, say)\n\t\tif (this.invariants.has('ends')) this.ends(say)\n\t\tfor (const violation of found) {\n\t\t\tthis.violations.push(violation)\n\t\t\tif (this.options.onViolation) this.options.onViolation(violation)\n\t\t\telse throw new Error(`The computer player ${violation.player} broke \"${violation.invariant}\" at tick ${violation.tick}: ${violation.message}`)\n\t\t}\n\t}\n\n\t/** A unit in at most one squad and one hold: two would give it two orders a think */\n\tprivate squads(say: (invariant: AiInvariant, message: string) => void): void {\n\t\tconst { squads, goals } = this.brain\n\t\tconst inSquad = new Map<number, string>()\n\t\tfor (const squad of squads.all())\n\t\t\tfor (const id of squad.data.units) {\n\t\t\t\tconst other = inSquad.get(id)\n\t\t\t\tif (other !== undefined) say('squads', `unit ${id} is in the squads ${other} and ${squad.name}`)\n\t\t\t\telse inSquad.set(id, squad.name)\n\t\t\t}\n\t\tconst holder = new Map<number, string>()\n\t\tconst holds = goals.all()\n\t\tfor (const id in holds)\n\t\t\tfor (const unit of holds[id].units) {\n\t\t\t\tconst other = holder.get(unit)\n\t\t\t\tif (other !== undefined) say('squads', `unit ${unit} is held by ${other} and ${id}`)\n\t\t\t\telse holder.set(unit, id)\n\t\t\t}\n\t}\n\n\t/** What the think spent and what its builders will pay never more than what it had: else the same gold went twice */\n\tprivate purse(say: (invariant: AiInvariant, message: string) => void): void {\n\t\tconst { purse, view } = this.brain\n\t\tconst had = view.me.resources\n\t\tfor (const kind of new Set([...Object.keys(purse.spent), ...Object.keys(purse.promised)])) {\n\t\t\tconst owed = (purse.spent[kind] ?? 0) + (purse.promised[kind] ?? 0)\n\t\t\tif (owed > (had[kind] ?? 0))\n\t\t\t\tsay('purse', `it spent ${purse.spent[kind] ?? 0} ${kind} and its builders will pay ${purse.promised[kind] ?? 0}, of ${had[kind] ?? 0}`)\n\t\t}\n\t}\n\n\t/** Every number a decision is made of is a number: a `NaN` compares false with everything and picks silently */\n\tprivate finite(say: (invariant: AiInvariant, message: string) => void): void {\n\t\tconst { goals, squads, view } = this.brain\n\t\tfor (const goal of goals.report()) {\n\t\t\tif (!Number.isFinite(goal.score)) say('finite', `the goal ${goal.id} scores ${goal.score}`)\n\t\t\tfor (const each of goal.considerations)\n\t\t\t\tif (!Number.isFinite(each.value) || !Number.isFinite(each.curved))\n\t\t\t\t\tsay('finite', `the goal ${goal.id}'s ${each.name} is ${each.value} (${each.curved} curved)`)\n\t\t}\n\t\tfor (const squad of squads.all()) {\n\t\t\tconst strength = squad.strength()\n\t\t\tif (!Number.isFinite(strength)) say('finite', `the squad ${squad.name} is ${strength} strong`)\n\t\t}\n\t\tconst odds = view.strength.odds(view.mine.fighters(), view.enemies)\n\t\tif (!Number.isFinite(odds)) say('finite', `its odds against what it sees are ${odds}`)\n\t}\n\n\t/** A command about what its picture of the game has: its own units by theirs, anyone else's as seen, places on the map */\n\tprivate command(command: PlayerCommand, say: (invariant: AiInvariant, message: string) => void): void {\n\t\tconst { view, session } = this.brain\n\t\tconst fields = command as unknown as Record<string, unknown>\n\t\tconst what = `its ${command.type} command`\n\t\tconst own = (id: unknown, field: string) => {\n\t\t\tif (typeof id !== 'number') return\n\t\t\tif (!view.byId(id)) say('commands', `${what} names ${field} ${id}, which it does not see`)\n\t\t\telse if (!view.mine.has(id)) say('commands', `${what} names ${field} ${id}, which is not its own`)\n\t\t}\n\t\tconst known = (id: unknown, field: string) => {\n\t\t\tif (typeof id === 'number' && !view.byId(id)) say('commands', `${what} names a ${field} ${id} it does not see`)\n\t\t}\n\t\tconst place = (at: unknown, field: string) => {\n\t\t\tif (!at || typeof at !== 'object') return\n\t\t\tconst { x, z } = at as Vec2\n\t\t\tconst { width, depth } = session.terrain\n\t\t\tif (!Number.isFinite(x) || !Number.isFinite(z) || x < 0 || z < 0 || x > width || z > depth)\n\t\t\t\tsay('commands', `${what} sends ${field} to (${x}, ${z}), off the map`)\n\t\t}\n\t\tfor (const field of OWN_ONE) own(fields[field], field)\n\t\tif (typeof fields.hero === 'number' && !view.me.heroes.some((hero) => hero.id === fields.hero)) own(fields.hero, 'hero')\n\t\tfor (const field of OWN_LIST) if (Array.isArray(fields[field])) for (const id of fields[field] as unknown[]) own(id, 'unit')\n\t\tfor (const field of KNOWN) known(fields[field], field)\n\t\tplace(fields.point, 'its point')\n\t\tconst order = fields.order as Record<string, unknown> | undefined\n\t\tif (order && typeof order === 'object') {\n\t\t\tknown(order.target, 'target')\n\t\t\tplace(order.point, `its ${String(order.type)}`)\n\t\t}\n\t}\n\n\t/** No order sent again for the same thing to a unit still at it: a unit told the same every think stops and starts */\n\tprivate churn(sent: readonly PlayerCommand[], say: (invariant: AiInvariant, message: string) => void): void {\n\t\tconst { view, tick, session } = this.brain\n\t\tconst window = CHURN_WINDOW * session.tickRate\n\t\tfor (const command of sent) {\n\t\t\tconst key = orderKey(command)\n\t\t\tif (key === undefined || (command as { queue?: boolean }).queue) continue\n\t\t\tfor (const id of (command as { entities: readonly number[] }).entities) {\n\t\t\t\tconst last = this.orders.get(id)\n\t\t\t\t// still at it: carrying out an order of that kind. One that stands idle lost it (its target fell, its path\n\t\t\t\t// ended), one doing something else was taken off it: telling it again is right\n\t\t\t\tconst busy = view.byId(id)?.own?.order === kindOf(command)\n\t\t\t\tif (last && last.key === key && tick - last.tick <= window && busy)\n\t\t\t\t\tsay('churn', `unit ${id} was sent ${key} again ${((tick - last.tick) / session.tickRate).toFixed(1)} s after, still at it`)\n\t\t\t\tthis.orders.set(id, { key, tick })\n\t\t\t}\n\t\t}\n\t\tfor (const [id, last] of this.orders) if (tick - last.tick > window) this.orders.delete(id)\n\t}\n\n\t/** Builders, tasks, holds of money and blocked wants each end in time */\n\tprivate ends(say: (invariant: AiInvariant, message: string) => void): void {\n\t\tconst { view, squads, purse, plans, tick, session } = this.brain\n\t\tconst going = new Map<string, { limit: number; what: string; from?: number }>()\n\t\tfor (const unit of view.mine)\n\t\t\tif (unit.own?.build)\n\t\t\t\tgoing.set(`builder:${unit.id}:${unit.own.build}`, {\n\t\t\t\t\tlimit: this.limits.builder,\n\t\t\t\t\twhat: `worker ${unit.id} on its way to build a ${unit.own.build}`\n\t\t\t\t})\n\t\tfor (const squad of squads.all()) {\n\t\t\tconst task = squad.data.task\n\t\t\t// holding a place, or going with a unit (it follows as long as the unit lives): standing duties, never stuck; and\n\t\t\t// a task that says it is one (`TaskDef.standing`)\n\t\t\tif (\n\t\t\t\t!task ||\n\t\t\t\ttask.done !== undefined ||\n\t\t\t\ttask.phase === 'holding' ||\n\t\t\t\ttask.kind === 'hold' ||\n\t\t\t\t(task.kind === 'escort' && task.phase === 'going') ||\n\t\t\t\tsquads.taskDef(task.kind)?.standing\n\t\t\t)\n\t\t\t\tcontinue\n\t\t\t// the same units on the same task in the same phase: a squad whose units come and go (those fleeing) starts again\n\t\t\tgoing.set(`task:${squad.name}:${task.kind}:${task.since}:${task.phase}:${squad.data.units.join(',')}`, {\n\t\t\t\tlimit: this.limits.task,\n\t\t\t\twhat: `the squad ${squad.name} ${task.phase} on its ${task.kind}`,\n\t\t\t\tfrom: task.phaseSince\n\t\t\t})\n\t\t}\n\t\tfor (const id in purse.holdings()) going.set(`hold:${id}`, { limit: this.limits.reservation, what: `money held for ${id}` })\n\t\t// a want that cannot be got to (its tech out of reach), or one that does not stand and finds no spot or builder: a\n\t\t// standing want (`keep`) waits for room as long as it takes, which is no fault\n\t\tfor (const want of plans.build.report().wants) {\n\t\t\tconst stuck = want.status.startsWith('blocked') || (!want.keep && /^no (spot|builder)/.test(want.status))\n\t\t\tif (stuck) going.set(`want:${want.id}:${want.status}`, { limit: this.limits.want, what: `the want ${want.id} (${want.status})` })\n\t\t}\n\t\tfor (const key of [...this.since.keys()]) if (!going.has(key)) this.since.delete(key)\n\t\tfor (const key of [...this.said]) if (!going.has(key)) this.said.delete(key)\n\t\tfor (const [key, { limit, what, from }] of going) {\n\t\t\tif (!this.since.has(key)) this.since.set(key, from ?? tick)\n\t\t\tconst seconds = (tick - this.since.get(key)!) / session.tickRate\n\t\t\tif (seconds > limit && !this.said.has(key)) {\n\t\t\t\tthis.said.add(key)\n\t\t\t\tsay('ends', `${what} for ${Math.round(seconds)} s, past ${limit}`)\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Checks a brain against the invariants after each of its thinks from now on: throws at the first broken rule, or tells\n * `onViolation` of each. Returns the checks (their `violations`) and the way to stop: what `watchComputer` does for a\n * seat, and every brain does by itself while `debugChecks.ai` is on\n *\n * const checks = checkBrain(brain, { onViolation: (each) => found.push(each) })\n *\n * @beta\n */\nexport function checkBrain(brain: Brain, options: AiCheckOptions = {}): { checks: BrainChecks; stop: () => void } {\n\tconst checks = new BrainChecks(brain, options)\n\treturn { checks, stop: brain.watch(checks) }\n}\n"],"mappings":"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"}
|