Add support for up to 6 players.

This commit is contained in:
Greyson Parrelli
2026-07-28 07:36:09 -04:00
parent e542118175
commit a4f5f6910d
38 changed files with 2306 additions and 713 deletions
+87
View File
@@ -0,0 +1,87 @@
package game
// Battle pairings for multiplayer games ("4 & 6 Player Mode" in the rulebook).
//
// Every round, players split into pairs and each pair fights its own battle.
// The pairings are a fixed table printed in the book — seats are lettered
// A, B, C… in seat order, so seat 0 is A. Both tables are transcribed
// literally rather than generated: each is a round-robin that runs out of
// fresh pairings before six rounds are up (three rounds exhaust four players,
// five rounds exhaust six), and the book's choice of which earlier rounds to
// replay for the remainder is a decision, not something a generator would
// reproduce.
// Matchup is one battle: the two seats that fight it. The order is the printed
// order and carries no meaning on its own — the first player of each battle is
// decided separately (a coin flip; see Game.startBattles).
type Matchup [2]int
// pairingTables maps a player count to its per-round pairings, indexed by
// round-1. Two players is the degenerate case: one pairing, every round.
var pairingTables = map[int][MaxRounds][]Matchup{
2: {
{{0, 1}}, // R1 A/B
{{0, 1}}, // R2 A/B
{{0, 1}}, // R3 A/B
{{0, 1}}, // R4 A/B
{{0, 1}}, // R5 A/B
{{0, 1}}, // R6 A/B
},
4: {
{{0, 1}, {2, 3}}, // R1 A/B C/D
{{0, 2}, {1, 3}}, // R2 A/C B/D
{{0, 3}, {1, 2}}, // R3 A/D B/C
{{0, 1}, {2, 3}}, // R4 A/B C/D
{{0, 2}, {1, 3}}, // R5 A/C B/D
{{0, 3}, {1, 2}}, // R6 A/D B/C
},
6: {
{{0, 1}, {2, 3}, {4, 5}}, // R1 A/B C/D E/F
{{0, 2}, {1, 4}, {3, 5}}, // R2 A/C B/E D/F
{{0, 3}, {1, 5}, {2, 4}}, // R3 A/D B/F C/E
{{0, 4}, {1, 3}, {2, 5}}, // R4 A/E B/D C/F
{{0, 5}, {1, 2}, {3, 4}}, // R5 A/F B/C D/E
{{0, 1}, {2, 3}, {4, 5}}, // R6 A/B C/D E/F
},
}
// PlayerCounts lists the player counts a game can be played at, in order. The
// game needs an even number of players so everyone has an opponent every
// round; a lobby with an odd number of humans fills the gap with bots.
var PlayerCounts = []int{2, 4, 6}
// ValidPlayerCount reports whether n players can start a game.
func ValidPlayerCount(n int) bool {
_, ok := pairingTables[n]
return ok
}
// Pairings returns the battles for a round (1-based) at the given player
// count, or nil if either is out of range. The returned slice is shared table
// data — treat it as read-only.
func Pairings(players, round int) []Matchup {
table, ok := pairingTables[players]
if !ok || round < 1 || round > MaxRounds {
return nil
}
return table[round-1]
}
// OpponentOf returns the seat that `seat` fights in the given round, or -1 if
// it has no battle (which the fixed tables never produce for a valid count).
func OpponentOf(players, round, seat int) int {
for _, m := range Pairings(players, round) {
switch seat {
case m[0]:
return m[1]
case m[1]:
return m[0]
}
}
return -1
}
// Pairings returns this round's battle pairings for the game's player count.
func (g *Game) Pairings() []Matchup {
return Pairings(len(g.Players), g.Round)
}