Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 33 additions & 14 deletions src/utils/algorithms/calculateMMR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import {
getQueueSettings,
getUsersNeedingRoleUpdates,
updatePlayerMmrAll,
countPlayerGames,
getLeaderboardPosition,
} from '../queryDB'
import type { Matches, Queues, teamResults } from 'psqlDB'
import type { Queues, teamResults } from 'psqlDB'
import { setUserQueueRole } from 'utils/queueHelpers'
import { clamp } from 'lodash-es'

Expand Down Expand Up @@ -130,7 +132,6 @@ export async function calculatePredictedMMR(

export async function calculateNewMMR(
queueId: number,
matchData: Matches,
queueSettings: Queues,
teamResults: teamResults,
winningTeamId: number,
Expand All @@ -143,50 +144,68 @@ export async function calculateNewMMR(
queueSettings.default_elo,
)

const playerMMRChanges: Array<{ user_id: string; oldMMR: number; newMMR: number }> = []
const playerMMRChanges: Array<{
user_id: string
oldMMR: number
newMMR: number
oldRank: number | null
newRank: number | null
}> = []
const updatePromises: Promise<void>[] = []
let roleUpdateUsers: string[] = []

for (const ts of teamStats) {
const isWinner = ts.isWinner
const mmrChange = isWinner ? ratingChange : -ratingChange / loserCount

for (const player of ts.team.players) {
const oldMMR = player.elo ?? queueSettings.default_elo
const oldRank = await getLeaderboardPosition(queueId, player.user_id)
const oldVolatility = player.volatility ?? 0

const newMMR = parseFloat((oldMMR + mmrChange).toFixed(1))
const newVolatility = Math.min(oldVolatility + 1, 10)

playerMMRChanges.push({
user_id: player.user_id,
oldMMR,
newMMR,
})

player.elo = clamp(newMMR, 0, 9999)
player.elo_change = parseFloat(mmrChange.toFixed(1))
player.volatility = newVolatility

updatePromises.push(
updatePlayerMmrAll(queueId, player.user_id, newMMR, newVolatility),
)

const newRank = await getLeaderboardPosition(queueId, player.user_id)

playerMMRChanges.push({
user_id: player.user_id,
oldMMR,
newMMR,
oldRank,
newRank,
})

const gamesPlayed = await countPlayerGames(queueId, player.user_id)
if (gamesPlayed === 1) {
roleUpdateUsers.push(player.user_id)
}
}

ts.team.score = isWinner ? 1 : 0
}

await Promise.all(updatePromises)

const usersNeedingRoleUpdate = await getUsersNeedingRoleUpdates(
// Get users who need role updates due to MMR threshold changes
let usersNeedingRoleUpdate = await getUsersNeedingRoleUpdates(
queueId,
playerMMRChanges,
)

if (usersNeedingRoleUpdate.length > 0) {
roleUpdateUsers = roleUpdateUsers.concat(usersNeedingRoleUpdate).flat()

if (roleUpdateUsers.length > 0) {
await Promise.all(
usersNeedingRoleUpdate.map((userId) =>
setUserQueueRole(queueId, userId),
),
roleUpdateUsers.map((userId) => setUserQueueRole(queueId, userId)),
)
}

Expand Down
1 change: 0 additions & 1 deletion src/utils/matchHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,6 @@ export async function endMatch(

teamResults = await calculateNewMMR(
queueId,
matchData,
queueSettings,
teamResultsData,
winningTeamId,
Expand Down
59 changes: 55 additions & 4 deletions src/utils/queryDB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
} from 'psqlDB'
import { client, getGuild } from '../client'
import { QueryResult } from 'pg'
import { setUserQueueRole } from './queueHelpers'
import { endMatch } from './matchHelpers'

// Get the helper role
Expand Down Expand Up @@ -281,6 +280,23 @@ export async function getAllQueueRoles(
return res.rows
}

// Count completed games for a user in a queue
export async function countPlayerGames(
queueId: number,
userId: string,
): Promise<number> {
const res = await pool.query(
`
SELECT COUNT(CASE WHEN m.winning_team IS NOT NULL THEN 1 END)::integer as games_played
FROM match_users mu
JOIN matches m ON m.id = mu.match_id
WHERE mu.user_id = $1 AND m.queue_id = $2
`,
[userId, queueId],
)
return res.rows[0]?.games_played ?? 0
}

// get a users highest queue role
export async function getUserQueueRole(
queueId: number,
Expand All @@ -306,7 +322,13 @@ export async function getUserQueueRole(

export async function getUsersNeedingRoleUpdates(
queueId: number,
players: Array<{ user_id: string; oldMMR: number; newMMR: number }>,
players: Array<{
user_id: string
oldMMR: number
newMMR: number
oldRank: number | null
newRank: number | null
}>,
): Promise<string[]> {
if (players.length === 0) return []

Expand All @@ -317,6 +339,13 @@ export async function getUsersNeedingRoleUpdates(
[queueId],
)

const leaderboardRoles = await pool.query(
`SELECT leaderboard_min, leaderboard_max FROM queue_roles
WHERE queue_id = $1 AND mmr_threshold IS NULL
ORDER BY leaderboard_min DESC`,
[queueId],
)

const thresholds = roles.rows.map((r) => r.mmr_threshold)
const usersToUpdate: string[] = []

Expand All @@ -327,6 +356,26 @@ export async function getUsersNeedingRoleUpdates(
if (oldRole !== newRole) {
usersToUpdate.push(player.user_id)
}

// Also handle leaderboard positions
if (
player.oldRank !== null &&
player.newRank !== null &&
leaderboardRoles &&
leaderboardRoles.rowCount !== 0
) {
const oldLeaderboardRole = leaderboardRoles.rows.find(
(r) => r.leaderboard_min <= player.oldRank!,
)
const newLeaderboardRole = leaderboardRoles.rows.find(
(r) => r.leaderboard_min <= player.newRank!,
)

// Update leaderboard role if its not the same
if (oldLeaderboardRole !== newLeaderboardRole) {
usersToUpdate.push(player.user_id)
}
}
}

return usersToUpdate
Expand Down Expand Up @@ -363,13 +412,15 @@ export async function getLeaderboardQueueRole(
SELECT *
FROM queue_roles
WHERE queue_id = $1
AND leaderboard_min >= $2
AND leaderboard_max <= $2
AND leaderboard_min <= $2
AND leaderboard_max >= $2
LIMIT 1
`,
[queueId, rank],
)

console.log(roleRes.rowCount)

if (roleRes.rowCount === 0) return null
return roleRes.rows[0]
}
Expand Down