Skip to content

AI base planning and building

Lays a computer house's base out as an ordered node list, then resolves what to build next and where to put it.

Features & systemsScenarios & AIAI & teams

A computer house does not weigh up what to build next. It works down a base node list: an ordered list in which each entry — a node — names one BuildingType and the cell that structure belongs on. Position in the list is the build order, and nothing else bears on it: there is no priority scoring, and the house always takes the first node it has not built yet.

Not every node names a structure at a place. A cell of 0,0 names no cell, and hands the spot to the placement search instead. And three of the values that would otherwise name a BuildingType are instructions to the planner: -1 asks for a base defense whose type the planner chooses, -2 stops the scan there, and -3 runs the perimeter wall planner.

The list is carried in the house’s own map section, not in a section of its own. NodeCount gives the entry count and entries are read in order from zero-padded three-digit keys; a value beginning with - is one of the three instructions, otherwise the first field is an ObjectType ID and the next two are the cell X and Y. Structures a scenario starts with are ordinary map objects, which the node list then matches.

map file
[GDI]
NodeCount=4
000=MYCONST,42,58 ; example construction yard BuildingType
001=MYWEAP,45,58 ; example war factory BuildingType
002=-1,0,0 ; base defense; the planner picks the type and the cell
003=MYPOWR,0,0 ; example power plant BuildingType; cell picked at build time

A house whose list is empty generates one when either of two things happens: an MCV of a non-human house deploys into a ConstructionYard=yes BuildingType outside a campaign game, or a house passes to the computer. A scenario-supplied list suppresses generation entirely.

  1. Candidates. A BuildingType is a candidate while all of this holds:
    • its Owner includes this house;
    • it is AIBuildThis=yes;
    • its TechLevel is within the house’s scenario tech level;
    • it is not Weeder=yes, or the map carries a veinhole monster;
    • it is not the excluded plug — under the Firestorm addon one of the hard-coded IDs GAPLUG2, GAPLUG3 and GAPLUG4 is drawn at random and left out.
  2. Seed. Entry 0 of BuildConst, which must also pass that filter, then unconditionally the first BuildPower entry the house may own. The first ownable BuildBarracks entry moves to the head of the candidate list and the first ownable BuildWeapons entry to second place.
  3. Expansion. Repeated passes append every candidate whose Prerequisite list the queue already satisfies, resolving a generic prerequisite through BuildWeapons, BuildBarracks, BuildRadar or BuildTech and treating the BuildConst construction yard as always satisfied. A Helipad=yes type is appended one to three extra times; the hard-coded GAPLUG waits for a pass that adds nothing else.
  4. Refineries. 2 - Difficulty extra copies of the first ownable BuildRefinery entry, at random positions after the first refinery.
  5. Defenses. A build cost running from the cost of queue entries 1 and 2 accumulates entry by entry, and before each entry the plan calls for (cost - 2000) / 1500 defenses scaled by NodBaseDefenseCoefficient for a house named “Nod” and by GDIBaseDefenseCoefficient for every other house. Each unit of shortfall becomes a -1 placeholder, preceded by a WallTower node for GDI. When the house is Nod, or when AIBuildsWalls=no, further placeholders follow: (3 - Difficulty) * 3 of them for a house named GDI, and (3 - Difficulty) * 2 for every other house.
  6. Wall. A -3 node closes the list when AIBuildsWalls=yes and the house is either not Nod or has NodAIBuildsWalls=yes.

Difficulty in steps 4 and 5 is the house’s own difficulty slot[Easy] is 0, [Normal] 1 and [Difficult] 2 — and a computer house is handed the inverse of the setting the player chose. The table works both terms out for each setting. Read the two right-hand columns downward: the harder the player set the game, the more extra refineries the plan carries and the larger the placeholder counts in step 5 come out.

Setting chosenSlot the computer house holds2 - Difficulty, the extra refineries3 - Difficulty, the term in the placeholder counts
Easy2, the [Difficult] section01
Normal1, the [Normal] section12
Hard0, the [Easy] section23

The same 3 - Difficulty term caps the wall defenses a GDI house appends after its perimeter wall.

A house takes the first node not counted as built while all of this holds:

  • it is not a human player’s house;
  • its country is not MultiplayPassive=yes;
  • its production mode allows structure work;
  • it has no structure already committed;
  • it owns a construction yard.

A node counts as built under any of these:

  • a building of the node’s own type, owned by this house, stands on the node’s cell;
  • the node names a wall type and its cell carries that wall’s overlay;
  • the node names a wall type and its cell carries any building.

A node whose cell is 0,0 never counts as built. A -3 node deletes itself before running the wall planner, and a -1 node or a cell-less WallTower node is deleted when the defense planner fails on it, taking the following node with it when it is a wall tower. Any other node becomes the house’s pending structure. Because the first unbuilt node is always the one taken, a node no owned factory can produce holds up every node behind it.

Placement is resolved when the finished structure leaves the construction yard. A real cell on the matching node is used when the type is an upgrade or the compactness test accepts that cell, and otherwise a search runs and writes its result back into the node.

Placing any structure flags its footprint, expanded by AIBaseSpacing on all sides, as occupied by its house and grows that house’s base rectangle to contain it. The search ranks the frontier of that area — cells with at least one, but not all eight, occupied neighbors — nearest the base center for an ordinary structure and thinnest in existing defense coverage for a base defense, then steps outward from each ranked cell away from the mass of the base, clear of the structure’s own footprint plus AIBaseSpacing. A step is accepted while all of this holds:

  • the step’s footprint rectangle, grown by that spacing, holds no cell this house already occupies;
  • that same rectangle lies inside the playable area;
  • every cell of the structure’s own foundation is clear to build on, except that a type laying its own tile underneath itself needs only one clear cell;
  • the ground height at the step is within 2 of the height under the base center;
  • the compactness test accepts the step.

The first two terms are settled together as one test, and that test is skipped altogether on a second pass over the ranked cells, so a cramped base still builds. A search that accepts nothing returns cell 0,0.

That compactness test passes unconditionally in a campaign game, so campaign bases spread wherever the other tests allow. Every other session type requires an already-occupied cell within the candidate footprint padded by AIBaseSpacing — one cell beyond that margin to the north and west, and twice the spacing plus one to the south and east — welding a skirmish or multiplayer base to what it holds.

An allied ground object standing in the placement zone is ordered to move and the factory waits PlacementDelay minutes. A permanent obstruction — an overlay, a terrain object, a building that cannot take the structure as an upgrade, or another house’s object — and a failed placement both abandon the structure: the factory refunds the cost already paid, deletes the object under construction, and clears the pending structure. The node is deleted when its type is a wall or a gate, and otherwise every node claiming that cell has its cell reset to 0,0. After a WallTower is placed, the next base-defense node moves onto the tower’s cell.

The planner is handed a list of cells the base is expected to be attacked through: each one is a threat cell, and the list as a whole is the house’s threat ring. Exactly one thing ever fills it — the perimeter wall planner records the wall cells it laid, and only for a house named GDI — so every other house reaches the defense planner with no threat ring at all. The qualifications below all turn on which of those two cases applies.

A defense node is filled in against the quadrant of the base that needs it most. Each owned building’s anti-air, anti-armor and anti-infantry values are summed per quadrant and stamped into three per-cell coverage maps out to radius 6, falling off as value / ((distance - 1) * 0.1 + 1), and the lowest-total quadrant wins — restricted, where a threat ring was supplied, to the quadrants holding at least one of its cells. The category is the one least represented in that quadrant — ties resolve to anti-infantry first, then anti-armor over anti-air — because the predicted enemy composition it is measured against is a fixed 0.33 in each of the three.

A BuildingType is a candidate for that category while all of this holds:

  • the house may own it;
  • its value in that category is above zero;
  • its TechLevel is within the house’s reach;
  • its prerequisites are met by the non-defense buildings the house owns, plus WallTower for GDI.

An empty list falls back to anti-armor, then anti-infantry, then anti-air, and all three empty deletes the node. One candidate is drawn at random, weighted by 10000 / cost + its value in that category, so cheap defenses dominate. It consumes the best-scoring cell of the threat ring where one was supplied, and takes the placement search’s result otherwise.

What the node then receives depends on which kind of node it is. A -1 placeholder takes both the chosen type and the chosen cell. A WallTower node keeps its own type and takes only the cell, and the chosen defense is written into the node after it at that same cell — but only while that following node is still a -1 placeholder. In that case a wall node already claiming the cell is deleted as well, and only when a threat ring was supplied.

A BuildingType’s three category values are computed from rules once the weapons are loaded, and only for a type with IsBaseDefense=yes. From its primary weapon, damage is Damage / (ROF * 0.025) truncated to a whole number. An anti-aircraft projectile sets AntiAirValue to damage multiplied by the warhead’s Verses percentage against heavy armor; an anti-ground projectile sets AntiArmorValue from that same heavy figure and AntiInfantryValue from the Verses percentage against none. All three are capped at MaximumBaseDefenseValue. A type without IsBaseDefense=yes, or with no primary weapon, keeps all three at zero and never enters a candidate list; at runtime a building whose own value is zero reports the first non-zero value among its plugged-in upgrades instead.

The wall ring is the base rectangle grown by one cell on each side, walked along its four edges. A cell takes a wall while all of this holds:

  • its height is within 2 of the height under the base center;
  • neither it nor its outward neighbor is rock, water or ice;
  • it carries no overlay;
  • neither it nor its outward neighbor holds a building;
  • neither it nor its outward neighbor holds a terrain object;
  • its ramp is flat;
  • it lies inside the playable area.

A run becomes wall nodes once it reaches five cells, or sooner when an overlay or a ramp cuts it short.

Wall nodes come from the first ownable ConcreteWalls entry and are all appended before any gate node. Gate nodes — EWGates on the north and south edges, NSGates on the east and west — take the midpoint of a run and consume three wall slots each; a run cut short by an overlay or a ramp is laid as plain wall. For GDI the wall cells also become the threat ring the defense planner draws from, and pairs of a WallTower node and a -1 node are appended, 0.2 per wall node and capped at (3 - Difficulty) * GDIWallDefenseCoefficient+GDIWallDefense. The base rectangle then becomes the wall ring, so the next wall is planned one ring further out.

A power plant node is inserted immediately before the node the house is about to build while all of this holds:

  • the session is not a campaign game;
  • the node’s own drain added to the house’s current drain exceeds its current power output;
  • the node is not the BuildConst construction yard;
  • the node’s type draws power at all.

Which plant goes in depends on the house. GDI inserts GDIPowerTurbine when it owns a GDIPowerPlant with a free upgrade slot and a random draw falls under AIUseTurbineUpgradeProbability — a fraction of 1 that defaults to 1, so the turbine is taken whenever a slot is free unless the value is lowered — and GDIPowerPlant otherwise; every other house inserts NodAdvancedPower when the buildings it owns meet that type’s prerequisites, and NodRegularPower otherwise.

A house that cannot make money, again only outside campaign games, sells its base from the back of the node list forward until the proceeds cover a harvester — where it owns both a refinery and a war factory — or a refinery otherwise, abandons its factories, and either orders that harvester or inserts a refinery node at the current build position. Selling out the whole list without raising enough sends every unit it owns to hunt.

Rebuilding needs no separate mechanism: a destroyed structure stops matching its node, and the node becomes the next hole in the list. When a building is taken off the map, every other node claiming its cell has its cell cleared, and outside campaign games an IsBaseDefense=yes node is retired to a -1 placeholder so the planner picks a fresh type and cell for it.

[AI] carries a block of ratio and limit settings that no decision reads: RefineryRatio, RefineryLimit, BarracksRatio, BarracksLimit, WarRatio, WarLimit, DefenseRatio, DefenseLimit, AARatio, AALimit, TeslaRatio, TeslaLimit, HelipadRatio, HelipadLimit, AirstripRatio, AirstripLimit, BaseSizeAdd, InfantryReserve, InfantryBaseMult and PowerEmergency. The type lists BuildDefense, BuildPDefense, BuildAA and BuildHelipad are parsed but never consulted; base defenses come from the computed values above, not from a list. PowerSurplus is parsed into the rules but never reaches a house, whose own power margin stays at zero, so the power interjection fires on the first shortfall rather than on a configured cushion. PercentBuilt in a house’s map section is read and written back unchanged and controls nothing.

32 of 32

KeyDescriptionValueWhen omitted
AIBaseSpacingglobal rulesCells of clearance kept around a building when base ground is reserved and computer placements are searched.integer1
AIBuildThisBuildingTypeAllows a computer house to put the BuildingType into the base plan it generates.booleanno
AIBuildsWallsglobal rulesLets a computer house close its base plan with the node that runs the perimeter wall planner.booleanyes
AIUseTurbineUpgradeProbabilityglobal rulesThe chance that a computer house named "GDI" answers a power shortfall with a turbine upgrade rather than another power plant.floating point1
BuildBarracksglobal rulesThe barracks a computer house resolves a generic barracks prerequisite to, in order of preference.typelist(empty)
BuildConstglobal rulesThe construction yard BuildingType; only the first entry is read.typelist(empty)
BuildPowerglobal rulesThe power plants a computer house plans first, in order of preference.typelist(empty)
BuildRadarglobal rulesThe buildings a computer house resolves a generic radar prerequisite to, in order of preference.typelist(empty)
BuildRefineryglobal rulesThe refineries a computer house plans and measures its income against, in order of preference.typelist(empty)
BuildTechglobal rulesThe buildings a computer house resolves a generic tech prerequisite to, in order of preference.typelist(empty)
BuildWeaponsglobal rulesThe war factories a computer house resolves a generic factory prerequisite to, in order of preference.typelist(empty)
ConcreteWallsglobal rulesThe walls a computer house builds its base perimeter from, in order of preference.typelist(empty)
ConstructionYardBuildingTypeA BuildingType with this flag animates while it produces, refuses ordinary undeploy orders, and starts a computer house's base when an MCV deploys into it.booleanno
EWGatesglobal rulesThe gates a computer house fits into the east-west runs of its base perimeter, in order of preference.typelist(empty)
GDIBaseDefenseCoefficientglobal rulesScales how many base defenses every computer house except one named "Nod" plans against its accumulated build cost.floating point1
GDIPowerPlantglobal rulesThe BuildingType a computer house named "GDI" inserts to cover a power shortfall.classnone
GDIPowerTurbineglobal rulesThe BuildingType a computer house named "GDI" inserts to cover a power shortfall while it owns a power plant with a free upgrade slot.classnone
GDIWallDefenseglobal rulesThe fixed part of the cap on wall tower and base defense pairs a computer house named "GDI" appends after planning its perimeter wall.floating point6
GDIWallDefenseCoefficientglobal rulesThe difficulty-scaled part of the cap on wall tower and base defense pairs a computer house named "GDI" appends after planning its perimeter wall.floating point3
HelipadBuildingTypeLets a BuildingType accept an aircraft as a docking target and be approached at its docking cell rather than its center.booleanno
IsBaseDefenseBuildingType, TeamTypeGives a BuildingType the computed ratings the computer picks base defenses by, or counts a TeamType against its house's defensive team total.booleanno
MaximumBaseDefenseValueglobal rulesCaps each of the three defense values computed for a base defense BuildingType.integer60
NSGatesglobal rulesThe gates a computer house fits into the north-south runs of its base perimeter, in order of preference.typelist(empty)
NodAIBuildsWallsglobal rulesLets a computer house named "Nod" close its base plan with the node that runs the perimeter wall planner.booleanyes
NodAdvancedPowerglobal rulesThe BuildingType every computer house except one named "GDI" inserts to cover a power shortfall once it can build it.classnone
NodBaseDefenseCoefficientglobal rulesScales how many base defenses a computer house named "Nod" plans against its accumulated build cost.floating point1
NodRegularPowerglobal rulesThe BuildingType every computer house except one named "GDI" inserts to cover a power shortfall.classnone
NodeCountscenario basesThe number of base node entries read from the house's section.integer0
PlacementDelayglobal rulesMinutes a factory waits before trying again when the object it finished cannot leave yet.floating point.05
VersesWarheadTypeThe percentage of a weapon's damage the warhead delivers against each armor class, in armor order.string100%,100%,100%,100%,100%
WallTowerglobal rulesThe BuildingType that joins a brick or sandbag wall run from any direction.classnone
WeederBuildingType, UnitTypeMakes a BuildingType the structure vein harvesters dock at, or a UnitType a vein harvester.booleanno