Created
May 20, 2024 03:31
-
-
Save diogobruni/0ccde469a66bbaeabc4ec901be62a7fb to your computer and use it in GitHub Desktop.
BDO Matchmaker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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