@@ -8,6 +8,10 @@ import type { teamResults } from 'psqlDB'
88import { setUserQueueRole } from 'utils/queueHelpers'
99import { clamp } from 'lodash-es'
1010
11+ // MMR formula constants
12+ const C_MMR_CHANGE = 25 // Base MMR change value
13+ const V_VARIANCE = 1200 // MMR variance
14+
1115// Function is from Owen, named Elowen, blame him if anything goes wrong
1216// - Jeff
1317function calculateRatingChange (
@@ -27,23 +31,26 @@ function calculateRatingChange(
2731 return gMultiplier * ( numerator / denominator )
2832}
2933
30- // Calculate predicted MMR changes for each team without updating the database
31- export async function calculatePredictedMMR (
32- queueId : number ,
34+ // Helper function to calculate team statistics and MMR changes
35+ async function calculateTeamStatsAndChanges (
3336 teamResults : teamResults ,
3437 winningTeamId : number ,
35- ) : Promise < Map < string , number > > {
36- const settings = await getQueueSettings ( queueId )
37-
38- // MMR formula constants
39- const C_MMR_CHANGE = 25
40- const V_VARIANCE = 1200
41-
38+ defaultElo : number ,
39+ ) : Promise < {
40+ teamStats : Array < {
41+ team : teamResults [ 'teams' ] [ 0 ]
42+ avgMMR : number
43+ avgVolatility : number
44+ isWinner : boolean
45+ } >
46+ ratingChange : number
47+ loserCount : number
48+ } > {
4249 // Calculate average MMR and volatility for each team
4350 const teamStats = teamResults . teams . map ( ( team ) => {
4451 const players = team . players
4552 const avgMMR =
46- players . reduce ( ( sum , p ) => sum + ( p . elo ?? settings . default_elo ) , 0 ) /
53+ players . reduce ( ( sum , p ) => sum + ( p . elo ?? defaultElo ) , 0 ) /
4754 players . length
4855 const avgVolatility =
4956 players . reduce ( ( sum , p ) => sum + ( p . volatility ?? 0 ) , 0 ) / players . length
@@ -60,7 +67,7 @@ export async function calculatePredictedMMR(
6067 const loserStats = teamStats . filter ( ( ts ) => ! ts . isWinner )
6168
6269 if ( ! winnerStats || loserStats . length === 0 ) {
63- return new Map ( )
70+ throw new Error ( 'Invalid team stats: no winner or losers found' )
6471 }
6572
6673 const avgLoserMMR =
@@ -80,21 +87,46 @@ export async function calculatePredictedMMR(
8087 globalAvgVolatility ,
8188 )
8289
83- // Build map of user_id -> predicted MMR change
84- const predictions = new Map < string , number > ( )
90+ return {
91+ teamStats,
92+ ratingChange,
93+ loserCount : loserStats . length ,
94+ }
95+ }
96+
97+ // Calculate predicted MMR changes for each team without updating the database
98+ export async function calculatePredictedMMR (
99+ queueId : number ,
100+ teamResults : teamResults ,
101+ winningTeamId : number ,
102+ ) : Promise < Map < string , number > > {
103+ const settings = await getQueueSettings ( queueId )
104+
105+ try {
106+ const { teamStats, ratingChange, loserCount } =
107+ await calculateTeamStatsAndChanges (
108+ teamResults ,
109+ winningTeamId ,
110+ settings . default_elo ,
111+ )
85112
86- for ( const ts of teamStats ) {
87- const isWinner = ts . isWinner
88- const mmrChange = isWinner
89- ? ratingChange
90- : - ratingChange / loserStats . length
113+ // Build map of user_id -> predicted MMR change
114+ const predictions = new Map < string , number > ( )
91115
92- for ( const player of ts . team . players ) {
93- predictions . set ( player . user_id , parseFloat ( mmrChange . toFixed ( 1 ) ) )
116+ for ( const ts of teamStats ) {
117+ const isWinner = ts . isWinner
118+ const mmrChange = isWinner ? ratingChange : - ratingChange / loserCount
119+
120+ for ( const player of ts . team . players ) {
121+ predictions . set ( player . user_id , parseFloat ( mmrChange . toFixed ( 1 ) ) )
122+ }
94123 }
95- }
96124
97- return predictions
125+ return predictions
126+ } catch ( err ) {
127+ console . error ( 'Error calculating predicted MMR:' , err )
128+ return new Map ( )
129+ }
98130}
99131
100132export async function calculateNewMMR (
@@ -106,84 +138,45 @@ export async function calculateNewMMR(
106138 const settings = await getQueueSettings ( matchData . queue_id )
107139 const winningTeamId = await getWinningTeamFromMatch ( matchId )
108140
109- // MMR formula constants
110- const C_MMR_CHANGE = 25 // Base MMR change value
111- const V_VARIANCE = 1200 // MMR variance
141+ try {
142+ const { teamStats, ratingChange, loserCount } =
143+ await calculateTeamStatsAndChanges (
144+ teamResults ,
145+ winningTeamId ?? 0 ,
146+ settings . default_elo ,
147+ )
112148
113- // Calculate average MMR and volatility for each team
114- const teamStats = teamResults . teams . map ( ( team ) => {
115- const players = team . players
116- const avgMMR =
117- players . reduce ( ( sum , p ) => sum + ( p . elo ?? settings . default_elo ) , 0 ) /
118- players . length
119- const avgVolatility =
120- players . reduce ( ( sum , p ) => sum + ( p . volatility ?? 0 ) , 0 ) / players . length
149+ // Apply changes to all teams and players
150+ for ( const ts of teamStats ) {
151+ const isWinner = ts . isWinner
152+ const mmrChange = isWinner ? ratingChange : - ratingChange / loserCount
121153
122- return {
123- team,
124- avgMMR,
125- avgVolatility,
126- isWinner : team . id === winningTeamId ,
127- }
128- } )
154+ for ( const player of ts . team . players ) {
155+ const oldMMR = player . elo ?? settings . default_elo
156+ const oldVolatility = player . volatility ?? 0
129157
130- // Find winner and calculate average MMR of all losing teams
131- const winnerStats = teamStats . find ( ( ts ) => ts . isWinner )
132- const loserStats = teamStats . filter ( ( ts ) => ! ts . isWinner )
158+ const newMMR = parseFloat ( ( oldMMR + mmrChange ) . toFixed ( 1 ) )
159+ const newVolatility = Math . min ( oldVolatility + 1 , 10 )
133160
134- if ( ! winnerStats || loserStats . length === 0 ) {
135- throw new Error ( 'Unable to determine winner and loser teams' )
136- }
161+ // Update database
162+ await updatePlayerMmrAll ( queueId , player . user_id , newMMR , newVolatility )
137163
138- // Average MMR and volatility of all losing teams
139- const avgLoserMMR =
140- loserStats . reduce ( ( sum , ts ) => sum + ts . avgMMR , 0 ) / loserStats . length
141- const avgLoserVolatility =
142- loserStats . reduce ( ( sum , ts ) => sum + ts . avgVolatility , 0 ) /
143- loserStats . length
164+ // Update teamResults object
165+ player . elo = clamp ( newMMR , 0 , 9999 )
166+ player . elo_change = parseFloat ( mmrChange . toFixed ( 1 ) )
167+ player . volatility = newVolatility
144168
145- // Use overall average volatility for g factor
146- const globalAvgVolatility =
147- ( winnerStats . avgVolatility + avgLoserVolatility ) / 2
169+ // Set user queue role
170+ await setUserQueueRole ( queueId , player . user_id )
171+ }
148172
149- // Calculate rating change using the formula
150- const ratingChange = calculateRatingChange (
151- C_MMR_CHANGE ,
152- V_VARIANCE ,
153- avgLoserMMR ,
154- winnerStats . avgMMR ,
155- globalAvgVolatility ,
156- )
157-
158- // Apply changes to all teams and players
159- for ( const ts of teamStats ) {
160- const isWinner = ts . isWinner
161- const mmrChange = isWinner
162- ? ratingChange
163- : - ratingChange / loserStats . length
164-
165- for ( const player of ts . team . players ) {
166- const oldMMR = player . elo ?? settings . default_elo
167- const oldVolatility = player . volatility ?? 0
168-
169- const newMMR = parseFloat ( ( oldMMR + mmrChange ) . toFixed ( 1 ) )
170- const newVolatility = Math . min ( oldVolatility + 1 , 10 )
171-
172- // Update database
173- await updatePlayerMmrAll ( queueId , player . user_id , newMMR , newVolatility )
174-
175- // Update teamResults object
176- player . elo = clamp ( newMMR , 0 , 9999 )
177- player . elo_change = parseFloat ( mmrChange . toFixed ( 1 ) )
178- player . volatility = newVolatility
179-
180- // Set user queue role
181- await setUserQueueRole ( queueId , player . user_id )
173+ // Set team score
174+ ts . team . score = isWinner ? 1 : 0
182175 }
183176
184- // Set team score
185- ts . team . score = isWinner ? 1 : 0
177+ return teamResults
178+ } catch ( err ) {
179+ console . error ( 'Error calculating new MMR:' , err )
180+ return teamResults
186181 }
187-
188- return teamResults
189182}
0 commit comments