diff --git a/match_collector/champion_stat.ts b/match_collector/champion_stat.ts index 92e285f..36fdd1c 100644 --- a/match_collector/champion_stat.ts +++ b/match_collector/champion_stat.ts @@ -6,6 +6,7 @@ function sameArrays(array1 : Array, array2 : Array) { return true; } +import { MongoClient } from "mongodb"; import { ItemTree, treeInit, treeMerge, treeCutBranches, treeSort } from "./item_tree"; const itemDict = new Map() @@ -57,6 +58,12 @@ type LaneData = { runes: Array builds: Builds } +type ChampionData = { + champion: Champion + winningMatches: number + losingMatches: number + lanes: Array +} function handleParticipantRunes(participant, runes: Array) { const primaryStyle = participant.perks.styles[0].style @@ -161,60 +168,64 @@ function handleMatchItems(timeline, participant: any, participantIndex : number, } } -async function championInfos(client, patch: number, champion: Champion) { - const championId = champion.id +function handleMatch(match: any, champions : Map) { + let participantIndex = 0; + for(let participant of match.info.participants) { + participantIndex += 1 + const championId = participant.championId + const champion = champions.get(championId) + + // Lanes + let lane = champion.lanes.find((x) => x.data == participant.teamPosition) + if(lane == undefined) { + const builds : Builds = {tree:treeInit(), start: [], bootsFirst: 0, boots: [], lateGame: [], suppItems: []} + lane = {count:1, data: participant.teamPosition, runes:[], builds:builds, winningMatches: 0, losingMatches: 0, winrate: 0, pickrate: 0} + champion.lanes.push(lane) + } + else lane.count += 1 + + // Winrate + if(participant.win) { + champion.winningMatches++; + lane.winningMatches++; + } + else { + champion.losingMatches++; + lane.losingMatches++; + } + + // Runes + handleParticipantRunes(participant, lane.runes) + + // Items + handleMatchItems(match.timeline, participant, participantIndex, lane.builds) + } +} + +async function handleMatchList(client: MongoClient, patch: string, champions: Map) { const database = client.db("matches"); const matches = database.collection(patch) const allMatches = matches.find() - - let winningMatches = 0; - let losingMatches = 0; - let totalMatches = 0; - const lanes : Array = []; + const totalMatches: number = await matches.countDocuments() + + let currentMatch = 0; for await (let match of allMatches) { - totalMatches += 1; - let participantIndex = 0; - for(let participant of match.info.participants) { - participantIndex += 1 - if(participant.championId != championId) continue; - - // Winrate - if(participant.win) - winningMatches += 1; - else - losingMatches += 1; - - // Lanes - let lane = lanes.find((x) => x.data == participant.teamPosition) - if(lane == undefined) { - const builds : Builds = {tree:treeInit(), start: [], bootsFirst: 0, boots: [], lateGame: [], suppItems: []} - lane = {count:1, data: participant.teamPosition, runes:[], builds:builds, winningMatches: 0, losingMatches: 0, winrate: 0, pickrate: 0} - lanes.push(lane) - } - else lane.count += 1 - - if(participant.win) - lane.winningMatches += 1; - else - lane.losingMatches += 1; - - // Runes - handleParticipantRunes(participant, lane.runes) - - // Items - handleMatchItems(match.timeline, participant, participantIndex, lane.builds) - - break; - } + console.log("Computing champion stats, game entry " + currentMatch + "/" + totalMatches + " ...") + currentMatch += 1; + handleMatch(match, champions) } + + return totalMatches +} - let totalChampionMatches = winningMatches + losingMatches; +async function finalizeChampionStats(champion: ChampionData, totalMatches: number) { + let totalChampionMatches = champion.winningMatches + champion.losingMatches; - arrayRemovePercentage(lanes, totalChampionMatches, 0.2) - lanes.sort((a, b) => b.count - a.count) + arrayRemovePercentage(champion.lanes, totalChampionMatches, 0.2) + champion.lanes.sort((a, b) => b.count - a.count) // Filter runes to keep 3 most played - for(let lane of lanes) { + for(let lane of champion.lanes) { const runes = lane.runes runes.sort((a, b) => b.count - a.count) @@ -225,7 +236,7 @@ async function championInfos(client, patch: number, champion: Champion) { rune.pickrate = rune.count / lane.count; } - for(let lane of lanes) { + for(let lane of champion.lanes) { const builds = lane.builds // Cut item tree branches to keep only 4 branches every time and with percentage threshold @@ -257,35 +268,28 @@ async function championInfos(client, patch: number, champion: Champion) { builds.lateGame.sort((a, b) => b.count - a.count) } - for(let lane of lanes) { + for(let lane of champion.lanes) { lane.winrate = lane.winningMatches / lane.count lane.pickrate = lane.count / totalMatches } - return {name: champion.name, - alias: champion.alias.toLowerCase(), - id: championId, - lanes: lanes, - winrate: winningMatches / totalChampionMatches, + return {name: champion.champion.name, + alias: champion.champion.alias.toLowerCase(), + id: champion.champion.id, + lanes: champion.lanes, + winrate: champion.winningMatches / totalChampionMatches, gameCount: totalChampionMatches, pickrate: totalChampionMatches/totalMatches, }; } -async function makeChampionStat(client, patch : number, champion : Champion) { - const championInfo = await championInfos(client, patch, champion) - const database = client.db("champions") - const collection = database.collection(patch) - await collection.updateOne({id: championInfo.id}, {$set: championInfo}, { upsert: true }) -} - async function championList() { const response = await fetch("https://raw.communitydragon.org/latest/plugins/rcp-be-lol-game-data/global/default/v1/champion-summary.json"); const list = await response.json() return list.slice(1) } -async function makeChampionsStats(client, patch : number) { +async function makeChampionsStats(client: MongoClient, patch : string) { var globalItems = await itemList() for(let item of globalItems) { itemDict.set(item.id, item) @@ -293,15 +297,30 @@ async function makeChampionsStats(client, patch : number) { const list = await championList() console.log("Generating stats for " + list.length + " champions") - let i = 0; + + // Pre-generate list of champions + const champions: Map = new Map() for(let champion of list) { - console.log("Entry " + i + "/" + list.length + " (" + champion.name + ")...") - await makeChampionStat(client, patch, champion) - i += 1 + champions.set(champion.id, { + champion: {id: champion.id, name: champion.name, alias: champion.alias}, + winningMatches: 0, + losingMatches: 0, + lanes: [] + }) } + // Loop through all matches to generate stats + const totalMatches = await handleMatchList(client, patch, champions) + + // Finalize and save stats for every champion const database = client.db("champions") const collection = database.collection(patch) + for(let champion of list) { + const championInfo = await finalizeChampionStats(champions.get(champion.id), totalMatches) + await collection.updateOne({id: champion.id}, {$set: championInfo}, { upsert: true }) + } + + // Create alias-index for better key-find await collection.createIndex({alias:1}) }