Skip to main content

Module match_engine

Module match_engine 

Source
Expand description

The Phase-2a event-based possession match engine (MATCH_MODEL.md), behind the same play_match call site the Phase-1 crude engine used to occupy. State space, resolution model, the wide route, and the knob table are a faithful Rust port of the calibrated Python prototype (match_model_prototype.ipynb, referenced from MATCH_MODEL.md §1) — nothing here is a re-guess of the shape-finding, only its translation.

Deferred to Phase 2e (behind this same call site, no structural change): tactics as transition-matrix modifiers, cards & fouls, injuries, set pieces, substitutions, and the character/hidden attributes.

Structs§

FormationStats
Per-formation usage seen by StreamTelemetry::record — one increment per side per match (a formation used by both home and away in the same match counts twice), keyed by Lineup::formation in the caller.
Knobs
MatchEvent
One beat in the minute-by-minute stream. zone is the zone-entry context (MATCH_MODEL.md §9) so a beat can say where on the pitch it happened.
MatchOutcome
The result of a simulated match: the score that folds into GameState (via Event::MatchPlayed) plus the minute-by-minute trace. The trace rides alongside the fold, never inside it (MATCH_MODEL.md §7) — it is a Trace, not a fold input, and callers are free to discard it. Nothing here is persisted by commands::advance_matchday; only the score is.
StreamTelemetry
Accumulates match aggregates directly from MatchOutcome traces — everything SeasonTelemetry can’t see because the fold discards the stream. Mirrors the notebook’s report() fields.

Enums§

MatchEventKind
ShotKind
How the ball arrived at the shot (MATCH_MODEL.md §5) — the discriminant that makes headed vs long-range goals countable for the goal-source-mix metric. Through-ball, dribbled, and cutback finishes share Finish; only the attacker-attribute selection and chance-quality knob differ between them internally (§9’s stream schema pins exactly these three variants).
ShotOutcome
ShotSource
How the possession reached the shot (MATCH_MODEL.md §5’s arrival table) — finer-grained than ShotKind, which collapses through-ball, dribble, and cutback finishes into Finish. This is what makes the wide-origin-goal-share calibration target (cross + cutback, MATCH_MODEL.md §8) actually computable from the stream, not just headed-goal share. A rebound follow-up shot keeps the source of the shot that created it — the rebound is a continuation of the same attack, not a new arrival route.
Side
Which side an event belongs to. Distinct from fforge_domain::ClubId — the stream is home/away-relative; a caller maps it to real clubs via the Fixture it came from.
Zone
Def/Mid/AttC/AttW are dwelling zones; Box is not dwelt in — an edge that reaches it resolves a shot immediately (arrival = chance).

Functions§

ai_pick_lineup
Deterministic AI team selection: for each formation, greedily fill slots with the best remaining player by CA-in-slot-role (ties → lower player id); keep the formation with the best mean. This is the Phase-1 stub of the layer-3 club decision AI — same seam, richer policy later.
lineup_strength
Mean CA-in-slot-role over the eleven — a squad-quality scalar independent of any particular match-resolution model. Used for display and by ai_pick_lineup’s formation comparison below.
play_match
Simulate one match: (lineups, world, rng) in, score + trace out. A pure function of its inputs — same seed stream, same outcome, by construction (MATCH_MODEL.md §7).