Support games with up to 6 players (OMNI-26) #27

Merged
Breadway merged 1 commit from feature/six-player-games into main 2026-10-08 19:54:35 +08:00
Owner

Closes OMNI-26

Stacked on #25 (base is fix/review-findings, so the diff is just this change). Retarget to main once #25 merges.

What changed

  • MAX_PLAYERS is 6. The value head, masks and per-player resource arrays follow it. The validator accepts 2 to 6 players.
  • One zero-sum payoff rule for any player count (zero_sum_payoffs): a sole winner scores +1 and every other player -1/(n-1). Shared wins, or survivors when only losers are flagged, share the win on the same scale; everyone or nobody winning is a draw. Timeouts and random adjudication use the same rule. Two-player results are unchanged except that both players losing at once is now a draw instead of -1 each.
  • New PRef::Target(i): acting on the player chosen by a Player action target. Before this, a chosen player could not be referenced from an effect at all (the tempo attack_face hits Opp regardless of the target), which made targeted player actions meaningless beyond two players. The validator checks the target exists and is a player target. Opp stays "next seat in turn order" and is documented as such.
  • Evaluation: play_match puts the agent under test in a rotating seat against n-1 copies of the opponent and classifies wins by payoff sign. omnia play and omnia simulate work per seat. Generator measurement reports a win rate per seat (seat_win_rates), scales the side-balance floor by player count, and rotates the seat in the skill checks.
  • games/melee.ron: a six-player free-for-all point race with player-targeted raids, used by tests and as an example.

Tests

Zero-sum payoff rule for 2 to 6 players; the three hand-written games run to completion with 2 to 6 players and zero-sum payoffs; limit of 2 to 6 enforced; player-target raids hit only the chosen seat; validator checks on player-target references; six-player observations and batches through the model; six-player matches (MCTS beats random, random wins about 1 game in 6); six-player generator measurement. The generic engine tests (determinism, replay, relabelling, hidden-information) now also run on melee.

Limits worth knowing

  • No generator family produces more than two players yet; only melee exercises 3+ players in a purpose-built game.
  • Games with deep response chains need limits.max_stack scaled with the player count (gambit with 5 players overflowed the 2-player limit of 8).
  • Win/draw/loss counts in play_match are by payoff sign; the Wilson interval treats a game as a Bernoulli trial, which is only indicative with more than two players.
  • The model input shape changes again (value head width 6), on top of the layout change in #25, so existing checkpoints do not load either way.
Closes OMNI-26 Stacked on #25 (base is `fix/review-findings`, so the diff is just this change). Retarget to `main` once #25 merges. ## What changed - `MAX_PLAYERS` is 6. The value head, masks and per-player resource arrays follow it. The validator accepts 2 to 6 players. - One zero-sum payoff rule for any player count (`zero_sum_payoffs`): a sole winner scores +1 and every other player `-1/(n-1)`. Shared wins, or survivors when only losers are flagged, share the win on the same scale; everyone or nobody winning is a draw. Timeouts and random adjudication use the same rule. Two-player results are unchanged except that both players losing at once is now a draw instead of -1 each. - New `PRef::Target(i)`: acting on the player chosen by a `Player` action target. Before this, a chosen player could not be referenced from an effect at all (the tempo `attack_face` hits `Opp` regardless of the target), which made targeted player actions meaningless beyond two players. The validator checks the target exists and is a player target. `Opp` stays "next seat in turn order" and is documented as such. - Evaluation: `play_match` puts the agent under test in a rotating seat against `n-1` copies of the opponent and classifies wins by payoff sign. `omnia play` and `omnia simulate` work per seat. Generator measurement reports a win rate per seat (`seat_win_rates`), scales the side-balance floor by player count, and rotates the seat in the skill checks. - `games/melee.ron`: a six-player free-for-all point race with player-targeted raids, used by tests and as an example. ## Tests Zero-sum payoff rule for 2 to 6 players; the three hand-written games run to completion with 2 to 6 players and zero-sum payoffs; limit of 2 to 6 enforced; player-target raids hit only the chosen seat; validator checks on player-target references; six-player observations and batches through the model; six-player matches (MCTS beats random, random wins about 1 game in 6); six-player generator measurement. The generic engine tests (determinism, replay, relabelling, hidden-information) now also run on melee. ## Limits worth knowing - No generator family produces more than two players yet; only `melee` exercises 3+ players in a purpose-built game. - Games with deep response chains need `limits.max_stack` scaled with the player count (gambit with 5 players overflowed the 2-player limit of 8). - Win/draw/loss counts in `play_match` are by payoff sign; the Wilson interval treats a game as a Bernoulli trial, which is only indicative with more than two players. - The model input shape changes again (value head width 6), on top of the layout change in #25, so existing checkpoints do not load either way.
- MAX_PLAYERS 4 -> 6 (value head, masks and resource arrays follow)
- one zero-sum payoff rule for any player count (zero_sum_payoffs): sole
  winner +1, others -1/(n-1), shared wins and all-win/all-lose are draws;
  also used for timeouts and random adjudication
- PRef::Target(i): act on the player chosen by an action target (Opp is only
  the next seat), with validator checks
- play_match, omnia play/simulate and generator measurement work per seat for
  n players (rotating seat, win = positive payoff, per-seat win rates, side
  balance floor scaled by player count)
- games/melee.ron: six-player free-for-all example, plus tests for 2 to 6
  players across engine, validator, model batching, matches and measurement
Author
Owner

Review (board cleanup 2026-10-08)

Verified on top of #25: the build is clean and all workspace tests pass, including the new six-player engine, match, measurement and model tests.

  • zero_sum_payoffs gives the same results as the old code for 2 players: a sole winner gets +1/-1, a shared win or nobody winning is a draw, a sole survivor gets +1, and a timeout tie is a draw. Existing 2p results are unaffected.
  • play_match and measure rotate the agent under test through all seats. The side-balance floor scales with 2/n. The note that scores are only indicative for n>2 is accurate.
  • PRef::Target(i) is checked by the validator against both scope and a Player-target slot. The engine falls back to me on a mismatch, which can only be reached by an invalid def.
  • Format break: MAX_PLAYERS 4->6 widens the value head and every [f32; MAX_PLAYERS] payoff/value array. Older checkpoints and serialized samples will not load, the same as with #25.

Ready to merge after #25. Retarget the base to main first.

## Review (board cleanup 2026-10-08) Verified on top of #25: the build is clean and all workspace tests pass, including the new six-player engine, match, measurement and model tests. - `zero_sum_payoffs` gives the same results as the old code for 2 players: a sole winner gets +1/-1, a shared win or nobody winning is a draw, a sole survivor gets +1, and a timeout tie is a draw. Existing 2p results are unaffected. - `play_match` and `measure` rotate the agent under test through all seats. The side-balance floor scales with 2/n. The note that scores are only indicative for n>2 is accurate. - `PRef::Target(i)` is checked by the validator against both scope and a Player-target slot. The engine falls back to `me` on a mismatch, which can only be reached by an invalid def. - **Format break:** `MAX_PLAYERS` 4->6 widens the value head and every `[f32; MAX_PLAYERS]` payoff/value array. Older checkpoints and serialized samples will not load, the same as with #25. Ready to merge after #25. Retarget the base to main first.
Breadway changed target branch from fix/review-findings to main 2026-10-08 19:54:34 +08:00
Breadway deleted branch feature/six-player-games 2026-10-08 19:54:35 +08:00
Author
Owner

Retargeted from fix/review-findings to main after #25 merged, then merged 2026-10-08. All tests pass, including the six-player ones. MAX_PLAYERS 4->6 widens the value head, so older checkpoints will not load. Closes OMNI-26.

Retargeted from fix/review-findings to main after #25 merged, then merged 2026-10-08. All tests pass, including the six-player ones. MAX_PLAYERS 4->6 widens the value head, so older checkpoints will not load. Closes OMNI-26.
Sign in to join this conversation.
No reviewers
No labels
bug
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Breadway/omniastra!27
No description provided.