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) }