Target catalog
Last edited by · ·
Sugarscape v3 target catalog
Targets describe outcomes that a player's SugarLang ruleset should grow. Most
are normalized distributions; scalar objective kinds support leagues such as
Commonwealth. The shipped catalog lives in targets/, one JSON object per file.
Four distribution targets are engine-generated (2026-08-11): no tabulated data for
the classic Sugarscape results was ever published, so the honest primary source
is the DTL engine submodule itself — tools/generate_targets.py runs the
GAS-referenced example configs under DTL's internal defaults (greedy "none"
decision model) for 30 seeds, pools the variable over the final 100 ticks, and
bins canonically. Those files carry "provisional": false and full engine-run
provenance in generation. Other distribution targets record their own
provisional or generated provenance.
Schema
A target always contains id, kind, and variable. Omitted kind means
distribution for compatibility with older catalog and inline targets.
A distribution target additionally contains:
id: stable catalog identifier.variable: measured variable name.scope:globalorseat. Omitted scope defaults toglobal; all shipped targets use global scope.support: inclusive lower and upper measurement bounds.bins: strictly increasing edges beginning and ending at the support bounds.probs: one normalized probability per bin interval.window: nominal number of final ticks to pool. The episode's effectivemeasurement_windowreplaces this value when targets are assigned.source: honest provenance, including whether source data was digitized.provisional: whether the probabilities are placeholders.generation: parametric family, parameters, and a plain-language generation description.
All targets for one variable must use identical support and bin edges. Catalog loading rejects inconsistencies. Samples outside the support clamp into the first or last bin.
A maximize or minimize target instead contains exactly id, kind,
variable, and description. Objective targets do not carry a scope: they are
implicitly measured over the submitting seat's agents. Episodes may not mix
target kinds. minimize is schema-reserved but has no score method yet, so an
episode using it is rejected explicitly rather than assigned an invented
orientation or ceiling.
Measured variables
| Variable | Recipe |
|---|---|
wealth | Pool each living agent's sugar plus spice over the final window. |
age | Pool each living agent's age over the final window, using the same canonical support and bins as age_at_death. This measurement-only variable has no shipped target yet. |
population | One living-agent count per tick. |
age_at_death | Pool death-event ages captured before DTL clears its death list. |
majority_tribe_share | One largest-tribe population share per tick. |
sick_fraction | One fraction of living agents with any disease per tick. |
mean_trade_price | One mean active-trader price per tick; zero when agents live but no trade occurs. |
wellness | For each observed agent identity, mean normalized DTL composite happiness (h + 5) / 10 over the ticks it was present in the final window. Values clamp to [0, 1] and use canonical decile bins. |
Every variable is measured globally and independently for each seat, regardless of which variables the episode targets. Per-agent and death-event measurements with no samples are marked empty. Population zero remains a real scalar sample. The wellness diagnostic histogram includes one window mean for every agent observed in the retained window, including agents that later died. Commonwealth scoring and component diagnostics separately filter those records to agents alive at the final tick; running replay summaries filter to agents alive at the sampled tick.
Scoring
The ranked method is w1-hyperbolic/1. First compute raw Wasserstein-1 distance
D from the two cumulative histograms, weighted by bin width. Its units are the
measured variable's units; it is not divided by the declared support.
The target supplies its own characteristic scale. Choose the leftmost median
bin i* (equivalently, the smallest-index point mass minimizing W1 to the
target), then compute:
S = max(W1(point_mass_i*, target), width(i*))
score = S / (S + D)
The first term is the target's binned mean absolute deviation from its median. The bin-width floor defines a positive resolution for a one-bin target. Thus an exact match scores 1, one characteristic scale of transport scores 0.5, and larger errors approach zero without losing their ordering to a clipped plateau. With unequal bin widths, leftmost median tie-breaking is part of the scoring contract because the chosen bin determines the floor.
Results report raw_w1, w1_scale, and base-2 Jensen-Shannon divergence as
diagnostics. Jensen-Shannon is not part of the ranked score because it does not
encode distance between bins. An empty target measurement scores zero, reports
raw_w1: null, and sets empty_measurement: true.
Replays recorded before this method identifier use legacy w1-support/1 when
the viewer computes counterfactual target scores. Unknown identifiers fail
closed rather than being guessed. See the full decision record in
docs/designs/2026-08-18-w1-scoring-v2.md.
wellness.max uses wellness-sum/1:
sum over seat agents alive at the final tick of mean_window_wellness(agent)
Extinction scores zero. Results report the survivor count, mean wellness, and equal-agent means for DTL's health, conflict, social, family, and wealth happiness components. The population sum is intentionally unbounded above.
Shipped targets
Scalar objective:
wellness.max— maximize summed final-survivor wellness in the Commonwealth league.
Engine-generated (tools/generate_targets.py; validation stats in each file's
generation.stats):
wealth.skewed-gini-0.5—examples/agent_replacement.json({G1},{M},{R}); measured Gini 0.472 vs GAS's ≈0.5population.carrying-capacity—examples/constant_growback.jsonwith immortal agents (GAS's carrying-capacity setup); mean population 223 vs the book's ≈224price.equilibrium—examples/trade_basic.json; pooled mean price 1.11 vs GAS's equilibrium ≈1tribe.convergence—examples/cultural_tagging.json; mean majority share 0.79
Engine-generated counterfactuals (no empirical or GAS anchor — deliberately aspirational shapes; a target need not be perfectly reachable in a variant to be a target, players simply get as close as their rulesets allow):
wealth.egalitarian— equalized endowments + universal income + short lifespans compress wealth to Gini ≈ 0.30 (vs ~0.47 default); no real-world wealth distribution is this equal (empirical Ginis ≥ ~0.5)tribe.diversity— quadrant-separated tribes hold a stable split (mean majority share 0.64)
Still provisional:
age-at-death.survivorship— empirical source: HMD life-tabledx(CC BY 4.0); convert withtools/convert_hmd_survivorship.pyonce the life table is downloaded (UN WPP abridged tables, CC BY 3.0 IGO, are the fallback)
Shelved (2026-08-11): disease.endemic and disease.eradicated. No DTL
configuration we found sustains a stable endemic equilibrium — immune systems
eradicate within ~10 ticks, or transmission/penalty pressure collapses the
population (an 81.8%-prevalence configuration existed but killed its hosts).
Revisit with a disease-reintroduction mechanism if disease variants return.
sick_fraction remains measured (measurement-only canonical bins in
src/coworld/targets.py) so results histograms stay complete.
Replacing a provisional histogram with generated or empirical data requires
preserving that variable's canonical support and bins, updating the provenance,
and setting provisional accurately.
Probing Commonwealth components
tools/probe_commonwealth.py runs the bundled fixed constitution against the
canonical Commonwealth config and fails unless every DTL happiness component
has a nonzero running observation. It defaults to reproducible episode seed
1729 while leaving the variant's mechanics unchanged:
PYTHONHASHSEED=0 .venv/bin/python tools/probe_commonwealth.py \
--out build/commonwealth-probe/report.json
The report records sample counts and observed ranges for health, conflict, social, family, and wealth happiness, along with the final wellness score.