Order Language
Order Language
This document describes the grammar for v0 order parsing:
- the text format accepted by the v0 parser
- the
domain.Orderhierarchy the parser emits - the boundary between parse-time validation and execution-time validation
- the phase number assigned to each parsed order
The parser described here is intentionally smaller than the full 1978 rules language.
Scope
Included in v0 parsing:
- line-oriented parsing of submitted order text
- typed parsing for the MVP order set
- static validation that does not need live game state
- line diagnostics for malformed, invalid, unsupported, or not-yet-implemented input
Excluded from v0 parsing:
- turn execution
- game-state lookups
- inventory, ownership, and reachability checks
- support for every historical punctuation form used in the original manuals
Canonical Source Format
The v0 parser accepts a scrubbed, whitespace-oriented order language.
File-level rules
- Input must be UTF-8 text.
- Line endings are normalized to
\nbefore parsing. - Parsing is case-insensitive outside quoted strings.
- Runs of spaces and tabs outside quoted strings are treated as a single separator.
- Blank lines are ignored.
- Top-level parsing is one order per line, except for
setup, which is a multi-line block.
Comments
//starts a comment when it appears outside a quoted string.- A comment runs to end of line.
- Comment text is removed before tokenization.
//inside a quoted string is preserved as part of the quoted string.
Example:
move 77 orbit 6 // move the scout inward
name ship 39 "Slash // Burn"Quoted strings
- Double quotes delimit a single string field.
- Quoted strings preserve case and spaces.
- Quotes are required for all names. A name without surrounding quotes is a parse error.
- Surrounding quotes are stripped; the domain value contains only the characters between them.
- Escape sequences are not part of v0. A literal
"inside a name is not supported. - An unterminated quoted string is a parse diagnostic.
Numbers
- IDs, group numbers, and deposit numbers must be bare positive integers (no commas).
- Quantities accept embedded commas as thousands separators; commas are removed before parsing.
- Percentages are bare integers in the range
0–100. No%suffix. - Pay rates are non-negative integers in game currency units.
Examples (quantity field):
5000050,000
Examples (ID field):
91348
Deliberate v0 simplifications
- Canonical syntax is whitespace-separated. Commas and trailing periods from the historical manuals are not part of the v0 grammar.
- Canonical command words are lowercase in this document, but the parser matches them case-insensitively.
- The parser accepts a small alias set for historically common spellings, but new examples and tests should use the canonical forms below.
Primitive Tokens
IDs and group numbers
| Token | Meaning | Parse-time rule |
|---|---|---|
<id> |
ship or colony ID | integer > 0 |
<group-id> |
factory or mining group ID | integer > 0 |
<deposit-id> |
deposit ID | integer > 0 |
Parse-time does not verify that the referenced object actually exists.
Coordinates and locations
<system-coords> is a three-part coordinate token:
<x>-<y>-<z>Parse-time rules:
x,y, andzmust be integers in0..30
<orbit-ref> is either:
- an orbit number:
1through10 - a star-qualified orbit:
<star-seq>-<orbit>where<star-seq>is a single ASCII letter and<orbit>is1..10
Examples:
6c-4
Percentages and decimals
<percent>:
- bare integer, no
%suffix - parse-time range:
0..100
<rate>:
- non-negative integer in game currency units
- parse-time range:
>= 0
Examples:
5010005
Names
<name> is a quoted string: the remaining tokens on the line after the target ID, joined by spaces, which must begin and end with ".
Parse-time rules:
- quotes are required
- surrounding quotes are stripped before the name is stored in the domain value
- length 1..24 characters (measured after stripping quotes)
Units and Population Kinds
The parser maps textual unit tokens to domain.UnitKind. Tech-level suffixes are not part of the v0 token vocabulary; use the base name only.
Canonical population tokens
| Canonical token | Maps to |
|---|---|
unemployable |
domain.Unemployables |
unskilled-worker |
domain.UnskilledWorkers |
professional |
domain.Professionals |
soldier |
domain.Soldiers |
spy |
domain.Spies |
construction-worker |
domain.ConstructionWorkers |
rebel |
domain.Rebels |
Accepted aliases may include obvious plural forms and the short forms already used
in repository docs where they are unambiguous, for example professionals, soldiers,
spies, construction-workers, and pro.
Canonical unit tokens
The canonical unit vocabulary follows lower-case, domain-aligned slugs.
Population and commodity units use the base name only — no tech-level suffix:
foodconsumer-goodsstructurallight-structuralresearch-point
Equipment units require a -N tech-level suffix. The base name alone is rejected:
| Base name | Example with required suffix |
|---|---|
factory |
factory-6 |
mine |
mine-2 |
farm |
farm-1 |
hyper-engine |
hyper-engine-1 |
space-drive |
space-drive-1 |
life-support |
life-support-3 |
sensor |
sensor-2 |
automation |
automation-1 |
transport |
transport-1 |
energy-weapon |
energy-weapon-2 |
energy-shield |
energy-shield-1 |
missile-launcher |
missile-launcher-1 |
Accepted aliases for some base names: fact for factory, hype for hyper-engine, spac for space-drive, sen for sensor, cons for consumer-goods.
Manufacturing targets
<build-target> for the build change command accepts:
<unit-token>consumer-goods
The historical research and retool targets are recognized but treated as
not_implemented in v0.
Command Set
MVP commands accepted by the v0 parser
| Command | Canonical form | Notes |
|---|---|---|
| Set up | setup ... end |
only multi-line order; returns not_implemented in v0 |
| Build Change | build change <id> <group-id> <build-target> |
|
| Mining Change | mining change <id> <group-id> <deposit-id> |
|
| Transfer | transfer <source-id> <dest-id> <unit-token> <quantity> |
one item per order |
| Assemble (other) | assemble <id> <unit-token> <quantity> |
generic units |
| Assemble (factory) | assemble <id> factory <factory-unit> <quantity> <build-target> |
factories with build target |
| Assemble (mine) | assemble <id> mine <mine-unit> <quantity> <deposit-id> |
mines assigned to a deposit |
| Move (in-system) | move <id> orbit <orbit-ref> |
explicit destination kind |
| Move (system jump) | move <id> system <system-coords> |
explicit destination kind |
| Draft | draft <id> <population-kind> <quantity> |
v0 specialist drafting only |
| Pay | pay <id> <population-kind> <rate> |
integer rate |
| Ration | ration <id> <percent> |
bare integer, no % |
| Name (ship) | name ship <id> <name> |
|
| Name (colony) | name colony <id> <name> |
|
| Name (planet) | name planet <id> <name> |
numeric planet ID only |
Setup
Canonical form:
setup ship from <id>
transfer <quantity> <unit-token>
transfer <quantity> <unit-token>
endor
setup colony from <id>
transfer <quantity> <unit-token>
endRules:
setupis the only multi-line order.shipandcolonyare the only valid setup kinds.from <id>names the existing source ship or colony.- Each body line must begin with
transfer. endcloses the block.- The new ship or colony ID is not supplied by the player; it will come from sequence counters during execution.
Accepted alias:
set upmay be accepted as an alias forsetup
Example:
setup ship from 29
transfer 50000 structural
transfer 5 space-drive-1
transfer 5 life-support-1
transfer 5 food
transfer 5 professional
transfer 1 sensor-1
transfer 10000 fuel
transfer 61 hyper-engine-1
endBuild Change
Canonical form:
build change <id> <group-id> <build-target>Examples:
build change 16 8 hyper-engine-1
build change 16 9 consumer-goodsNotes:
researchandretoolare recognized but returnnot_implementedin v0.- Parse-time does not verify that the group exists or belongs to the source colony.
Mining Change
Canonical form:
mining change <id> <group-id> <deposit-id>Example:
mining change 348 18 92Transfer
Canonical form:
transfer <source-id> <dest-id> <unit-token> <quantity>Example:
transfer 22 29 spy 10Rules:
- v0 transfer is single-item only; multiple items require multiple orders.
- Parse-time does not verify location, capacity, or inventory.
Assemble
There are three assemble forms. The second token after the location ID determines which form is in use.
Other form — assembles generic units:
assemble <id> <unit-token> <quantity>Factory form — assembles factory units and configures their build target:
assemble <id> factory <factory-unit> <quantity> <build-target>Mine form — assembles mine units assigned to a specific deposit:
assemble <id> mine <mine-unit> <quantity> <deposit-id>Examples:
assemble 58 missile-launcher-1 6000
assemble 58 missile-launcher-1 6,000
assemble 91 factory factory-6 54000 hyper-engine-1
assemble 91 factory factory-6 54,000 hyper-engine-1
assemble 83 mine mine-2 25680 92
assemble 83 mine mine-2 25,680 92Notes:
factoryandmineon the second position are keyword discriminators, not unit tokens.- The factory form requires the factory unit to be a
factory-Ntoken; any other unit kind is rejected. - The mine form requires the mine unit to be a
mine-Ntoken; any other unit kind is rejected. - Quantities accept comma thousands-separators.
Move
In-system move:
move <id> orbit <orbit-ref>System jump:
move <id> system <system-coords>Examples:
move 77 orbit 6
move 88 orbit c-4
move 79 system 4-6-19Notes:
- v0 parsing uses explicit destination kinds (
orbit,system) to avoid ambiguity. - The parsed order preserves symbolic destination intent; it does not commit the execution model to a particular ship-location storage shape.
Draft
Canonical form:
draft <id> <population-kind> <quantity>Examples:
draft 13 soldier 3600
draft 16 professional 400
draft 16 construction-worker 250v0 draft coverage:
- accepted targets:
professional,soldier,spy,construction-worker - recognized but not implemented:
trainee
Pay
Canonical form:
pay <id> <population-kind> <rate>Examples:
pay 38 unskilled-worker 1
pay 38 professional 5
pay 38 soldier 4Parse-time rules:
- rate must be a non-negative integer (game currency units per turn)
- allowed population kinds:
unemployable,unskilled-worker,professional,soldier,spy,construction-worker
Ration
Canonical form:
ration <id> <percent>Example:
ration 16 50Name
Ship naming:
name ship <id> <name>Colony naming:
name colony <id> <name>Planet naming:
name planet <id> <name>Examples:
name ship 39 "Dragonfire"
name colony 7 "Outpost Beta"
name planet 5 "New Terra"Non-MVP Commands
The parser distinguishes between:
- known commands that are not implemented in v0
- completely unknown input
Known historical commands outside the v0 MVP set return the diagnostic code
not_implemented.
This list includes:
bombardinvaderaidsupportdisassemblebuysellsurveyprobecheck rebelsconvert rebelsincite rebelscheck for spiesattack spiesgather informationdisbandcontrolun-controlpermissionnews
Anything else that does not match either the MVP set or the known-not-implemented
set returns unknown_command.
Domain Order Hierarchy
The parser emits typed domain.Order values. Domain values do not carry parser
artifacts such as comments, raw text, or line numbers.
The domain model uses a small interface plus concrete order structs.
Order
├── SetUpOrder
├── BuildChangeOrder
├── MiningChangeOrder
├── TransferOrder
├── AssembleOrder (other form)
├── AssembleFactoryOrder (factory form)
├── AssembleMineOrder (mine form)
├── MoveOrder
├── DraftOrder
├── PayOrder
├── RationOrder
└── NameOrderOrder interface
type OrderKind int
type Order interface {
Kind() OrderKind
TurnPhase() Phase
Validate() error
}Support types
// MoveDestination holds the raw parsed destination for a move order.
// Full execution-time resolution requires the ship-location model from a later sprint.
type MoveDestination struct {
Raw string // e.g. "orbit 6" or "system 4-6-19"
}
// NameTargetKind identifies what kind of entity a NameOrder renames.
type NameTargetKind int
const (
NameTargetPlanet NameTargetKind = iota + 1
NameTargetShip
NameTargetColony
)Concrete order structs
// SetUpOrder — Phase 2
type SetUpOrder struct {
ColonyID ColonyID
NewName string
NewKind UnitKind
}
// BuildChangeOrder — Phase 4
type BuildChangeOrder struct {
ColonyID ColonyID
FactoryGroupID FactoryGroupID
NewUnitKind UnitKind
}
// MiningChangeOrder — Phase 5
type MiningChangeOrder struct {
ColonyID ColonyID
MiningGroupID MiningGroupID
DepositID DepositID
}
// TransferOrder — Phase 8
type TransferOrder struct {
SourceID ColonyID
DestID ColonyID
UnitKind UnitKind
Quantity int
}
// AssembleOrder — Phase 9 (other form)
type AssembleOrder struct {
ColonyID ColonyID
UnitKind UnitKind
Quantity int
}
// AssembleFactoryOrder — Phase 9 (factory form)
type AssembleFactoryOrder struct {
LocationID ColonyID
FactoryUnit UnitKind // must be Factory
FactoryQty int
BuildTarget UnitKind
}
// AssembleMineOrder — Phase 9 (mine form)
type AssembleMineOrder struct {
LocationID ColonyID
MineUnit UnitKind // must be Mine
MineQty int
DepositID DepositID
}
// MoveOrder — Phase 12
type MoveOrder struct {
ShipID ShipID
Destination MoveDestination
}
// DraftOrder — Phase 15
type DraftOrder struct {
ColonyID ColonyID
PopKind UnitKind
Quantity int
}
// PayOrder — Phase 16
type PayOrder struct {
ColonyID ColonyID
PopKind UnitKind
Wage int // non-negative integer; 0 means no pay
}
// RationOrder — Phase 17
type RationOrder struct {
ColonyID ColonyID
RationPercentage int // 0..100
}
// NameOrder — Phase 21
type NameOrder struct {
TargetKind NameTargetKind
TargetID int // PlanetID, ShipID, or ColonyID depending on TargetKind
NewName string
}Design notes:
MoveOrdercarries a raw destination string (orbit 6,system 4-6-19). Full parsing into orbit and system types is deferred until the ship-location model is defined in a later sprint.NameOrderuses an explicitTargetKindfield so ship, colony, and planet naming are unambiguous without splitting into separate types.- The domain model preserves execution-relevant data only. Line numbers belong in app-layer diagnostics.
Parse-Time Validation
Parse-time validation is owned jointly by infra and domain:
infra/ordertexttokenizes and maps text to candidate valuesdomainvalidates static invariants intrinsic to the order itself
Parse-time validation includes:
- command recognition
- correct number of fields for a recognized form
- correct block shape for
setup - valid integer, percentage, decimal, coordinate, and orbit syntax
- valid unit and population tokens
- name quoting and 24-character limit
- static numeric ranges such as orbit
1..10, percentage0..100, and coordinates0..30
Parse-time validation does not include:
- does the referenced ship, colony, group, or deposit exist?
- is the source at the same location as the target?
- does the source have enough units or population?
- does a move fit current hyper-engine limits?
- is a build-change target operationally legal for the current factory group?
- does a setup order include enough materials to produce a valid ship or colony?
Execution-Time Validation
Execution-time validation belongs in the turn engine, not in the parser.
Examples by order family:
| Order family | Parse-time | Execution-time |
|---|---|---|
| Build Change | command shape, group ID syntax, target token validity | group exists, group belongs to source colony, target change is legal now |
| Mining Change | command shape, deposit ID syntax | mining group exists, deposit exists, deposit is reachable/controlled |
| Transfer | source/target ID syntax, quantity syntax, unit token validity | source and target co-located, enough inventory, capacity/life-support checks |
| Assemble | variant selection, quantity syntax, unit token validity | enough disassembled items, enough construction workers, destination can host group |
| Set up | block syntax, transfer line syntax | required materials present, source location valid, new entity can be created |
| Move | destination token shape | ship exists, ship is at a valid origin, destination reachable with current engines/fuel |
| Draft | quantity syntax, allowed population token | enough source population, specialist conversion rules satisfied |
| Pay/Ration | decimal/percent parsing, population kind validity | colony economy effects, starvation, rebellion, carry-forward state |
| Name | target token shape, name length | target exists, player is allowed to rename it |
Diagnostics
The parse result exposes stable diagnostics with:
linecodemessage
Stable diagnostic codes:
| Code | Meaning |
|---|---|
unknown_command |
the line does not begin with a recognized command form |
not_implemented |
the line matches a known historical command that v0 does not support |
syntax |
the command is recognized, but the field count or clause layout is wrong |
invalid_value |
a field is present but fails static validation |
unterminated_quote |
a quoted field is not closed before end of line |
unexpected_end |
end appeared outside an open setup block |
Diagnostic rules:
- diagnostics are reported in input order
- parsing continues after a bad top-level line
- a bad line does not invalidate previously accepted orders
- a malformed
setupblock produces diagnostics and does not emit aSetupOrder accepted_countcounts accepted top-level orders, not subordinatetransferlines insidesetup
Phase Mapping
The parser assigns a phase number to each accepted order so later turn processing can group by phase without reparsing.
| Order | Phase |
|---|---|
| Set up | 2 |
| Build Change | 4 |
| Mining Change | 5 |
| Transfer | 8 |
| Assemble | 9 |
| Move | 12 |
| Draft | 15 |
| Pay | 16 |
| Ration | 17 |
| Name | 21 |
Parse-time does not require players to submit orders in phase order. Input order is preserved, but phase assignment is explicit on the typed order values.
Compatibility Notes
- v0 canonical syntax follows the scrubbed, whitespace-based style already described in
apps/site/content/docs/developers/reference/agent-reference.md. - Historical comma-separated examples remain useful as source material, but they are not the grammar target for v0.
- The parser may accept a narrow alias set for convenience, but every new example added to the codebase should use the canonical forms in this document.