diff --git a/src/lib/elo-utils.ts b/src/lib/elo-utils.ts index 893f6c6..bf531c7 100644 --- a/src/lib/elo-utils.ts +++ b/src/lib/elo-utils.ts @@ -177,10 +177,10 @@ export async function recalculateAllElo(prisma: PrismaClient) { const matches = await prisma.match.findMany({ orderBy: { playedAt: 'asc' }, include: { - team1P1: true, - team1P2: true, - team2P1: true, - team2P2: true, + player1P1: true, + player1P2: true, + player2P1: true, + player2P2: true, }, }); @@ -230,13 +230,18 @@ export async function recalculateAllElo(prisma: PrismaClient) { // Process each match in chronological order for (const match of matches) { - const { team1P1, team1P2, team2P1, team2P2, team1Score, team2Score, id: matchId, playedAt } = match; + const { player1P1, player1P2, player2P1, player2P2, team1Score, team2Score, id: matchId, playedAt } = match; + + // Skip matches with missing players + if (!player1P1 || !player1P2 || !player2P1 || !player2P2) { + continue; + } // Get current ratings for all players - const p1Rating = getPlayerStats(team1P1.id).rating; - const p2Rating = getPlayerStats(team1P2.id).rating; - const p3Rating = getPlayerStats(team2P1.id).rating; - const p4Rating = getPlayerStats(team2P2.id).rating; + const p1Rating = getPlayerStats(player1P1.id).rating; + const p2Rating = getPlayerStats(player1P2.id).rating; + const p3Rating = getPlayerStats(player2P1.id).rating; + const p4Rating = getPlayerStats(player2P2.id).rating; // Calculate team ratings const team1Rating = calculateTeamElo(p1Rating, p2Rating); @@ -261,7 +266,7 @@ export async function recalculateAllElo(prisma: PrismaClient) { const isTie = team1Score === team2Score; // Update Player 1 (team 1, player 1) - const stats1 = getPlayerStats(team1P1.id); + const stats1 = getPlayerStats(player1P1.id); stats1.rating += p1Change; stats1.gamesPlayed += 1; if (isTie) { @@ -273,7 +278,7 @@ export async function recalculateAllElo(prisma: PrismaClient) { } // Update Player 2 (team 1, player 2) - const stats2 = getPlayerStats(team1P2.id); + const stats2 = getPlayerStats(player1P2.id); stats2.rating += p2Change; stats2.gamesPlayed += 1; if (isTie) { @@ -285,7 +290,7 @@ export async function recalculateAllElo(prisma: PrismaClient) { } // Update Player 3 (team 2, player 1) - const stats3 = getPlayerStats(team2P1.id); + const stats3 = getPlayerStats(player2P1.id); stats3.rating += p3Change; stats3.gamesPlayed += 1; if (isTie) { @@ -297,7 +302,7 @@ export async function recalculateAllElo(prisma: PrismaClient) { } // Update Player 4 (team 2, player 2) - const stats4 = getPlayerStats(team2P2.id); + const stats4 = getPlayerStats(player2P2.id); stats4.rating += p4Change; stats4.gamesPlayed += 1; if (isTie) { @@ -309,7 +314,7 @@ export async function recalculateAllElo(prisma: PrismaClient) { } // Update partnership stats for team 1 - const partnership1 = getPartnershipStats(team1P1.id, team1P2.id); + const partnership1 = getPartnershipStats(player1P1.id, player1P2.id); partnership1.gamesPlayed += 1; if (isTie) { // For ties, don't increment wins or losses (gamesPlayed is already incremented) @@ -324,7 +329,7 @@ export async function recalculateAllElo(prisma: PrismaClient) { } // Update partnership stats for team 2 - const partnership2 = getPartnershipStats(team2P1.id, team2P2.id); + const partnership2 = getPartnershipStats(player2P1.id, player2P2.id); partnership2.gamesPlayed += 1; if (isTie) { // For ties, don't increment wins or losses (gamesPlayed is already incremented) @@ -340,10 +345,10 @@ export async function recalculateAllElo(prisma: PrismaClient) { // Create elo snapshots for all players const snapshotData = [ - { playerId: team1P1.id, ratingBefore: p1Rating, ratingChange: p1Change }, - { playerId: team1P2.id, ratingBefore: p2Rating, ratingChange: p2Change }, - { playerId: team2P1.id, ratingBefore: p3Rating, ratingChange: p3Change }, - { playerId: team2P2.id, ratingBefore: p4Rating, ratingChange: p4Change }, + { playerId: player1P1.id, ratingBefore: p1Rating, ratingChange: p1Change }, + { playerId: player1P2.id, ratingBefore: p2Rating, ratingChange: p2Change }, + { playerId: player2P1.id, ratingBefore: p3Rating, ratingChange: p3Change }, + { playerId: player2P2.id, ratingBefore: p4Rating, ratingChange: p4Change }, ]; for (const snapshot of snapshotData) { diff --git a/src/lib/glicko2-utils.ts b/src/lib/glicko2-utils.ts index bf29a65..81ed1ef 100644 --- a/src/lib/glicko2-utils.ts +++ b/src/lib/glicko2-utils.ts @@ -259,10 +259,10 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { const matches = await prisma.match.findMany({ orderBy: { playedAt: 'asc' }, include: { - team1P1: true, - team1P2: true, - team2P1: true, - team2P2: true, + player1P1: true, + player1P2: true, + player2P1: true, + player2P2: true, }, }); @@ -291,7 +291,12 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { // Process each match for (const match of matches) { - const { team1P1, team1P2, team2P1, team2P2, team1Score, team2Score } = match; + const { player1P1, player1P2, player2P1, player2P2, team1Score, team2Score } = match; + + // Skip matches with missing players + if (!player1P1 || !player1P2 || !player2P1 || !player2P2) { + continue; + } // Get current ratings const getOrCreatePlayer = (playerId: number) => { @@ -306,10 +311,10 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { return glicko.makePlayer(record.rating, record.deviation, record.volatility); }; - const p1 = getOrCreatePlayer(team1P1.id); - const p2 = getOrCreatePlayer(team1P2.id); - const p3 = getOrCreatePlayer(team2P1.id); - const p4 = getOrCreatePlayer(team2P2.id); + const p1 = getOrCreatePlayer(player1P1.id); + const p2 = getOrCreatePlayer(player1P2.id); + const p3 = getOrCreatePlayer(player2P1.id); + const p4 = getOrCreatePlayer(player2P2.id); const team1Won = team1Score > team2Score; const team2Won = team2Score > team1Score; @@ -330,22 +335,22 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { glicko.updateRatings(matchesToUpdate); // Update in-memory ratings - playerRatings.set(team1P1.id, { + playerRatings.set(player1P1.id, { rating: p1.getRating(), deviation: p1.getRd(), volatility: p1.getVol() }); - playerRatings.set(team1P2.id, { + playerRatings.set(player1P2.id, { rating: p2.getRating(), deviation: p2.getRd(), volatility: p2.getVol() }); - playerRatings.set(team2P1.id, { + playerRatings.set(player2P1.id, { rating: p3.getRating(), deviation: p3.getRd(), volatility: p3.getVol() }); - playerRatings.set(team2P2.id, { + playerRatings.set(player2P2.id, { rating: p4.getRating(), deviation: p4.getRd(), volatility: p4.getVol() @@ -353,7 +358,7 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { // Update database records await (prisma as any).glicko2Rating.upsert({ - where: { playerId: team1P1.id }, + where: { playerId: player1P1.id }, update: { rating: p1.getRating(), deviation: p1.getRd(), @@ -364,7 +369,7 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { draws: isTie ? { increment: 1 } : undefined, }, create: { - playerId: team1P1.id, + playerId: player1P1.id, rating: p1.getRating(), deviation: p1.getRd(), volatility: p1.getVol(), @@ -376,7 +381,7 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { }); await (prisma as any).glicko2Rating.upsert({ - where: { playerId: team1P2.id }, + where: { playerId: player1P2.id }, update: { rating: p2.getRating(), deviation: p2.getRd(), @@ -387,7 +392,7 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { draws: isTie ? { increment: 1 } : undefined, }, create: { - playerId: team1P2.id, + playerId: player1P2.id, rating: p2.getRating(), deviation: p2.getRd(), volatility: p2.getVol(), @@ -399,7 +404,7 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { }); await (prisma as any).glicko2Rating.upsert({ - where: { playerId: team2P1.id }, + where: { playerId: player2P1.id }, update: { rating: p3.getRating(), deviation: p3.getRd(), @@ -410,7 +415,7 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { draws: isTie ? { increment: 1 } : undefined, }, create: { - playerId: team2P1.id, + playerId: player2P1.id, rating: p3.getRating(), deviation: p3.getRd(), volatility: p3.getVol(), @@ -422,7 +427,7 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { }); await (prisma as any).glicko2Rating.upsert({ - where: { playerId: team2P2.id }, + where: { playerId: player2P2.id }, update: { rating: p4.getRating(), deviation: p4.getRd(), @@ -433,7 +438,7 @@ export async function recalculateAllGlicko2(prisma: PrismaClient) { draws: isTie ? { increment: 1 } : undefined, }, create: { - playerId: team2P2.id, + playerId: player2P2.id, rating: p4.getRating(), deviation: p4.getRd(), volatility: p4.getVol(), diff --git a/src/lib/openskill-utils.ts b/src/lib/openskill-utils.ts index 2c7997c..333eab8 100644 --- a/src/lib/openskill-utils.ts +++ b/src/lib/openskill-utils.ts @@ -187,10 +187,10 @@ export async function recalculateAllOpenSkill(prisma: PrismaClient) { const matches = await prisma.match.findMany({ orderBy: { playedAt: 'asc' }, include: { - team1P1: true, - team1P2: true, - team2P1: true, - team2P2: true, + player1P1: true, + player1P2: true, + player2P1: true, + player2P2: true, }, }); @@ -210,13 +210,18 @@ export async function recalculateAllOpenSkill(prisma: PrismaClient) { // Process each match for (const match of matches) { - const { team1P1, team1P2, team2P1, team2P2, team1Score, team2Score } = match; + const { player1P1, player1P2, player2P1, player2P2, team1Score, team2Score } = match; + + // Skip matches with missing players + if (!player1P1 || !player1P2 || !player2P1 || !player2P2) { + continue; + } // Get current ratings - const p1Rating = playerRatings.get(team1P1.id) ?? { mu: 25.0, sigma: 8.33 }; - const p2Rating = playerRatings.get(team1P2.id) ?? { mu: 25.0, sigma: 8.33 }; - const p3Rating = playerRatings.get(team2P1.id) ?? { mu: 25.0, sigma: 8.33 }; - const p4Rating = playerRatings.get(team2P2.id) ?? { mu: 25.0, sigma: 8.33 }; + const p1Rating = playerRatings.get(player1P1.id) ?? { mu: 25.0, sigma: 8.33 }; + const p2Rating = playerRatings.get(player1P2.id) ?? { mu: 25.0, sigma: 8.33 }; + const p3Rating = playerRatings.get(player2P1.id) ?? { mu: 25.0, sigma: 8.33 }; + const p4Rating = playerRatings.get(player2P2.id) ?? { mu: 25.0, sigma: 8.33 }; const team1Won = team1Score > team2Score; const team2Won = team2Score > team1Score; @@ -228,14 +233,14 @@ export async function recalculateAllOpenSkill(prisma: PrismaClient) { const newRatings = calculateOpenSkillRatings(teams, rankings); // Update in-memory ratings - playerRatings.set(team1P1.id, newRatings[0][0]); - playerRatings.set(team1P2.id, newRatings[0][1]); - playerRatings.set(team2P1.id, newRatings[1][0]); - playerRatings.set(team2P2.id, newRatings[1][1]); + playerRatings.set(player1P1.id, newRatings[0][0]); + playerRatings.set(player1P2.id, newRatings[0][1]); + playerRatings.set(player2P1.id, newRatings[1][0]); + playerRatings.set(player2P2.id, newRatings[1][1]); // Update database records await (prisma as any).openSkillRating.upsert({ - where: { playerId: team1P1.id }, + where: { playerId: player1P1.id }, update: { rating: fromOpenSkillRating(newRatings[0][0]), gamesPlayed: { increment: 1 }, @@ -244,7 +249,7 @@ export async function recalculateAllOpenSkill(prisma: PrismaClient) { draws: isTie ? { increment: 1 } : undefined, }, create: { - playerId: team1P1.id, + playerId: player1P1.id, rating: fromOpenSkillRating(newRatings[0][0]), gamesPlayed: 1, wins: team1Won ? 1 : 0, @@ -254,7 +259,7 @@ export async function recalculateAllOpenSkill(prisma: PrismaClient) { }); await (prisma as any).openSkillRating.upsert({ - where: { playerId: team1P2.id }, + where: { playerId: player1P2.id }, update: { rating: fromOpenSkillRating(newRatings[0][1]), gamesPlayed: { increment: 1 }, @@ -263,7 +268,7 @@ export async function recalculateAllOpenSkill(prisma: PrismaClient) { draws: isTie ? { increment: 1 } : undefined, }, create: { - playerId: team1P2.id, + playerId: player1P2.id, rating: fromOpenSkillRating(newRatings[0][1]), gamesPlayed: 1, wins: team1Won ? 1 : 0, @@ -273,7 +278,7 @@ export async function recalculateAllOpenSkill(prisma: PrismaClient) { }); await (prisma as any).openSkillRating.upsert({ - where: { playerId: team2P1.id }, + where: { playerId: player2P1.id }, update: { rating: fromOpenSkillRating(newRatings[1][0]), gamesPlayed: { increment: 1 }, @@ -282,7 +287,7 @@ export async function recalculateAllOpenSkill(prisma: PrismaClient) { draws: isTie ? { increment: 1 } : undefined, }, create: { - playerId: team2P1.id, + playerId: player2P1.id, rating: fromOpenSkillRating(newRatings[1][0]), gamesPlayed: 1, wins: team2Won ? 1 : 0, @@ -292,7 +297,7 @@ export async function recalculateAllOpenSkill(prisma: PrismaClient) { }); await (prisma as any).openSkillRating.upsert({ - where: { playerId: team2P2.id }, + where: { playerId: player2P2.id }, update: { rating: fromOpenSkillRating(newRatings[1][1]), gamesPlayed: { increment: 1 }, @@ -301,7 +306,7 @@ export async function recalculateAllOpenSkill(prisma: PrismaClient) { draws: isTie ? { increment: 1 } : undefined, }, create: { - playerId: team2P2.id, + playerId: player2P2.id, rating: fromOpenSkillRating(newRatings[1][1]), gamesPlayed: 1, wins: team2Won ? 1 : 0,