Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Unit AI (UNITAI.C)

The computer players’ unit planning is in src/UNITAI.C, which compiles to the resident segment F15FB (16,266 bytes). It has two halves:

  • unit_phase(player) runs once per civilization per turn. It measures strength on every continent, decides a plan for each continent, chooses the government and sends idle units towards targets.
  • ai_order(player, unit) runs for each unit as it moves. It returns the key the unit would press: see Unit Orders.

game_turn() in TURN.C processes each civilization in turn: civ_economy() for its cities, then city_phase(), unit_phase(), unit_cleanup() and finally ai_turn() in CONTROL.C, which moves the units. ai_turn() is used for the human too; for a computer player it calls ai_order() instead of reading the keyboard.

Unit roles

Each unit type has a role in utypes (GAME.C). The AI matches units to targets by role.

RoleMeaningUnit types
0SettleSettlers
1AttackLegion, Cavalry, Knights, Catapult, Cannon, Chariot, Armor, Artillery, Bomber, Nuclear
2DefendMilitia, Phalanx, Musketeers, Riflemen, Mech. Inf.
3Naval combatIronclad, Cruiser, Battleship, Submarine, Carrier
4Air superiorityFighter
5TransportTrireme, Sail, Frigate, Transport
6SpecialDiplomat, Caravan

Strength tables

unit_phase() first rebuilds per-continent tables for the civilization. Continents are the region numbers 1–14 from world generation.

TableContents
defenders_island[civ][region]Sum of the defence factors of land-based units and city garrisons
civ_cont_attackers[civ][region]Sum of the attack factors of the same units
ai_continent_towns[civ][region]Number of cities
military_table[civ]Sum of attack plus defence over all units except Nuclear
troops_total[civ], civ_settlers_n[civ]Unit counts

A land unit at sea is marked busy (status bit 0x10) and left out of the continent tables. The pass also builds fog_map, a 20 × 13 grid of 4 × 4 tile blocks. Bit 0 marks a block containing one of the civilization’s cities and bit 1 << (role + 1) a block containing a unit of that role. civ_held_cells counts the occupied blocks.

The same loop over cities adds targets. Each foreign city that the civilization may attack becomes an attack target (kind 1). Its priority is 3 with City Walls, otherwise 5. A city with no units on its tile and no garrison becomes a defence target (kind 2): priority 4 for the civilization’s own cities, 2 for others. A foreign city qualifies as an attack target if it is not covered by peace, and if it is visible, belongs to another AI, or turn_tick * difficulty exceeds 200. One city in four is skipped on each turn.

Continent plans

For each continent, unit_phase() sets civ_area_plan[civ][region]:

PlanMeaningCondition
0DevelopNone of the conditions below
5Expand overseasThe civilization knows Map Making, and the continent is crowded: total defence is more than half its land area, or towns × 6 + 2 exceed its city sites
1AttackAn enemy (at war, or with cancellation planned) is present that the civilization can outfight: its defence is below the civilization’s attack, or its attack is below the civilization’s defence. Barbarian cities also count.
2DefendAn enemy is present that the civilization cannot outfight, or the human, although at peace, has more attack there than half the civilization’s attack plus its defence
1AttackThe civilization has no units or cities on the continent, and does not share it only with civilizations at peace with it

The rules are applied in this order, and a later rule overrides an earlier one. Enemies count only if they have defenders on the continent and either cities there or more than a quarter of the civilization’s attack. When a plan changes, retarget() makes every city of the civilization on that continent choose its production again.

The tables also set near_human when the civilization has defenders on a continent where the human has more than one city.

After the plans, Barbarians stop here, and so do human players. A computer civilization with no cities or settlers is removed with kill_civ(). Government is then reconsidered every eighth turn (see AI Diplomacy).

Targets and sites

Each civilization has two lists:

  • Targets: 32 entries of position, kind (a unit role) and priority (civ_targets_x, civ_targets_y, objectives_type, aim_count). add_target() keeps them sorted by priority and ignores a duplicate of equal or lower priority. Units add targets as they move.
  • Sites: 16 remembered entries (camp_xpos, civ_lot_y, spot_classes, ai_camp_site_weights), mostly city sites found by settlers. unit_cleanup() clears the targets at the end of each unit_phase() and re-adds every site of kind 0–2 as a target.

Sending units to targets

unit_phase() then looks at each idle unit. Units that are not busy, and not Nuclear, are considered.

First, a unit may be disbanded. Every sixteenth turn for a given unit ((unit + turn_tick) & 15), the AI disbands it when its type is obsolete for the civilization, or when it is Militia and the civilization is beyond Despotism or the continent’s plan is 5. A unit is kept if it is blocked by enemies, is alone in a city, is at sea, or is on a continent planned for defence.

Otherwise, the unit considers every target of its role on its own continent. Bombers consider attack and naval targets on any continent, but only odd-priority targets on alternating turns, with a cost that doubles when map_dist() exceeds the unit’s movement allowance. For other units the cost of a target is

cost = slots[target] * distance / (priority + 1)

where slots starts at max(3, troops / 8) for every target and grows by one each time a unit is sent. Units that are fortifying or fortified leave only for targets with a priority of at least two that are not already well served, and never leave a city. A target is skipped when four times its priority is less than its cost divided by the starting slot value. The unit takes the cheapest target as its destination (goto_x, goto_y) and is marked busy.