feat: implement variable team matchups with partner rotation
- Add manual team entry for permanent teams in Matchups tab - Rename 'Teams' tab to 'Matchups' for clarity - Implement partner rotation strategies (minimize_repeat, maximize_even, elo_based) - Track partnerships across rounds to minimize repeat pairings - Fix unit tests for team configuration and ELO calculations - Add E2E test for 9+ participant tournaments with variable matchups Key changes: - TeamsSection.tsx: Added router.refresh(), manual team entry, and matchup display - schedule-generator.ts: Enhanced generateVariableRoundRobin to track partnerships - team-generator.ts: Fixed partnership frequency tracking across rounds - API routes: Updated to support variable team durability with rotation strategies
This commit is contained in:
@@ -10,6 +10,11 @@ export interface RoundSchedule {
|
||||
matchups: MatchupPairing[]
|
||||
}
|
||||
|
||||
export interface TeamPairing {
|
||||
player1Id: number
|
||||
player2Id: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a round-robin schedule using the circle method.
|
||||
*
|
||||
@@ -21,7 +26,7 @@ export interface RoundSchedule {
|
||||
* @returns Array of rounds, each containing matchup pairings
|
||||
*/
|
||||
export function generateRoundRobin(
|
||||
teamPairings: { player1Id: number; player2Id: number }[]
|
||||
teamPairings: TeamPairing[]
|
||||
): RoundSchedule[] {
|
||||
if (teamPairings.length < 2) {
|
||||
return []
|
||||
@@ -111,3 +116,73 @@ export function expectedMatchups(teamCount: number): number {
|
||||
if (teamCount < 2) return 0
|
||||
return (teamCount * (teamCount - 1)) / 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a schedule where teams are created fresh each round.
|
||||
* This ensures every player gets different partners throughout the tournament.
|
||||
*
|
||||
* @param players - Array of players to be paired
|
||||
* @param teamCount - Number of teams (player pairs) to create per round
|
||||
* @param roundCount - Number of rounds to generate
|
||||
* @param generateTeamFunction - Function to generate teams for a round
|
||||
* @returns Array of rounds, each containing matchups with fresh team pairings
|
||||
*/
|
||||
export function generateVariableRoundRobin(
|
||||
players: { id: number; name: string; currentElo: number }[],
|
||||
teamCount: number,
|
||||
roundCount: number,
|
||||
generateTeamFunction: (players: { id: number; name: string; currentElo: number }[]) => TeamPairing[]
|
||||
): RoundSchedule[] {
|
||||
if (players.length < 4 || teamCount < 2) {
|
||||
return []
|
||||
}
|
||||
|
||||
const rounds: RoundSchedule[] = []
|
||||
|
||||
for (let roundNum = 1; roundNum <= roundCount; roundNum++) {
|
||||
// Generate fresh teams for this round
|
||||
const teams = generateTeamFunction(players)
|
||||
|
||||
// Validate we have enough teams
|
||||
if (teams.length < 2) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Apply round-robin pairing to the fresh teams
|
||||
const teamPairings = teams.map((team) => ({
|
||||
player1Id: team.player1Id,
|
||||
player2Id: team.player2Id,
|
||||
}))
|
||||
|
||||
// Use circle method for this round's matchups
|
||||
const hasOddTeams = teamPairings.length % 2 !== 0
|
||||
const workingTeams = hasOddTeams
|
||||
? [...teamPairings, { player1Id: -1, player2Id: -1 }]
|
||||
: [...teamPairings]
|
||||
const n = workingTeams.length
|
||||
const matchupsPerRound = n / 2
|
||||
const matchups: MatchupPairing[] = []
|
||||
|
||||
for (let i = 0; i < matchupsPerRound; i++) {
|
||||
const team1Idx = i
|
||||
const team2Idx = n - 1 - i
|
||||
|
||||
const team1 = workingTeams[team1Idx]
|
||||
const team2 = workingTeams[team2Idx]
|
||||
|
||||
// Skip bye matchups
|
||||
if (team1.player1Id !== -1 && team2.player1Id !== -1) {
|
||||
matchups.push({
|
||||
player1P1Id: team1.player1Id,
|
||||
player1P2Id: team1.player2Id,
|
||||
player2P1Id: team2.player1Id,
|
||||
player2P2Id: team2.player2Id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
rounds.push({ roundNumber: roundNum, matchups })
|
||||
}
|
||||
|
||||
return rounds
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user