refactor(lib): update schedule and team generators to use player pairings

This commit is contained in:
2026-04-03 21:03:31 -07:00
parent aa98600147
commit 803b79f03c
+35 -18
View File
@@ -1,6 +1,8 @@
export interface MatchupPairing { export interface MatchupPairing {
team1Id: number player1P1Id: number
team2Id: number player1P2Id: number
player2P1Id: number
player2P2Id: number
} }
export interface RoundSchedule { export interface RoundSchedule {
@@ -15,18 +17,22 @@ export interface RoundSchedule {
* team exactly once. If N is odd, a "bye" is added internally so one * team exactly once. If N is odd, a "bye" is added internally so one
* team sits out each round (the bye matchup is excluded from output). * team sits out each round (the bye matchup is excluded from output).
* *
* @param teamIds - Array of team IDs to schedule * @param teamPairings - Array of player pairings (each with 2 player IDs)
* @returns Array of rounds, each containing matchup pairings * @returns Array of rounds, each containing matchup pairings
*/ */
export function generateRoundRobin(teamIds: number[]): RoundSchedule[] { export function generateRoundRobin(
if (teamIds.length < 2) { teamPairings: { player1Id: number; player2Id: number }[]
): RoundSchedule[] {
if (teamPairings.length < 2) {
return [] return []
} }
// Use circle method: fix first team, rotate the rest // Use circle method: fix first team, rotate the rest
// If odd number of teams, add a sentinel for byes // If odd number of teams, add a sentinel for byes
const hasOddTeams = teamIds.length % 2 !== 0 const hasOddTeams = teamPairings.length % 2 !== 0
const workingTeams = hasOddTeams ? [...teamIds, -1] : [...teamIds] const workingTeams = hasOddTeams
? [...teamPairings, { player1Id: -1, player2Id: -1 }]
: [...teamPairings]
const n = workingTeams.length const n = workingTeams.length
const numRounds = n - 1 const numRounds = n - 1
const matchupsPerRound = n / 2 const matchupsPerRound = n / 2
@@ -40,12 +46,17 @@ export function generateRoundRobin(teamIds: number[]): RoundSchedule[] {
const team1Idx = i const team1Idx = i
const team2Idx = n - 1 - i const team2Idx = n - 1 - i
const team1Id = workingTeams[team1Idx] const team1 = workingTeams[team1Idx]
const team2Id = workingTeams[team2Idx] const team2 = workingTeams[team2Idx]
// Skip bye matchups (where either team is the sentinel -1) // Skip bye matchups (where either team is the sentinel)
if (team1Id !== -1 && team2Id !== -1) { if (team1.player1Id !== -1 && team2.player1Id !== -1) {
matchups.push({ team1Id, team2Id }) matchups.push({
player1P1Id: team1.player1Id,
player1P2Id: team1.player2Id,
player2P1Id: team2.player1Id,
player2P2Id: team2.player2Id,
})
} }
} }
@@ -61,19 +72,25 @@ export function generateRoundRobin(teamIds: number[]): RoundSchedule[] {
} }
/** /**
* Validate that a set of team IDs can be scheduled. * Validate that a set of player pairings can be scheduled.
*/ */
export function validateScheduleInput(teamIds: number[]): { export function validateScheduleInput(
teamPairings: { player1Id: number; player2Id: number }[]
): {
valid: boolean valid: boolean
error?: string error?: string
} { } {
if (teamIds.length < 2) { if (teamPairings.length < 2) {
return { valid: false, error: "At least 2 teams are required to generate a schedule" } return { valid: false, error: "At least 2 teams are required to generate a schedule" }
} }
const uniqueIds = new Set(teamIds) // Check for duplicate teams
if (uniqueIds.size !== teamIds.length) { const teamKeys = teamPairings.map(
return { valid: false, error: "Duplicate team IDs found" } (t) => [t.player1Id, t.player2Id].sort().join('-')
)
const uniqueKeys = new Set(teamKeys)
if (uniqueKeys.size !== teamPairings.length) {
return { valid: false, error: "Duplicate team pairings found" }
} }
return { valid: true } return { valid: true }