feat: update match API to calculate all rating systems
This commit is contained in:
@@ -3,6 +3,8 @@ import { prisma } from "@/lib/prisma";
|
|||||||
import { getSession } from "@/lib/auth-simple";
|
import { getSession } from "@/lib/auth-simple";
|
||||||
|
|
||||||
import { calculateTeamElo, calculateTeamEloChange } from "@/lib/elo-utils";
|
import { calculateTeamElo, calculateTeamEloChange } from "@/lib/elo-utils";
|
||||||
|
import { calculateOpenSkillRatings, fromOpenSkillRating } from "@/lib/openskill-utils";
|
||||||
|
import { calculateGlicko2Ratings } from "@/lib/glicko2-utils";
|
||||||
|
|
||||||
const K_FACTOR = 32; // Standard K-factor for Elo calculations
|
const K_FACTOR = 32; // Standard K-factor for Elo calculations
|
||||||
|
|
||||||
@@ -80,10 +82,7 @@ export async function POST(request: Request) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate Elo changes
|
// Calculate match outcome
|
||||||
const team1Rating = calculateTeamElo(player1!.currentElo, player2!.currentElo);
|
|
||||||
const team2Rating = calculateTeamElo(player3!.currentElo, player4!.currentElo);
|
|
||||||
|
|
||||||
const team1Won = team1Score > team2Score;
|
const team1Won = team1Score > team2Score;
|
||||||
const team2Won = team2Score > team1Score;
|
const team2Won = team2Score > team1Score;
|
||||||
const isTie = team1Score === team2Score;
|
const isTie = team1Score === team2Score;
|
||||||
@@ -91,6 +90,10 @@ export async function POST(request: Request) {
|
|||||||
const team1ScoreActual = isTie ? 0.5 : (team1Won ? 1 : 0);
|
const team1ScoreActual = isTie ? 0.5 : (team1Won ? 1 : 0);
|
||||||
const team2ScoreActual = isTie ? 0.5 : (team2Won ? 1 : 0);
|
const team2ScoreActual = isTie ? 0.5 : (team2Won ? 1 : 0);
|
||||||
|
|
||||||
|
// Calculate Elo changes
|
||||||
|
const team1Rating = calculateTeamElo(player1!.currentElo, player2!.currentElo);
|
||||||
|
const team2Rating = calculateTeamElo(player3!.currentElo, player4!.currentElo);
|
||||||
|
|
||||||
const team1EloChange = calculateTeamEloChange(team1Rating, team2Rating, team1ScoreActual, K_FACTOR);
|
const team1EloChange = calculateTeamEloChange(team1Rating, team2Rating, team1ScoreActual, K_FACTOR);
|
||||||
const team2EloChange = calculateTeamEloChange(team2Rating, team1Rating, team2ScoreActual, K_FACTOR);
|
const team2EloChange = calculateTeamEloChange(team2Rating, team1Rating, team2ScoreActual, K_FACTOR);
|
||||||
|
|
||||||
@@ -99,6 +102,35 @@ export async function POST(request: Request) {
|
|||||||
const player3EloChange = team2EloChange / 2;
|
const player3EloChange = team2EloChange / 2;
|
||||||
const player4EloChange = team2EloChange / 2;
|
const player4EloChange = team2EloChange / 2;
|
||||||
|
|
||||||
|
// Calculate OpenSkill ratings
|
||||||
|
const player1OpenSkill = { mu: player1!.currentElo, sigma: 8.33 };
|
||||||
|
const player2OpenSkill = { mu: player2!.currentElo, sigma: 8.33 };
|
||||||
|
const player3OpenSkill = { mu: player3!.currentElo, sigma: 8.33 };
|
||||||
|
const player4OpenSkill = { mu: player4!.currentElo, sigma: 8.33 };
|
||||||
|
|
||||||
|
const rankings = isTie ? [0, 0] : team1Won ? [0, 1] : [1, 0];
|
||||||
|
const openSkillTeams = [[player1OpenSkill, player2OpenSkill], [player3OpenSkill, player4OpenSkill]];
|
||||||
|
const newOpenSkillRatings = calculateOpenSkillRatings(openSkillTeams, rankings);
|
||||||
|
|
||||||
|
// Calculate Glicko2 ratings
|
||||||
|
const Glicko2 = require('glicko2').Glicko2;
|
||||||
|
const glicko = new Glicko2();
|
||||||
|
const glicko1 = glicko.makePlayer(player1!.currentElo, 350, 0.06);
|
||||||
|
const glicko2 = glicko.makePlayer(player2!.currentElo, 350, 0.06);
|
||||||
|
const glicko3 = glicko.makePlayer(player3!.currentElo, 350, 0.06);
|
||||||
|
const glicko4 = glicko.makePlayer(player4!.currentElo, 350, 0.06);
|
||||||
|
|
||||||
|
const glickoMatches: [any, any, number][] = [];
|
||||||
|
const outcome = isTie ? 0.5 : team1Won ? 1 : 0;
|
||||||
|
|
||||||
|
[glicko1, glicko2].forEach(p1 => {
|
||||||
|
[glicko3, glicko4].forEach(p2 => {
|
||||||
|
glickoMatches.push([p1, p2, outcome]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
glicko.updateRatings(glickoMatches);
|
||||||
|
|
||||||
// Create match
|
// Create match
|
||||||
const match = await prisma.match.create({
|
const match = await prisma.match.create({
|
||||||
data: {
|
data: {
|
||||||
@@ -116,12 +148,24 @@ export async function POST(request: Request) {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update player stats
|
// Update player stats (Elo)
|
||||||
await updatePlayerStats(parseInt(team1P1Id), team1Won, player1EloChange, isTie);
|
await updatePlayerStats(parseInt(team1P1Id), team1Won, player1EloChange, isTie);
|
||||||
await updatePlayerStats(parseInt(team1P2Id), team1Won, player2EloChange, isTie);
|
await updatePlayerStats(parseInt(team1P2Id), team1Won, player2EloChange, isTie);
|
||||||
await updatePlayerStats(parseInt(team2P1Id), team2Won, player3EloChange, isTie);
|
await updatePlayerStats(parseInt(team2P1Id), team2Won, player3EloChange, isTie);
|
||||||
await updatePlayerStats(parseInt(team2P2Id), team2Won, player4EloChange, isTie);
|
await updatePlayerStats(parseInt(team2P2Id), team2Won, player4EloChange, isTie);
|
||||||
|
|
||||||
|
// Update OpenSkill ratings
|
||||||
|
await updateOpenSkillRating(parseInt(team1P1Id), fromOpenSkillRating(newOpenSkillRatings[0][0]), team1Won, isTie);
|
||||||
|
await updateOpenSkillRating(parseInt(team1P2Id), fromOpenSkillRating(newOpenSkillRatings[0][1]), team1Won, isTie);
|
||||||
|
await updateOpenSkillRating(parseInt(team2P1Id), fromOpenSkillRating(newOpenSkillRatings[1][0]), team2Won, isTie);
|
||||||
|
await updateOpenSkillRating(parseInt(team2P2Id), fromOpenSkillRating(newOpenSkillRatings[1][1]), team2Won, isTie);
|
||||||
|
|
||||||
|
// Update Glicko2 ratings
|
||||||
|
await updateGlicko2Rating(parseInt(team1P1Id), glicko1.getRating(), glicko1.getRd(), glicko1.getVol(), team1Won, isTie);
|
||||||
|
await updateGlicko2Rating(parseInt(team1P2Id), glicko2.getRating(), glicko2.getRd(), glicko2.getVol(), team1Won, isTie);
|
||||||
|
await updateGlicko2Rating(parseInt(team2P1Id), glicko3.getRating(), glicko3.getRd(), glicko3.getVol(), team2Won, isTie);
|
||||||
|
await updateGlicko2Rating(parseInt(team2P2Id), glicko4.getRating(), glicko4.getRd(), glicko4.getVol(), team2Won, isTie);
|
||||||
|
|
||||||
// Update partnership stats
|
// Update partnership stats
|
||||||
await updatePartnershipStats(parseInt(team1P1Id), parseInt(team1P2Id), team1Won, player1EloChange, isTie);
|
await updatePartnershipStats(parseInt(team1P1Id), parseInt(team1P2Id), team1Won, player1EloChange, isTie);
|
||||||
await updatePartnershipStats(parseInt(team2P1Id), parseInt(team2P2Id), team2Won, player3EloChange, isTie);
|
await updatePartnershipStats(parseInt(team2P1Id), parseInt(team2P2Id), team2Won, player3EloChange, isTie);
|
||||||
@@ -203,3 +247,53 @@ async function updatePartnershipStats(player1Id: number, player2Id: number, won:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateOpenSkillRating(playerId: number, rating: number, won: boolean, isTie: boolean = false) {
|
||||||
|
await prisma.openSkillRating.upsert({
|
||||||
|
where: { playerId },
|
||||||
|
update: {
|
||||||
|
rating,
|
||||||
|
gamesPlayed: { increment: 1 },
|
||||||
|
wins: won ? { increment: 1 } : undefined,
|
||||||
|
losses: !won && !isTie ? { increment: 1 } : undefined,
|
||||||
|
draws: isTie ? { increment: 1 } : undefined,
|
||||||
|
lastUpdated: new Date(),
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
playerId,
|
||||||
|
rating,
|
||||||
|
gamesPlayed: 1,
|
||||||
|
wins: won ? 1 : 0,
|
||||||
|
losses: !won && !isTie ? 1 : 0,
|
||||||
|
draws: isTie ? 1 : 0,
|
||||||
|
lastUpdated: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateGlicko2Rating(playerId: number, rating: number, deviation: number, volatility: number, won: boolean, isTie: boolean = false) {
|
||||||
|
await prisma.glicko2Rating.upsert({
|
||||||
|
where: { playerId },
|
||||||
|
update: {
|
||||||
|
rating,
|
||||||
|
deviation,
|
||||||
|
volatility,
|
||||||
|
gamesPlayed: { increment: 1 },
|
||||||
|
wins: won ? { increment: 1 } : undefined,
|
||||||
|
losses: !won && !isTie ? { increment: 1 } : undefined,
|
||||||
|
draws: isTie ? { increment: 1 } : undefined,
|
||||||
|
lastUpdated: new Date(),
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
playerId,
|
||||||
|
rating,
|
||||||
|
deviation,
|
||||||
|
volatility,
|
||||||
|
gamesPlayed: 1,
|
||||||
|
wins: won ? 1 : 0,
|
||||||
|
losses: !won && !isTie ? 1 : 0,
|
||||||
|
draws: isTie ? 1 : 0,
|
||||||
|
lastUpdated: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user