Skip to content

Reference controllers

Foray provides a handful of baseline controllers to the model through the adversarial run-container image, alongside the foray CLI and the map. Their purpose is to give the model something concrete to run and play against locally while it develops its own controller — a yardstick, not a template.

Every baseline is deliberately mediocre. Each one has an obvious, exploitable weakness, and none of them accounts properly for Foray’s twist. The point of the case is for the model to invent a controller that beats these comfortably and then beats other models — a submission that merely matches a baseline has not done the work. The model is free to read their source; it is expected not to copy it.

The run image provides, under $FORAY_HOME (/opt/foray), for each baseline:

  • the readable Rust source of the controller ($FORAY_HOME/references/<name>/lib.rs), and
  • a pre-built .wasm module ($FORAY_HOME/references/<name>.wasm),

plus the canonical map ($FORAY_HOME/maps/mirror-32x16.toml) and the controller buildkit ($FORAY_HOME/buildkit — fresh copies of foray-core and foray-controller-sdk the model’s controller crate path-depends on, so the seeded workspace vendors nothing). With foray preinstalled on PATH, the model can immediately run, e.g.:

Terminal window
foray simulate --red ./target/.../controller.wasm \
--blue "$FORAY_HOME/references/greedy-raider.wasm" \
--map "$FORAY_HOME/maps/mirror-32x16.toml" --out replay.json

and iterate against a known opponent. All three baselines are built from the same controller contract the model targets, so they double as worked examples of reading world and returning action. (The buildkit, CLI, and references are baked from this repo at image-build time so they stay in lockstep with the engine the validator scores with — see the run-container definition under containers/adversarial/.)

Picks a uniformly random legal move for each owned agent every tick (falling back to Stop when boxed in). It does not seek seeds, defend, or even prefer crossing the border. It exists as an absolute floor: any serious controller should beat it overwhelmingly, and a controller that doesn’t clearly beat random has a bug.

Sends every agent to forage: each one beelines (shortest path) toward the nearest enemy seed cache, eats whatever it reaches, and heads home only when it can carry no more or is boxed out. It never defends and ignores carry weight — it happily over-loads and crawls home. Its weaknesses are the two lessons of the case: leaving your own caches completely undefended, and turning heavy raiders into easy tags. A competent defender shreds it.

A naive “balanced” strategy: it statically assigns one or two agents to patrol the border and chase the nearest visible intruder, and sends the rest in as greedy-raider-style foragers. It is the strongest baseline, but its role assignment never adapts — it does not reinforce a collapsing defence, recall a raider to bank before it is caught, reason about jelly, or weigh load against the distance home. It rewards a controller that reads the match state and reallocates its three agents dynamically.

For v1, a run is scored on one canonical match: the validator builds the model’s submission to wasm, loads it as Red, loads the case’s committed border-soldier module as Blue, and runs that single Foray match on the fixed mirror-32x16 map and seed. The published replay.json is that match, and the recorded outcome (win / loss / draw / forfeit, plus winner, score, ticks) is from the submission’s perspective. border-soldier is the canonical opponent precisely because it is the strongest of the three baselines — beating it is the bar v1 sets.

random and greedy-raider are provided under $FORAY_HOME/references as local sparring partners (run them with foray simulate as above), but they are not the scored opponent. Cross-model round-robin / bracket tournaments — the field-wide standings the test-type evaluation describes — are a planned future step and are out of scope for v1: the match structure is still recorded faithfully in the manifest, but the validator only runs the one canonical match against the committed baseline.

The three baselines span the obvious failure space on purpose — pure noise (random), pure greed (greedy-raider), and rigid role-splitting (border-soldier) — so the model can see what losing looks like from three directions without being handed a winning shape. The interesting strategy space — balancing offence against defence tick by tick, timing a bank against carry weight, spending jelly both to survive and to kill, and contesting the drifting large seeds — is left entirely for the model to discover.