Skip to content

Instantly share code, notes, and snippets.

@diogobruni
Created May 20, 2024 03:31
Show Gist options
  • Save diogobruni/0ccde469a66bbaeabc4ec901be62a7fb to your computer and use it in GitHub Desktop.
Save diogobruni/0ccde469a66bbaeabc4ec901be62a7fb to your computer and use it in GitHub Desktop.
BDO Matchmaker
import { grabbers, blockers, tankers } from '../data/criterios'
export const make_a_match = (queue, { teamSize = 3, mmrDiff = 25, maxMMRDiff = 500 } = {}) => {
if (queue.length < teamSize * 2) {
throw new Error('Not enough players to make a match')
}
const sortedQueue = queue.sort((a, b) => b.mmr - a.mmr)
if (mmrDiff > maxMMRDiff) {
queue = queue.filter(p => p.character !== sortedQueue[0].character && p.mmr !== sortedQueue[0].mmr)
return make_a_match(queue, { teamSize, mmrDiff: 25, maxMMRDiff })
}
const topMMR = sortedQueue[0].mmr
const filteredQueue = sortedQueue.filter(p => p.mmr >= (topMMR - mmrDiff))
if (filteredQueue.length < teamSize * 2) {
return make_a_match(queue, { teamSize, mmrDiff: mmrDiff + 25, maxMMRDiff })
}
let bestMatch = null
let bestMatchMMRDiff = 0
for (let i = 0; i < Math.pow(filteredQueue.length, 3); i++) {
const shuffledQueue = filteredQueue.sort(() => Math.random() - 0.5)
const team1 = shuffledQueue.slice(0, 3)
const team2 = shuffledQueue.slice(3, 6)
const isValidTeams = validateTeams(team1, team2)
if (!isValidTeams) continue
const mmrTeam1 = team1.reduce((acc, p) => acc + p.mmr, 0)
const mmrTeam2 = team2.reduce((acc, p) => acc + p.mmr, 0)
const matchMMRDiff = Math.abs(mmrTeam1 - mmrTeam2)
if (bestMatch === null || matchMMRDiff < bestMatchMMRDiff) {
bestMatch = [team1, team2]
bestMatchMMRDiff = matchMMRDiff
}
}
if (bestMatch === null) return make_a_match(queue, { teamSize, mmrDiff: mmrDiff + 25, maxMMRDiff })
queue = queue.filter(p => !bestMatch.flat().includes(p))
const playerHigherMMR = Math.max(...bestMatch[0].map(p => p.mmr), ...bestMatch[1].map(p => p.mmr))
const playerLowerMMR = Math.min(...bestMatch[0].map(p => p.mmr), ...bestMatch[1].map(p => p.mmr))
return {
queue,
match: bestMatch,
stats: {
mmrSumTeamA: bestMatch[0].reduce((acc, p) => acc + p.mmr, 0),
mmrSumTeamB: bestMatch[1].reduce((acc, p) => acc + p.mmr, 0),
mmrAvgTeamA: bestMatch[0].reduce((acc, p) => acc + p.mmr, 0) / teamSize,
mmrAvgTeamB: bestMatch[1].reduce((acc, p) => acc + p.mmr, 0) / teamSize,
matchMMRDiff: bestMatchMMRDiff,
playerHigherMMR,
playerLowerMMR,
playerMMRDiff: playerHigherMMR - playerLowerMMR
},
functionParams: {
mmrDiff,
maxMMRDiff
}
}
}
const validateTeams = (teamA, teamB) => {
// Avoid repeated characters on Team A
const teamADifferentCharacters = teamA.map(p => p.character).filter((v, i, a) => a.indexOf(v) === i)
if (teamADifferentCharacters.length !== teamA.length) return false
// Avoid repeated characters on Team B
const teamBDifferentCharacters = teamB.map(p => p.character).filter((v, i, a) => a.indexOf(v) === i)
if (teamBDifferentCharacters.length !== teamB.length) return false
// Count grabbers, blockers and tankers on Team A
const [grabbersA, blockersA, tankersA] = teamA.reduce(
([countGrabbers, countBlockers, countTankers], player) => {
if (grabbers.includes(player.character)) countGrabbers++
if (blockers.includes(player.character)) countBlockers++
if (tankers.includes(player.character)) countTankers++
return [countGrabbers, countBlockers, countTankers]
},
[0, 0, 0]
)
// Count grabbers, blockers and tankers on Team A
const [grabbersB, blockersB, tankersB] = teamB.reduce(
([countGrabbers, countBlockers, countTankers], player) => {
if (grabbers.includes(player.character)) countGrabbers++
if (blockers.includes(player.character)) countBlockers++
if (tankers.includes(player.character)) countTankers++
return [countGrabbers, countBlockers, countTankers]
},
[0, 0, 0]
)
// Avoid teams with no grab unless both teams have no grabbers
if (grabbersA !== grabbersB && (grabbersA < 1 || grabbersB < 1)) return false
// Limited to 1 blocker per team or same number of blockers on both teams
// if (blockersA !== blockersB && (blockersA > 1 || blockersB > 1)) return false
if (Math.max(blockersA, blockersB) - Math.min(blockersA, blockersB) > 1) return false
// // Limited to 1 tanker per team or same number of tankers on both teams
// if (tankersA !== tankersB && (tankersA > 1 || tankersB > 1)) return false
if (Math.max(tankersA, tankersB) - Math.min(tankersA, tankersB) > 1) return false
return true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment