Stage 4 · Structural grammar

The Grammar Doesn’t Know What a Sword Is

Stage 4 builds the physical object. It works bottom-up from geometric primitives, and it is forbidden, as a matter of design law, from ever branching by intent. The spec marks exactly one principle non-negotiable, and it's this one.

Why bottom-up

The obvious way to generate game items is top-down: pick "sword" from a table, then fill in sword-shaped details. The whole game premise dies if you do that. If the generator knows it made a sword, the classification is trivially recoverable and interpretation has nothing to do; the player's job of working out what a thing was only exists if the engine itself never decided. So the grammar produces structures (an elongated form with an edge, socketed to a cylindrical form) and classification happens downstream, by reading the geometry back like an archaeologist would. The ambiguity is native: a long edged thing genuinely might be a weapon, a tool or a ritual object.

The vocabulary

Eight geometric primitives, each with its own parameter set: elongated, cylindrical, flat-broad, hollow-enclosed, ring-form, disc-form, bar-form, sheet-form. Two examples of the parameter space:

Components join through nine attachment types (inline, perpendicular, socketed, riveted, wrapped, lashed, hinged, threaded, friction-fit), and the attachments matter later: a hinged join is evidence of fine metalwork, and classification will read brooch anatomy off a pin bearing on a hoop.

<object>            ::= <component-group>+
<component-group>   ::= <primary-component> [<attachment> <component-group>]*
<primary-component> ::= <elongated> | <cylindrical> | <flat-broad>
                      | <hollow-enclosed> | <ring-form> | <disc-form>
                      | <bar-form> | <sheet-form>

The BNF's repetition operators are engine-owned: the data module supplies alternatives and weights only, and expandGrammar decides how many groups and how long the attachment chains run, bounded by the complexity budget below.

Grammar rules live in a data file as plain objects; the engine looks them up by symbol. Four production rules cover the MVP.

elongatedlong · double edge · sharp point cylindricalshort · narrow · thick wall disc-form × 2arrangement: symmetric socketed riveted Three component groups, two attachments, one arrangement group. Nothing here says "sword"; stage 8 will read the edge, the grip-shaped cylinder and the symmetric pair as evidence.
One expansion, flattened left to right along the primary axis.

Culture is a weight modifier

Cultures don't have allowed artefact types. They have tendencies, expressed as weight modifiers over grammar options. Selection works like this: start from the option's authored base weight, add each cultural modifier scaled by the culture's affinity for that material tag, multiply by phase influence, floor at 0.01.

weight = max(0.01,
  (baseWeight + Σ affinity(tag) × modifier)
  × Π (1 + (multiplier − 1) × phaseAttribute))

The 0.01 floor means nothing is ever impossible: a deeply pacifist culture occasionally produces a blade. The source comment reads, in full, "Because archaeology." Phase influence is calibrated so a high attribute amplifies while a low one merely fails to amplify; "low metallurgy" never drags an option below its base weight.

A phase is a multi-attribute profile, one per culture per period: eight technology axes (metallurgy, ceramics, textiles, leather-working, stone, glass, wood, bone), plus economy, society and aesthetics blocks. A culture can be exceptional metalworkers with crude ceramics, and its artefacts will say so.

Complexity budgets and arrangement patterns

Uncontrolled repetition produces nonsense, so repetition is patterned, and the constraint system encodes two claims about the world rather than two knobs on a generator.

The budget is a claim about people

Object complexity tracks who a culture can afford to specialise: a part-time potter makes simple forms, a workshop tradition supports composite objects with several distinct assemblies. So the budget hangs off society.craftSpecialisation, one number per culture-phase, resolved into a tier that every object draws against:

Complexity is a property of the society before it's a property of any object, which is why the budget binds at the culture level and not per artefact.

The patterns are a claim about objects

Real artefacts repeat components deliberately: rivets march in rows, beads circle a rim, sockets stack. Six arrangement patterns cover the MVP (symmetric pairs, radial spreads of 3–12, linear arrays, stacks, nests, branches), and a repeated component must belong to one of them; the accumulation checker rejects an expansion whose repetition fits no pattern, because unpatterned repetition reads as noise where patterned repetition reads as intent. The arrangement group survives normalisation, so downstream feature extraction can tell a radial spread of studs from one stud, and the player can too.

Portability, derived

Generated objects carry a portability band, from pocketable through team-lift to major-effort, and an inspection depth derived from physical dimensions: anything up to 30cm can be held and fully examined, up to 150cm examined in detail, beyond that observed in situ. These are mechanical facts about player handling, and classification rules are banned from reading them; the classification page has the boundary and the test that guards it.

Where it lives Engine: src/lib/engine/generation/grammar.ts · Rules: src/lib/data/grammars/core.ts · Primitives: src/lib/data/grammars/primitives.ts · Spec: doc 05 §5