Strategy opponent

Usage guide of rts.ai: data plans (easy, normal, hard) that play an rts.production faction by the player's own rules, the difficulty setting, service, wave event and the ai.attack, ai.build and ai.defend hooks.

Source: module-kits:kits/rts_ai/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs rts-aiMCP tool: gripforge_docs { "topic": "rts-ai" }

A computer opponent for strategy skirmishes (rts.ai) #

What it is for #

rts.ai is the computer side of an rts.production skirmish. It plays by the same rules as the player, through the same services, with no cheat and no extra resource. Every think seconds a faction puts its idle workers on the nearest nodes, trains workers up to a target, builds supply before a shortage, follows a build order, trains an army by weights, and sends attack waves on a schedule, larger each time. It can also defend its base. How it plays is data: its plans (easy, normal, hard) are documents you can edit or add to. The kit holds no rendering and no screen.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "rts.ai" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "rts.ai", config: { "difficulty": "hard" } }

It requires game.engine, rts.production and units.command. A faction is played by this kit when its ai field, in the factions of rts.production, names a plan. Targets: web and the Rust kit host.

First example #

<!-- test: kits/rts_ai/tests/rts_ai.rs::a_plan_can_be_chosen_for_a_faction_and_an_unknown_one_is_refused --> With the default plans, a computer faction bot plays easy. Calling the service setPlan("bot", "hard") answers true and plan("bot") now returns the hard plan. setPlan("bot", "impossible") or a non-string answers false and leaves the plan unchanged. factions() lists only the computer factions, with { faction, plan, step, wave, nextWave }; plan returns null for the player's own faction or an unknown one.

How it plays #

Every think seconds:

  1. Idle workers go to the nearest node of the item furthest below its gather share (the rest go to the first item).
  2. A supply building goes up when fewer than supplyMargin supply are free (house names it; default: the cheapest one that gives supply).
  3. Workers are trained up to workers.
  4. The next building of build is placed as soon as it is allowed and paid for, on a free spot around the base.
  5. Each finished building trains the army type furthest below its army weight (two in queue at most).
  6. When a wave is due, the idle army attack-moves to the nearest enemy building: at once when it has the planned size, else after 30 seconds with what it has.

With defend, a strike on its base sends its idle army at the attacker.

Data: the rts_ai_plans collection #

FieldMeaning
id, namePlan id and name (required, like every field below).
thinkSeconds between two decisions (0.2 to 30).
workersWorkers it trains up to.
gatherShare of workers per item, e.g. { "ore": 0.65, "wood": 0.35 } (optional).
supplyMarginBuilds supply when fewer are free.
houseThe supply building (optional).
buildBuild order, one building after the other.
armyList of { unit, weight }.
wavesThe first waves: { at, size }, match seconds and units.
every, growthSeconds between later waves (10 to 3600) and units added to each later wave.
defendWhether the idle army answers a strike on the base.

Defaults shipped: easy (think 3, 7 workers, one wave at 300 s of 3 units, no defence), normal (think 1.5, 10 workers, first wave at 210 s of 4, defends), hard (think 0.75, 14 workers, first wave at 160 s of 5, defends). A project that defines rts_ai_plans uses its own plans.

Settings and defaults #

SettingTypeDefaultMeaning
difficultystringemptyA plan every AI faction plays. Empty: the ai of each faction in rts.production.

Service and events #

Service rts.ai:

  • factions(): [{ faction, plan, step, wave, nextWave }].
  • plan(faction): the plan document, or null.
  • setPlan(faction, plan): true when the plan exists.
EventPayload
rts.ai.wave{ faction, wave, size, planned, at, target }: a wave left, with its units, the size the plan asked for, the match time and where it goes.

It listens to combat.damaged. The decision clock, the build order step and the waves sent are saved with the game, and so is the plan chosen by setPlan.

Hooks and modding #

HookPayloadMutableCancelled
ai.attack{ faction, plan, wave, size, planned, at, x, z }x, zThe wave is skipped; the next comes at its time.
ai.build{ faction, plan, type, reason, step, x, z }noneA build order step is skipped; a supply building is not asked again while the shortage lasts. reason is order or supply.
ai.defend{ faction, plan, attacker, target, size, x, z }noneThe army stays where it is. Asked at most every 3 seconds per faction.
{ "id": "truce", "hook": "ai.attack", "if": [{ "var": "story.truce", "op": "==", "value": true }], "cancel": true }

A mod may override difficulty and add, patch or remove documents of rts_ai_plans.

When it does not do what you expect #

  • A faction does nothing: its ai names no plan, or the match clock has not started, or the game is over.
  • Waves are small or late: the army is below the planned size, so the wave leaves 30 seconds after it was due with what exists.
  • A plan with no waves still attacks: the schedule then starts at 300 seconds with 3 units, spaced by every and growth.
  • setPlan answers false: the plan id is not in the plans, or is not a string.

Limits #

  • It uses only the services of rts.production, units.command and resource.nodes; it cannot do what the player cannot.
  • Its targets are enemy buildings; there is no scouting or tactics beyond waves and defence.

Versions #

Kit 1.1.0; service rts.ai 1.0.0. Hooks and moddable plans since 1.1.0 (game.engine 1.2).