Add support for up to 6 players.
This commit is contained in:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user