From c16974fcfbb2cb43b05e1e9b106dbe3858888eff Mon Sep 17 00:00:00 2001 From: Valentin Kaelin Date: Sun, 21 Jun 2020 20:58:04 +0200 Subject: [PATCH] feat: use custom RoleIdentification if Riot roles are badly identified --- .../app/Services/RoleIdentificationService.js | 20 ++++-- .../app/Transformers/BasicMatchTransformer.js | 6 +- .../Transformers/DetailedMatchTransformer.js | 3 + .../app/Transformers/LiveMatchTransformer.js | 15 +---- server/app/Transformers/MatchTransformer.js | 64 ++++++++++++++++++- 5 files changed, 84 insertions(+), 24 deletions(-) diff --git a/server/app/Services/RoleIdentificationService.js b/server/app/Services/RoleIdentificationService.js index 2e8bd29..1829e38 100644 --- a/server/app/Services/RoleIdentificationService.js +++ b/server/app/Services/RoleIdentificationService.js @@ -4,7 +4,7 @@ const got = require('got') const Redis = use('Redis') class RoleIdentificationService { - _getPermutations (array) { + _getPermutations(array) { const result = [] for (let i = 0; i < array.length; i++) { @@ -21,13 +21,13 @@ class RoleIdentificationService { return result } - _calculateMetric (championPositions, bestPositions) { + _calculateMetric(championPositions, bestPositions) { return Object.entries(bestPositions).reduce((agg, [position, champion]) => { return agg + (championPositions[champion][position] || 0) }, 0) / Object.keys(bestPositions).length } - _getPositions (championPositions, composition, top, jungle, middle, adc, support) { + _getPositions(championPositions, composition, top, jungle, middle, adc, support) { // Set the initial guess to be the champion in the composition, order doesn't matter let bestPositions = { 'TOP': composition[0], @@ -104,7 +104,7 @@ class RoleIdentificationService { /** * Get the CDN data of the champion playrates by role */ - async pullData () { + async pullData() { const url = 'http://cdn.merakianalytics.com/riot/lol/resources/latest/en-US/championrates.json' // Check if cached @@ -144,13 +144,13 @@ class RoleIdentificationService { * @param composition * @param jungle */ - getRoles (championPositions, composition, jungle = null) { + getRoles(championPositions, composition, jungle = null) { const identified = {} let positions = {} let secondaryPositions = null let secondaryMetric = -Infinity - if(jungle) { + if (jungle) { identified['JUNGLE'] = jungle } @@ -193,7 +193,13 @@ class RoleIdentificationService { identified[best[0]] = best[1] } - return positions + // Rename UTILITY to SUPPORT + const { + UTILITY: SUPPORT, + ...rest + } = positions + + return { ...rest, SUPPORT } } } diff --git a/server/app/Transformers/BasicMatchTransformer.js b/server/app/Transformers/BasicMatchTransformer.js index d534d8a..c7ba604 100644 --- a/server/app/Transformers/BasicMatchTransformer.js +++ b/server/app/Transformers/BasicMatchTransformer.js @@ -1,6 +1,7 @@ 'use strict' const MatchTransformer = use('App/Transformers/MatchTransformer') +const { queuesWithRole } = use('App/helpers') /** * BasicMatchTransformer class @@ -65,8 +66,9 @@ class BasicMatchTransformer extends MatchTransformer { enemyTeam.push(playerInfos) } } - allyTeam.sort(this.sortTeamByRole) - enemyTeam.sort(this.sortTeamByRole) + + // Roles + super.getMatchRoles(match, allyTeam, enemyTeam, player.teamId, playerData) return { account_id: identity.player.currentAccountId, diff --git a/server/app/Transformers/DetailedMatchTransformer.js b/server/app/Transformers/DetailedMatchTransformer.js index 6735166..9a10317 100644 --- a/server/app/Transformers/DetailedMatchTransformer.js +++ b/server/app/Transformers/DetailedMatchTransformer.js @@ -23,6 +23,9 @@ class DetailedMatchTransformer extends MatchTransformer { const firstTeam = this.getTeamData(match, match.teams[0]) const secondTeam = this.getTeamData(match, match.teams[1]) + // Roles + super.getMatchRoles(match, firstTeam.players, secondTeam.players) + return { gameId: match.gameId, season: match.seasonId, diff --git a/server/app/Transformers/LiveMatchTransformer.js b/server/app/Transformers/LiveMatchTransformer.js index 7c13731..7f218cd 100644 --- a/server/app/Transformers/LiveMatchTransformer.js +++ b/server/app/Transformers/LiveMatchTransformer.js @@ -1,7 +1,6 @@ 'use strict' const MatchTransformer = use('App/Transformers/MatchTransformer') -const RoleIdentificationService = use('App/Services/RoleIdentificationService') const SummonerService = use('App/Services/SummonerService') const { queuesWithRole } = use('App/helpers') @@ -24,13 +23,6 @@ class LiveMatchTransformer extends MatchTransformer { return participant } - _getTeamRoles(team) { - const teamJunglers = team.filter(p => p.jungle) - const jungle = teamJunglers.length === 1 ? teamJunglers[0].champion : null - - return RoleIdentificationService.getRoles(this.championRoles, team.map(p => p.champion), jungle) - } - /** * Transform raw data from Riot API * @param match data from Riot API, one live match @@ -50,8 +42,8 @@ class LiveMatchTransformer extends MatchTransformer { p.teamId === 100 ? blueTeam.push(playerRole) : redTeam.push(playerRole) }) - blueRoles = this._getTeamRoles(blueTeam) - redRoles = this._getTeamRoles(redTeam) + blueRoles = super.getTeamRoles(blueTeam) + redRoles = super.getTeamRoles(redTeam) } for (const participant of match.participants) { @@ -62,9 +54,6 @@ class LiveMatchTransformer extends MatchTransformer { if (needsRole) { const roles = participant.teamId === 100 ? blueRoles : redRoles participant.role = Object.entries(roles).find(([, champion]) => participant.championId === champion)[0] - if (participant.role === 'UTILITY') { - participant.role = 'SUPPORT' - } } } diff --git a/server/app/Transformers/MatchTransformer.js b/server/app/Transformers/MatchTransformer.js index 02c5b10..9c4fe75 100644 --- a/server/app/Transformers/MatchTransformer.js +++ b/server/app/Transformers/MatchTransformer.js @@ -19,7 +19,7 @@ class MatchTransformer { const perks = await Jax.CDragon.perks() const perkstyles = await Jax.CDragon.perkstyles() const summonerSpells = await Jax.CDragon.summonerSpells() - const championRoles = await RoleIdentificationService.pullData().catch(() => {}) + const championRoles = await RoleIdentificationService.pullData().catch(() => { }) this.champions = champions this.items = items @@ -196,7 +196,7 @@ class MatchTransformer { * @param gamemode of the match to check if a role is needed */ getRoleName(timeline, gamemode) { - if(!queuesWithRole.includes(gamemode)) { + if (!queuesWithRole.includes(gamemode)) { return 'NONE' } @@ -207,6 +207,66 @@ class MatchTransformer { return timeline.lane } + /** + * Return the 5 roles of a team based on champions + * @param team 5 champions + smite from a team + */ + getTeamRoles(team) { + const teamJunglers = team.filter(p => p.jungle) + const jungle = teamJunglers.length === 1 ? teamJunglers[0].champion : null + + return RoleIdentificationService.getRoles(this.championRoles, team.map(p => p.champion), jungle) + } + + /** + * Update roles for a team if Riot's ones are badly identified + * @param {Object} team 5 players data of the team + * @param {Array} champs 5 champions + smite from the team + * @param {Object} playerData data of the searched player, only for basic matches + */ + updateTeamRoles(team, champs, playerData = null) { + const actualRoles = [...new Set(team.map(p => p.role))] + if (actualRoles.length === 5) { + return + } + + champs = this.getTeamRoles(champs) + for (const summoner of team) { + summoner.role = Object.entries(champs).find(([, champion]) => summoner.champion.id === champion)[0] + + if (playerData && summoner.champion.id === playerData.champion.id) { + playerData.role = summoner.role + } + } + } + + /** + * + * @param {Object} match from Riot Api + * @param {Array} allyTeam 5 players of the first team + * @param {Array} enemyTeam 5 players of the second team + * @param {Number} allyTeamId team id of the searched player, only for basic matches + * @param {Object} playerData data of the searched player, only for basic matches + */ + getMatchRoles(match, allyTeam, enemyTeam, allyTeamId = 100, playerData = null) { + if (!this.championRoles || !queuesWithRole.includes(match.queueId)) { + return + } + + let allyChamps = [] + let enemyChamps = [] + match.participants.map(p => { + const playerRole = { champion: p.championId, jungle: p.spell1Id === 11 || p.spell2Id === 11 } + p.teamId === allyTeamId ? allyChamps.push(playerRole) : enemyChamps.push(playerRole) + }) + + this.updateTeamRoles(allyTeam, allyChamps, playerData) + this.updateTeamRoles(enemyTeam, enemyChamps) + + allyTeam.sort(this.sortTeamByRole) + enemyTeam.sort(this.sortTeamByRole) + } + /** * Get Summoner Spell Data from CDragon * @param id of the summonerSpell