Skip to content
Project Nova
Project Nova

Rulebook

Project: Nova is a programming strategy game about preparing a hostile planet for human colonization. Players do not directly command units during play. Instead, each player writes android programs, uploads them as scripts, launches androids, and studies the resulting simulation to improve later script versions.

This rulebook is written for players and coding agents that want to play the current game.

Every number in this document is a default. They are all rules — data, not code — and a host can retune any of them for a given game. Each one below is named with the rule it comes from, such as rules.android.cargoCapacity, and an Android reads the resolved values at run time from the rules global. See Rules for the whole table and how to change it. Where this rulebook states a number, read it as “the shipped default”; where a script needs the truth, read rules.

1. Objective

Only one player can become the planet’s founding colony authority.

Colony readiness is scored continuously from the current world state. Players prepare a viable colony through completed infrastructure, secured material, environmental preparation, and reliable resource supply. Discovery and other information-gathering are useful for play, but do not themselves earn readiness points.

A good android strategy should focus on:

  • finding scattered earth-launched material
  • building chargers to increase android capacity
  • building storage and resource infrastructure
  • avoiding or cleaning hazards
  • keeping androids charged and alive
  • leaving useful broadcasts and recordings for future script improvements

1.1 When the Humans Arrive

A game may have an arrival date: the round the colonists are expected to land on, set by rules.match.finalRound and read by a script as the finalTurn global.

It is a deadline, not a mechanic. Nothing happens to the world on that round — the engine does not stop the game or score it differently, because it is the host that decides how many rounds to run. What it changes is how an Android should play: a colony that will be inspected on round 20 is worth building differently from one with no arrival date, and readiness banked after the humans land is readiness that arrived too late to count.

Two ways to play set it for you, because in both the round count is a real deadline rather than a suggestion — neither can be continued once it has run:

  • A peer match, nova host --rounds 20 or Match › Host in the browser lab. The arrival is the round count the host offered.
  • A run in the browser lab, where the arrival is the round count in the Run panel.

A game created with nova create-game has no arrival unless its rules file gives it one, because nova run can always be asked for more rounds.

2. Core Loop

Players repeat this loop:

  1. Write or update android code.
  2. Upload the code as an android script version.
  3. Launch androids using available charger capacity.
  4. Run the simulation.
  5. Review the recording, messages, and world state.
  6. Improve future android behavior.

Androids are autonomous once launched. A script is a module whose default export is its turn function, called once per round and returning that round’s action:

const turn: AndroidTurn = () => ({ type: 'android.move', direction: 'east' });
export default turn;

The turn function runs with these globals:

  • androidId — the id of the android currently taking its turn
  • world — the fogged world snapshot visible to the script runner
  • rules — every number this game is played with (see Rules)
  • turn — the round now being played, counting from 1
  • finalTurn — the round the humans are expected to land on, or undefined when no arrival is scheduled. Nothing happens on that round mechanically; it is the deadline the colony is being built to, and a script that reads it can pace itself against the time it has left

The scripts stored in a game are the compiled result. A factory Android is written in TypeScript, across as many files as it needs, and the CLI compiles and bundles it into one script on upload. The action shapes below are what the turn function returns.

3. World

The planet is a rectangular grid of tiles.

Each tile has:

  • position: { x, y }
  • composition: materials and hazards in the ground
  • scattered: loose earth-launched material on the surface
  • optional revealedBy: player ids that can currently see the tile

The board’s size is rules.world.width x rules.world.height — 12 x 12 by default — and a script can read it even though the tiles themselves are fogged.

Tile composition is generated randomly when a new map is created, from rules.world.generation. Composition is not the same as loose material. Composition represents things in the ground or environmental conditions.

Current composition fields:

  • ore — natural material that extractors can harvest
  • water — natural material that extractors can harvest
  • acid — environmental hazard and extractable/processable material
  • radiation — environmental hazard

Current scattered material fields:

  • metal
  • electronics
  • polymer
  • ore
  • water
  • acidCanister

What a fresh map actually scatters is narrower than that, and deliberately: metal and electronics, and nothing else. Polymer is never scattered — Earth sent none, and the only polymer on the planet is polymer a colony made, out of an acid processing plant. The rest of the fields exist because material can reach the ground other ways: salvage returns it, and a hand-authored scenario can put anything anywhere.

Scattered material can be collected directly by androids. Composition cannot be collected directly; it requires buildings or special mechanics.

A tile can contain at most one building or construction site.

4. Players

Players are competitors. There are no formal alliances, shared ownership rules, shared victory, or safe teammate permissions.

A player is created when they first upload a script or launch an android. Each player is guaranteed an initial charger. Initial chargers are completed, owned by that player, and cannot be salvaged.

5. Androids

Androids are programmable autonomous units.

Current android fields include:

  • id
  • ownerId
  • scriptId
  • position
  • battery
  • health
  • active
  • optional cargo
  • memory — private persistent working state, limited to rules.android.memoryLimit characters (4,096)
  • recording — player-facing persistent log, limited to rules.android.recordingLimit characters (16,384)

Androids can currently:

  • move one tile orthogonally
  • collect scattered material from their current tile
  • carry up to rules.android.cargoCapacity total material units (12)
  • start and continue construction
  • charge on owned chargers
  • deposit to and withdraw from storage-capable owned buildings
  • salvage buildings
  • repair an owned damaged building
  • broadcast public messages
  • clean acid from adjacent tiles if their owner has an acid processing plant
  • launch another android of the same owner while standing on an owned completed charger
  • dismantle themselves, or another android of the same owner while standing on an owned completed charger

Androids have health. They lose a small amount of health over time. Environmental hazards can damage them faster.

At round end:

  • every android loses rules.android.decayPerRound health from ordinary decay (0.15)
  • radiation damages androids by radiation * rules.android.radiationDamagePerPoint (0.75)
  • acid damages androids by acid * rules.android.acidDamagePerPoint (1.5)

Hazards are not scenery. Four points of acid is six health a round, so a route across the flats is a decision with a cost, and a tile nobody can cross is a wall an Android can use.

A failed turn — a refused action, a script error, an exhausted turn budget — costs the android rules.android.failedTurnHealthPenalty health (10) on top of the round it lost. One bad edge case is survivable; a script that keeps making the same mistake wears its android out.

Androids with health at or below 0, or battery at or below 0, are destroyed at round end. A destroyed android is deactivated rather than removed: it takes no further turns and no longer holds charger capacity, but it stays in the world as a wreck, so its owner can still read the memory and recording it left behind. Deactivated androids take no further decay or hazard damage, and are not drawn on the board.

6. Chargers and Android Capacity

Chargers determine android deployment capacity.

A player’s active android cap is the sum of rules.buildings[type].androidCapacity over their completed buildings. Only chargers carry capacity by default, and each carries 2.

Examples:

  • 1 charger allows 2 active androids
  • 3 chargers allow 6 active androids

The starting charger therefore allows a second android from the first turn, and an android standing on it can launch that sibling itself. Doubling the hands before doing anything else is a real opening — and so is deciding not to.

A player always has at least their initial charger. Initial chargers are placed on non-overlapping tiles when the world is set up.

If a non-initial charger is destroyed or salvaged, the player’s deployment cap immediately decreases. Existing androids are not automatically destroyed, but the player cannot launch more androids until their active android count is below the current cap.

Launching is currently immediate if capacity is available. Launch delay is planned but not implemented yet.

A player is not the only one who can spend that capacity. An android standing on one of its owner’s completed chargers can launch a sibling itself with android.launch, and it is held to the same cap: the launch is refused unless the owner’s active android count is below their completed charger count. Because the launching android is itself active, a player at their cap cannot launch another until something frees capacity — a dismantle, a wreck, or another charger.

7. Scripts, Launching, and Dismantling

Players may upload improved script versions during the game. Existing androids do not automatically update to a new script.

To use a new script version, a player must launch a new android with that script id, or dismantle/free capacity and launch a replacement.

Dismantling is voluntary. It deactivates the android and frees charger capacity. It currently returns no material. As with any destroyed android, the dismantled one stays in the world as a wreck.

An android on one of its owner’s completed chargers can also dismantle another android of the same owner, so a fleet can retire and replace its own members without the player intervening. An android can never dismantle another player’s android, and can never name itself as the target — self-destruction is the untargeted form of the same action.

7.1 What a Turn May Spend

A turn runs under three budgets, and they are rules like any other, so a script can read its own allowance from rules.script:

RuleDefaultWhat exhausts it
rules.script.fuel10,000Infinite loops, and searches that never terminate
rules.script.timeoutMs1,000Work that is slow without being long, such as allocating relentlessly
rules.script.memoryBytes16 MBBuilding enormous arrays or strings

Exceeding any of them is a failed turn: it costs the round and rules.android.failedTurnHealthPenalty health, and leaves the next turn unaffected. fuel is counted in interpreter ticks rather than milliseconds, so a script is cut off at the same point on every machine — which is what lets two peers replay the same match and agree on the outcome. A normal bot is nowhere near any of these; the shipped starter builder finishes a turn without spending a single tick.

The call stack is bounded too, at 128 KB, but it is not a rule: its ceiling is a property of the sandbox build rather than a design choice.

8. Android Action API

An android script’s turn function must return one android event. The engine adds androidId automatically. Every action may also include optional memory and recording string fields. They replace the Android’s previous values as part of the same turn; they are not separate actions. Scripts can read both values from their Android in world.androids. memory is for operational state across turns. recording is the log available to the player after the Android is deactivated.

Because both fields are written as part of the action, a rejected action takes them with it: if the action is refused — moving outside the map, building without the material, writing past the memory limit — the turn becomes a failed turn, the Android loses rules.android.failedTurnHealthPenalty health, and neither memory nor recording is updated for that round. An Android that needs a reliable log should prefer an action it knows will be accepted over an ambitious one that may be refused.

In a peer match played with --disclosure recording (see the CLI guide), recording is the only account of the match the player receives, alongside the final scores. Under that mode what an Android writes down is part of its design, not a debugging aid.

const action: Action = {
  type: 'android.wait',
  memory: 'return-to-depot',
  recording: 'No safe route found this round.',
};

Wait

Do nothing.

const action: Action = { type: 'android.wait' };

Move

Move one tile north, south, east, or west, for rules.android.moveBatteryCost battery (1). Moving outside the map fails the turn, which costs the android its round and rules.android.failedTurnHealthPenalty health. The map’s bounds are in rules.world, so an Android never has to guess where the edge is.

const action: Action = { type: 'android.move', direction: 'east' };

Directions: north, south, east, west.

Charge

Recharge on an owned completed building on the android’s current tile that can charge — any type whose rules.buildings[type].charge is above zero, which is the charger by default. Adds that much battery (25), capped at rules.android.batteryCapacity (100).

const action: Action = { type: 'android.charge' };

Collect

Collect scattered material from the current tile into cargo. If resources is omitted, the android collects what it can up to cargo capacity.

const action: Action = { type: 'android.collect' };
const withResources: Action = { type: 'android.collect', resources: { metal: 3 } };

Deposit

Deposit cargo into an owned, completed, storage-capable building on the current tile. If resources is omitted, deposits all cargo.

A building accepts deposits when rules.buildings[type].storage.deposit is true: depots, processors, and acid processing plants by default. A construction site is not storage: nothing a building does — charging, storing, extracting, converting, seeing, carrying android capacity, or scoring — happens until it is finished.

const action: Action = { type: 'android.deposit' };
const withResources: Action = { type: 'android.deposit', resources: { ore: 2 } };

Withdraw

Withdraw material from an owned, completed, storage-capable building on the current tile.

const action: Action = { type: 'android.withdraw', resources: { metal: 4 } };

A building allows withdrawals when rules.buildings[type].storage.withdraw is true: depots, extractors, processors, and acid processing plants by default.

Start Construction

Start a building on the current tile. The tile must not already contain a building. Supplied resources can come from android cargo. Current compatibility also allows using loose/current-tile material in some cases, but scripts should prefer collecting material into cargo first.

Only material the building’s cost actually names is accepted, and never more of it than the cost asks for — a depot takes six metal, not eight, and not ten ore. Supplying nothing is allowed: the site is placed unpaid and the material can be delivered later with android.continue-construction.

const action: Action = { type: 'android.start-construction', buildingType: 'charger', resources: { metal: 10 } };

Continue Construction

Continue construction on an owned construction site on the current tile. If material requirements have been met, each continue action reduces remaining construction time by 1 tick.

const action: Action = { type: 'android.continue-construction' };
const withResources: Action = { type: 'android.continue-construction', resources: { electronics: 1 } };

Salvage

Damage/salvage the building on the current tile. Initial chargers cannot be salvaged. Self-salvage is faster than hostile salvage. When a building is fully salvaged, part of its build cost becomes scattered material on the tile.

const action: Action = { type: 'android.salvage' };

Broadcast

Broadcast a public message of up to rules.android.broadcastLimit characters (256). Messages are stored in world messages.

const action: Action = { type: 'android.broadcast', content: 'metal found at 3,4' };

Clean Acid

Clean rules.android.cleanAcidAmount acid (1) from an adjacent tile. The android’s owner must have a completed building that cleans acid anywhere in the world — rules.buildings[type].cleansAcid, the acid processing plant by default. The cleaned acid is stored as acidCanister in that building. Cleaning costs rules.android.cleanAcidBatteryCost battery (1).

const action: Action = { type: 'android.clean-acid', direction: 'north' };

Launch

Launch another android of the same owner on the current tile, which must hold one of the owner’s completed chargers. scriptId must be one of the owner’s scripts, as listed in world.scripts. The launch is refused unless the owner has spare charger capacity, exactly as a player launch is (see Chargers and Android Capacity). The new android starts at full battery and health and takes its first turn in the following round.

const action: Action = { type: 'android.launch', scriptId: 'script-2' };

Dismantle

Destroy this android voluntarily.

const action: Action = { type: 'android.dismantle' };

With targetAndroidId, destroy another android of the same owner instead. The acting android must be on one of its owner’s completed chargers, the target must be one of the owner’s active androids, and an android cannot name itself as the target.

const action: Action = { type: 'android.dismantle', targetAndroidId: 'android-3' };

9. Resources and Progression

There are two resource sources.

9.1 Scattered Earth-Launched Material

Humanity launched material pods before the androids arrived. These materials are scattered across the map and can be directly collected by androids.

This is the early-game resource source. It enables first depots, chargers, and other basic infrastructure.

9.2 Natural Planetary Composition

Tiles may contain natural composition such as ore, water, acid, and radiation.

Composition usually cannot be picked up directly. It requires buildings:

  • extractors harvest natural tile composition into building storage
  • processors refine ore into metal, and metal and water into electronics
  • acid processing plants enable cleanup of hazardous acid, and refine the canisters into polymer

Earth scattered a fixed amount of metal and electronics and it does not grow back. Polymer it sent none of at all. Everything past what is lying on the ground comes out of the ground and through a refinery, which is what makes the middle of the game a supply chain rather than a scavenger hunt.

That is also what stands between a player and a colony module. Its 20 electronics are more than a whole board scatters, and its 20 polymer cannot be found at any price — so the module is only ever built by a colony that runs a processor and has cleaned up enough of the planet to have canisters to refine. Neither refinery costs polymer, for the obvious reason: the first one could never be built.

10. Buildings

Current building types. Costs and construction times are rules.buildings[type].cost and .ticks:

BuildingCostConstruction ticksFunction
charger8 metal2Increases android capacity by 2; charges androids
depot6 metal2Stores material
extractor10 metal, 2 electronics3Harvests tile composition into storage
processor15 metal, 3 electronics4Refines ore into metal, and metal into electronics
acid-processing-plant12 metal, 2 electronics4Enables acid cleaning; refines canisters into polymer
relay-tower8 metal, 4 electronics3Planned communication infrastructure
scanner8 metal, 6 electronics4Reveals nearby tiles at longer range
radar14 metal, 10 electronics, 2 polymer7Reveals a radius-5 disc of tiles around itself
colony-module50 metal, 20 electronics, 20 polymer12Scores 1,000 readiness; victory condition planned

A building under construction occupies its tile immediately. Buildings have rules.buildings[type].health health (100).

A completed building harvests rules.buildings[type].extraction from the composition of its own tile at round end, capped by what the ground holds. Only the extractor does by default:

  • up to 3 ore from tile composition
  • up to 2 water from tile composition
  • up to 1 acidCanister from tile acid

Harvesting does not consume composition: the ground keeps yielding.

A completed building runs rules.buildings[type].conversion on its own storage once per round end. A building may hold several recipes, and they run in the order listed, each once, against the storage as the previous one left it — so one building can feed itself. By default:

  • the processor: 2 ore -> 1 metal, then 1 metal + 1 water -> 1 electronics
  • the acid processing plant: 1 acidCanister + 1 water -> 1 polymer

This is the only source of electronics and polymer beyond what Earth scattered, and both cost more than they yield in raw material. A colony that wants the parts a colony module is made of has to build the industry that makes them, and keep it fed.

11. Acid and Environmental Preparation

Acid is both a hazard and a strategic resource.

  • Androids standing on acid take damage at round end.
  • Acid in tile composition is not loose material.
  • Androids can clean acid from adjacent tiles only if their owner has a completed acid processing plant.
  • Cleaning acid reduces the target tile’s acid by 1 and stores 1 acidCanister in the plant.

This makes acid cleanup a mid-game environmental preparation goal rather than a free starting ability.

12. Salvage and Sabotage

Androids can salvage buildings on their current tile.

Rules:

  • initial chargers cannot be salvaged
  • a building whose rules.buildings[type].salvageableByOthers is false cannot be salvaged by another player at all — the depot, by default
  • self-salvage deals rules.salvage.ownDamage building damage per action (25)
  • hostile salvage deals rules.salvage.hostileDamage building damage per action (15)
  • buildings have rules.buildings[type].health health (100)
  • when destroyed by salvage, returned material becomes scattered on the tile
  • self-salvage returns rules.salvage.ownReturnRate (60%) of the material actually invested in the building
  • hostile salvage returns rules.salvage.hostileReturnRate (35%) of the same

The return is a share of what went into the building, not of what its type costs, so an unpaid construction site is worth nothing to take apart. Anything stored inside a building is different: it spills onto the tile in full, for whoever is standing there. Bringing down a full depot is a robbery, not an erasure — and a stockpile left in a building nobody is guarding is a stockpile within reach.

Salvage is intentionally slower than ordinary collection and requires the android to remain at the target.

Depots are the exception, and the reason is the whole point of hauling: material that reached a depot is material that is safe. A rival can contest the ground, the chargers, the extractors and the refineries, but not the stockpile already banked. A player can still salvage their own depot — the stored material spills onto the tile, and moving it is then their problem.

12.1 Repair

An android standing on one of its owner’s completed buildings can repair it, restoring rules.salvage.repairAmount health (10) for rules.salvage.repairCost (1 metal) out of its cargo. Health never exceeds the building’s own maximum, and a building that is not damaged cannot be repaired.

Repair is what makes a raid a contest. Hostile salvage takes rules.salvage.hostileDamage (15) off a building per action against a repair’s 10, so one defender roughly cancels one attacker: holding ground is possible, never free, and two raiders beat one defender. Buildings under construction are continued, not repaired.

13. Communication

Androids can broadcast public messages.

Messages can be used for:

  • local coordination
  • map notes
  • warnings
  • claims
  • deception
  • negotiation
  • debugging script behavior

There is no private radio by default. Relay towers are planned to expand communication behavior, but only basic message storage is currently implemented.

14. Visibility and Information

Visibility is recomputed from scratch at the start and the end of every round: a tile is visible only while something of yours is in range of it, and goes dark again once nothing is. Recomputing at the start is what gives a newly launched Android a lit first turn instead of a view of the single tile it is standing on. Sight comes from rules.android.sight and rules.buildings[type].sight; by default:

  • active androids reveal tiles within range 3
  • completed scanners reveal tiles within range 4
  • completed radars reveal tiles within radius 5

A building type whose sight rule is null reveals nothing.

Sight range is measured two different ways, and the difference is deliberate:

  • Androids and scanners count orthogonal steps (|dx| + |dy|), so their footprint is a diamond. It is the range the piece could actually walk, which makes short-range sight read as the piece looking around itself.
  • Radars use true distance (dx² + dy² <= radius²), so their footprint is a disc. A radar sweeps rather than walks, and at radius 5 a diamond would read as an obvious lozenge on the board instead of a sweep.

A radar therefore sees roughly twice the ground a scanner does — about 81 tiles against 41 — for roughly twice the cost and almost twice the construction time. It is the mid-game answer to mapping a region, where the scanner is the early-game answer to seeing past your own Androids.

Each script receives a fogged world projection for its Android’s owner. It includes the tiles that owner can currently see, as well as the acting Android’s current tile so it can always inspect itself. Androids, buildings, and broadcasts are included only when their position is on an included tile. Other players’ scripts and player records are omitted. The acting Android retains its memory and recording; those fields are [Redacted] on every other Android.

Deactivated Androids stay in world.androids as wrecks on the tile where they fell, so a script counting company should filter on active.

Sight is not memory: once your Androids move out of range, or a scanner or radar is salvaged, a tile drops out of the projection again. Scripts that need to remember the map must persist it themselves in Android memory or share it through broadcasts.

A player-facing replay preserves the complete world for rendering, but replaces another player’s script source and Android memory and recording with [Redacted]. The same redaction applies to the corresponding event payloads.

15. Conflict

Project: Nova is primarily a construction, logistics, and programming strategy game. Conflict exists, but should not reduce the game to simple combat.

Current conflict tools are indirect:

  • racing to scattered material
  • occupying valuable building sites
  • salvaging hostile infrastructure
  • broadcasting misleading or persuasive information
  • denying or cleaning hazardous terrain

Direct android combat is not implemented.

16. Colony Readiness Score

The readiness score answers: which player has prepared the most viable colony right now? It is calculated from the current World and reported per player with a contributor breakdown in nova status and the replay UI.

Only completed, functioning colony assets and materials secured in completed buildings count. Construction sites, loose material, Android cargo, scripts, messages, Android count, map discovery, scanners, radars, and relay towers earn no points. Sight infrastructure and exploration remain strategically useful because they help Androids locate viable resources and building sites.

Points come from rules.scoring.buildings and rules.scoring.materials; anything worth 0 points earns no line in the breakdown at all. The defaults:

ContributorPoints
Colony module1,000 each
Acid-processing plant120 each
Processor100 each
Extractor80 each
Depot40 each
Charger25, then 80% of the one before
Stored metal2 per unit
Stored electronics or polymer3 per unit
Stored ore1 per unit
Stored water or acid canister2 per unit

Stored materials count only when they are in a completed building owned by that player. The algorithm deliberately measures present readiness, not historical achievement: an asset that is salvaged or a stockpile that is spent stops contributing.

17. Design Goals

The rules should create these strategic questions:

  • Where should I build my first real base?
  • Should I expand chargers or secure resources first?
  • Is it worth replacing an android with better code now?
  • Should I broadcast openly, encode meaning, or stay silent?
  • Is hostile salvage worth the time and risk?
  • Should I clean acid, avoid it, or exploit it as a barrier?
  • How much should I invest in exploration versus infrastructure?
  • Can my androids recover from unexpected situations?

18. Rules

Everything numeric in this rulebook is a rule, and the rules are data. One object holds all of them, every field has a default, and any subset can be overridden — {} is the game as it ships.

// rules.json — a complete rules file
{
  "world": { "width": 24, "height": 24 },
  "android": { "cargoCapacity": 6 },
  "buildings": { "depot": { "cost": { "metal": 4 }, "ticks": 1 } },
}
npx nova create-game --file game.json --rules rules.json

The groups:

GroupCovers
worldBoard size, and how a fresh map’s composition and scattered material are generated
androidCargo capacity, battery capacity and costs, decay, hazard damage, the failed-turn penalty, sight, and limits
buildingsPer type: cost, ticks, health, charging, android capacity, sight, storage, extraction, conversions, acid cleaning, hostile-salvage immunity
salvageSalvage damage, the share of invested material returned, and what a repair restores and costs
scoringWhat each building and stored material is worth, and how fast a type’s value diminishes
scriptWhat one Android turn may spend: CPU ticks, wall clock, and heap
matchThe round the humans are expected to arrive on, and whether lost androids are replaced

Two conventions are worth knowing when writing a rules file:

  • Every single value has a default, so a group can be given partially: { "android": { "cargoCapacity": 6 } } leaves every other android rule alone.
  • A leaf value object — a sight, a generation roll, a conversion — is supplied whole or not at all. { "sight": { "range": 6 } } is rejected rather than silently inheriting a shape from somewhere else.

Mechanics read behaviour off the rules rather than off building names, so some retunings that look like code changes are not: a depot with androidCapacity: 1 raises the android cap, and a building with a conversion refines material.

Rules and Androids

The resolved rules are handed to every script as the rules global. An Android should read them rather than repeat them:

const capacity = rules.android.cargoCapacity;
const depotCost = rules.buildings.depot.cost;
const onMap = (p: Position): boolean => p.x >= 0 && p.y >= 0 && p.x < rules.world.width && p.y < rules.world.height;

An Android that copies a number out of this rulebook is an Android that breaks when the game is tuned — and the point of the rules global is that adapting is cheaper than guessing.

Rules and Recordings

A game file stores the rules it was created with, alongside the initial world and the event log. Every later command — run, status, play — replays it under those rules, so a retuned game continues and scores as itself. A recording made before rules were data opens under the shipped defaults, which is what it was played with.

A peer match is played under the host’s rules, and the recording each player keeps carries them. The board size is currently the only rule the match offer negotiates; the host’s round count is not negotiated but is written into the rules as match.finalRound, so both Androids know when the humans land.

19. Not Yet Implemented / Expected Future Work

These concepts are intended but not fully implemented:

  • launch delay
  • fleet-arrival endgame
  • relay tower broadcast extension
  • direct defensive structures
  • richer natural resource chains
  • player-facing reports beyond recordings and world state

Found something wrong? These documents live in the repository.