diff --git a/dev/scripts/setup-db.js b/dev/scripts/setup-db.js index 50747df..c8bcf68 100644 --- a/dev/scripts/setup-db.js +++ b/dev/scripts/setup-db.js @@ -32,11 +32,28 @@ async function setupDatabase() { console.log(`🎯 Latest patch version: ${latestPatch}`); // Check if data directory exists and has files + // Support both old format (patch_matches.json) and new platform-specific format (patch_PLATFORM_matches.json) console.log('🔍 Checking for data files...'); + const platforms = ['EUW1', 'EUN1', 'NA1', 'KR']; const dataFiles = [ - { path: 'patches.json', required: true, description: 'Patches data' }, - { path: `${latestPatch}_matches.json`, required: true, description: 'Match data' } + { path: 'patches.json', required: true, description: 'Patches data' } ]; + + // Check for platform-specific match files + let foundPlatformFiles = []; + for (const platform of platforms) { + const platformFile = `${latestPatch}_${platform}_matches.json`; + const fullPath = path.join(dataDir, platformFile); + if (fs.existsSync(fullPath)) { + foundPlatformFiles.push(platform); + dataFiles.push({ path: platformFile, required: false, description: `Match data for ${platform}` }); + } + } + + // If no platform-specific files found, look for old format + if (foundPlatformFiles.length === 0) { + dataFiles.push({ path: `${latestPatch}_matches.json`, required: true, description: 'Match data' }); + } let filesExist = true; for (const file of dataFiles) { @@ -80,14 +97,36 @@ async function setupDatabase() { // 6. Check existing matches count and import if needed console.log('Checking existing matches count...'); - const matchCount = await getMatchCount(latestPatch); - console.log(`📊 Current matches in database: ${matchCount}`); - if (matchCount < 100) { - console.log('📥 Importing matches (this may take a while)...'); - await importMatchesData(latestPatch); + // Check for platform-specific collections or fall back to old format + const existingPlatforms = await getExistingPlatforms(latestPatch); + + if (existingPlatforms.length > 0) { + console.log(`📊 Found platform-specific collections: ${existingPlatforms.join(', ')}`); + let totalMatches = 0; + for (const platform of existingPlatforms) { + const count = await getMatchCount(latestPatch, platform); + console.log(` ${platform}: ${count} matches`); + totalMatches += count; + } + console.log(`📊 Total matches in database: ${totalMatches}`); + + if (totalMatches < 100) { + console.log('📥 Importing matches (this may take a while)...'); + await importMatchesData(latestPatch, foundPlatformFiles); + } else { + console.log('✅ Skipping matches import - sufficient data already present'); + } } else { - console.log('✅ Skipping matches import - sufficient data already present'); + const matchCount = await getMatchCount(latestPatch); + console.log(`📊 Current matches in database: ${matchCount}`); + + if (matchCount < 100) { + console.log('📥 Importing matches (this may take a while)...'); + await importMatchesData(latestPatch, foundPlatformFiles); + } else { + console.log('✅ Skipping matches import - sufficient data already present'); + } } // 7. Fetch CDragon data for the current patch @@ -277,19 +316,42 @@ async function importPatchesData() { } } -async function importMatchesData(patchVersion) { - const matchesFile = path.join(__dirname, '../data', `${patchVersion}_matches.json`); - const collectionName = patchVersion; - +async function importMatchesData(patchVersion, foundPlatformFiles = []) { + const dataDir = path.join(__dirname, '../data'); + try { - const result = execSync( - `node ${path.join(__dirname, 'process-matches.js')} ${matchesFile} ${collectionName} 1000`, - { - stdio: 'inherit', - env: { ...process.env, MONGO_URI: getMongoUri() } + // If platform-specific files were found, import each one + if (foundPlatformFiles.length > 0) { + for (const platform of foundPlatformFiles) { + const matchesFile = path.join(dataDir, `${patchVersion}_${platform}_matches.json`); + const collectionName = `${patchVersion}_${platform}`; + + if (fs.existsSync(matchesFile)) { + console.log(`📥 Importing matches for ${platform}...`); + execSync( + `node ${path.join(__dirname, 'process-matches.js')} ${matchesFile} ${collectionName} 1000`, + { + stdio: 'inherit', + env: { ...process.env, MONGO_URI: getMongoUri() } + } + ); + console.log(`✅ Matches import completed for ${platform}`); + } } - ); - console.log('✅ Matches import completed'); + } else { + // Fall back to old format (single file without platform suffix) + const matchesFile = path.join(dataDir, `${patchVersion}_matches.json`); + const collectionName = patchVersion; + + execSync( + `node ${path.join(__dirname, 'process-matches.js')} ${matchesFile} ${collectionName} 1000`, + { + stdio: 'inherit', + env: { ...process.env, MONGO_URI: getMongoUri() } + } + ); + console.log('✅ Matches import completed'); + } } catch (error) { console.error('❌ Failed to import matches:', error); throw error; @@ -344,13 +406,14 @@ async function fetchCDragonData() { } } -async function getMatchCount(patchVersion) { +async function getMatchCount(patchVersion, platform = null) { const client = new MongoClient(getMongoUri()); await client.connect(); try { const db = client.db('matches'); - const collection = db.collection(patchVersion); + const collectionName = platform ? `${patchVersion}_${platform}` : patchVersion; + const collection = db.collection(collectionName); const count = await collection.countDocuments(); return count; } catch (error) { @@ -361,6 +424,33 @@ async function getMatchCount(patchVersion) { } } +async function getExistingPlatforms(patchVersion) { + const client = new MongoClient(getMongoUri()); + await client.connect(); + + try { + const db = client.db('matches'); + const collections = await db.listCollections().toArray(); + const collectionNames = collections.map(c => c.name); + + const platforms = ['EUW1', 'EUN1', 'NA1', 'KR']; + const existingPlatforms = []; + + for (const platform of platforms) { + if (collectionNames.includes(`${patchVersion}_${platform}`)) { + existingPlatforms.push(platform); + } + } + + return existingPlatforms; + } catch (error) { + console.error('❌ Failed to get existing platforms:', error); + return []; + } finally { + await client.close(); + } +} + function getMongoUri() { return process.env.MONGO_URI || 'mongodb://root:password@localhost:27017/buildpath?authSource=admin'; } diff --git a/frontend/components/nav/SideBar.vue b/frontend/components/nav/SideBar.vue index c8e6d2c..dfaa51d 100644 --- a/frontend/components/nav/SideBar.vue +++ b/frontend/components/nav/SideBar.vue @@ -137,7 +137,10 @@ if (route.path.startsWith('/tierlist/')) {

Loading stats...

- EUW Challenger only + Challenger only +

+

+ EUW/EUNE/NA/KR

About

diff --git a/frontend/server/api/stats.ts b/frontend/server/api/stats.ts index b41ef36..371bdd6 100644 --- a/frontend/server/api/stats.ts +++ b/frontend/server/api/stats.ts @@ -1,19 +1,43 @@ import type { MongoClient } from 'mongodb' -import { connectToDatabase, fetchLatestPatch } from '../utils/mongo' +import { connectToDatabase, fetchLatestPatch, getAvailablePlatforms } from '../utils/mongo' async function fetchGameCount(client: MongoClient, patch: string) { const database = client.db('matches') + + // Check for platform-specific collections + const platforms = await getAvailablePlatforms(client, patch) + + if (platforms.length > 0) { + // Sum counts from all platform-specific collections + let totalCount = 0 + const platformCounts: Record = {} + + for (const platform of platforms) { + const collection = database.collection(`${patch}_${platform}`) + const count = await collection.countDocuments() + platformCounts[platform] = count + totalCount += count + } + + return { total: totalCount, platforms: platformCounts } + } + + // Fall back to old format (single collection) const matches = database.collection(patch) const count = await matches.countDocuments() - return count + return { total: count, platforms: {} } } export default defineEventHandler(async _ => { const client = await connectToDatabase() const latestPatch = await fetchLatestPatch(client) - const gameCount = await fetchGameCount(client, latestPatch) + const gameCountData = await fetchGameCount(client, latestPatch) await client.close() - return { patch: latestPatch, count: gameCount } + return { + patch: latestPatch, + count: gameCountData.total, + platformCounts: gameCountData.platforms + } }) diff --git a/frontend/server/utils/mongo.ts b/frontend/server/utils/mongo.ts index 887b47e..c715eea 100644 --- a/frontend/server/utils/mongo.ts +++ b/frontend/server/utils/mongo.ts @@ -1,5 +1,9 @@ import { MongoClient } from 'mongodb' +// Available platforms for region-specific match data +const PLATFORMS = ['EUW1', 'EUN1', 'NA1', 'KR'] as const +type Platform = (typeof PLATFORMS)[number] + async function connectToDatabase() { // Create a MongoClient with a MongoClientOptions object to set the Stable API version let uri = `mongodb://${process.env.MONGO_USER}:${process.env.MONGO_PASS}@${process.env.MONGO_HOST}` @@ -22,4 +26,25 @@ async function fetchLatestPatch(client: MongoClient) { return latestPatch!.patch as string } -export { connectToDatabase, fetchLatestPatch } +/** + * Get available platforms for a given patch by checking which match collections exist + * Note: Match collections are platform-specific (e.g., "15.1_EUW1") + * Champion collections are aggregated across all platforms (e.g., "15.1") + */ +async function getAvailablePlatforms(client: MongoClient, patch: string): Promise { + const matchesDb = client.db('matches') + const collections = await matchesDb.listCollections().toArray() + const collectionNames = collections.map(c => c.name) + + const availablePlatforms: Platform[] = [] + for (const platform of PLATFORMS) { + if (collectionNames.includes(`${patch}_${platform}`)) { + availablePlatforms.push(platform) + } + } + + return availablePlatforms +} + +export { connectToDatabase, fetchLatestPatch, getAvailablePlatforms, PLATFORMS } +export type { Platform } diff --git a/match_collector/champion_stat.ts b/match_collector/champion_stat.ts index a26645f..fac2a39 100644 --- a/match_collector/champion_stat.ts +++ b/match_collector/champion_stat.ts @@ -218,7 +218,8 @@ function handleMatchBuilds( match: Match, participant: Participant, participantIndex: number, - builds: Builds + builds: Builds, + platform?: string ) { const timeline: Timeline = match.timeline @@ -226,7 +227,7 @@ function handleMatchBuilds( const build = findOrCreateBuild(builds, participant) build.count += 1 - const items: Array<{ itemId: number; goldAdvantage: GoldAdvantageTag }> = [] + const items: Array<{ itemId: number; goldAdvantage: GoldAdvantageTag; platform?: string }> = [] for (const frame of timeline.info.frames) { for (const event of frame.events) { if (event.participantId != participantIndex) continue @@ -301,7 +302,7 @@ function handleMatchBuilds( // Calculate gold advantage at time of purchase const goldAdvantage = calculateGoldAdvantage(match, frame, participantIndex) - items.push({ itemId: event.itemId, goldAdvantage }) + items.push({ itemId: event.itemId, goldAdvantage, platform }) } } @@ -312,7 +313,7 @@ function handleMatchBuilds( } } -function handleMatch(match: Match, champions: Map) { +function handleMatch(match: Match, champions: Map, platform?: string) { let participantIndex = 0 for (const participant of match.info.participants) { participantIndex += 1 @@ -395,17 +396,19 @@ function handleMatch(match: Match, champions: Map) { } // Items and runes (builds) - handleMatchBuilds(match, participant, participantIndex, lane.builds) + handleMatchBuilds(match, participant, participantIndex, lane.builds, platform) } } async function handleMatchList( client: MongoClient, patch: string, - champions: Map + champions: Map, + platform?: string ) { const database = client.db('matches') - const matches = database.collection(patch) + const collectionName = platform ? `${patch}_${platform}` : patch + const matches = database.collection(collectionName) const allMatches = matches.find() const totalMatches: number = await matches.countDocuments() @@ -415,7 +418,7 @@ async function handleMatchList( '\rComputing champion stats, game entry ' + currentMatch + '/' + totalMatches + ' ... ' ) currentMatch += 1 - handleMatch(match as unknown as Match, champions) + handleMatch(match as unknown as Match, champions, platform) } return totalMatches @@ -696,7 +699,7 @@ async function championList() { return list.slice(1) } -async function makeChampionsStats(client: MongoClient, patch: string) { +async function makeChampionsStats(client: MongoClient, patch: string, platforms: string[] = []) { const globalItems = await itemList() for (const item of globalItems) { itemDict.set(item.id, item) @@ -705,7 +708,7 @@ async function makeChampionsStats(client: MongoClient, patch: string) { const list = await championList() console.log('Generating stats for ' + list.length + ' champions') - // Pre-generate list of champions + // Pre-generate list of champions (shared across all platforms) const champions: Map = new Map() for (const champion of list) { champions.set(champion.id, { @@ -716,10 +719,18 @@ async function makeChampionsStats(client: MongoClient, patch: string) { }) } - // Loop through all matches to generate stats - const totalMatches = await handleMatchList(client, patch, champions) + // Process matches from all platforms, merging into the same champions map + let totalMatches = 0 + for (const platform of platforms) { + console.log(`\n=== Processing matches from platform: ${platform} ===`) + const platformMatches = await handleMatchList(client, patch, champions, platform) + totalMatches += platformMatches + console.log(`Processed ${platformMatches} matches from ${platform}`) + } - // Finalize and save stats for every champion + console.log(`\n=== Total matches processed: ${totalMatches} ===`) + + // Finalize and save stats to a single champions collection const database = client.db('champions') const collection = database.collection(patch) for (const champion of list) { @@ -729,6 +740,7 @@ async function makeChampionsStats(client: MongoClient, patch: string) { // Create alias-index for better key-find await collection.createIndex({ alias: 1 }) + console.log(`Stats saved to collection: ${patch}`) } export default { makeChampionsStats } diff --git a/match_collector/index.ts b/match_collector/index.ts index c3839ff..3ca90b2 100644 --- a/match_collector/index.ts +++ b/match_collector/index.ts @@ -1,4 +1,3 @@ -const base = 'https://euw1.api.riotgames.com' const api_key = process.env.RIOT_API_KEY const sleep_minutes = 12 @@ -7,6 +6,22 @@ import { MongoClient } from 'mongodb' import champion_stat from './champion_stat' import { Match } from './api' +// Region configuration: platform -> regional routing value +const PLATFORMS: Record = { + EUW1: 'EUROPE', + EUN1: 'EUROPE', + NA1: 'AMERICAS', + KR: 'ASIA' +} + +function getPlatformBaseUrl(platform: string): string { + return `https://${platform.toLowerCase()}.api.riotgames.com` +} + +function getRegionalBaseUrl(region: string): string { + return `https://${region.toLowerCase()}.api.riotgames.com` +} + main() async function main() { @@ -25,42 +40,51 @@ async function main() { 'Connected to database, latest patch ' + latestPatch + ' was epoch: ' + latestPatchTime ) - const alreadySeenGameList = await alreadySeenGames(client, latestPatch) - console.log('We already have ' + alreadySeenGameList.length + ' matches for this patch !') - console.log('Using RIOT_API_KEY: ' + api_key) if (api_key != null && api_key != undefined && api_key != '') { - const challengerLeague = await fetchChallengerLeague() - console.log('ChallengerLeague: got ' + challengerLeague.entries.length + ' entries') + // Iterate through all platforms + for (const [platform, region] of Object.entries(PLATFORMS)) { + console.log(`\n=== Processing platform: ${platform} (region: ${region}) ===`) - const gameList : string[] = [] - let i = 0 - for (const challenger of challengerLeague.entries) { - console.log('Entry ' + i + '/' + challengerLeague.entries.length + ' ...') - const puuid = challenger.puuid - const challengerGameList = await summonerGameList(puuid, latestPatchTime) - for (const game of challengerGameList) { - if (!gameList.includes(game) && !alreadySeenGameList.includes(game)) { - gameList.push(game) + const alreadySeenGameList = await alreadySeenGames(client, latestPatch, platform) + console.log( + 'We already have ' + alreadySeenGameList.length + ' matches for this patch/platform !' + ) + + const challengerLeague = await fetchChallengerLeague(platform) + console.log('ChallengerLeague: got ' + challengerLeague.entries.length + ' entries') + + const gameList: string[] = [] + let i = 0 + for (const challenger of challengerLeague.entries) { + console.log('Entry ' + i + '/' + challengerLeague.entries.length + ' ...') + const puuid = challenger.puuid + const challengerGameList = await summonerGameList(puuid, latestPatchTime, region) + for (const game of challengerGameList) { + if (!gameList.includes(game) && !alreadySeenGameList.includes(game)) { + gameList.push(game) + } } + i++ } - i++ - } - console.log('Games: got ' + gameList.length + ' entries') - i = 0 - for (const game of gameList) { - console.log('Entry ' + i + '/' + gameList.length + ' ...') - const gameMatch = await match(game) - const gameTimeline = await matchTimeline(game) - gameMatch.timeline = gameTimeline - await saveMatch(client, gameMatch, latestPatch) - i++ + console.log('Games: got ' + gameList.length + ' entries for ' + platform) + i = 0 + for (const game of gameList) { + console.log('Entry ' + i + '/' + gameList.length + ' ...') + // Determine region from matchId (format: REGION_matchId) + const matchRegion = game.split('_')[0] + const gameMatch = await match(game, matchRegion) + const gameTimeline = await matchTimeline(game, matchRegion) + gameMatch.timeline = gameTimeline + await saveMatch(client, gameMatch, latestPatch, platform) + i++ + } } } console.log('Generating stats...') - await champion_stat.makeChampionsStats(client, latestPatch) + await champion_stat.makeChampionsStats(client, latestPatch, Object.keys(PLATFORMS)) console.log('All done. Closing client.') await client.close() @@ -113,10 +137,11 @@ async function fetchLatestPatchDate(client: MongoClient) { return [latestPatch!.patch, Math.floor(latestPatch!.date.valueOf() / 1000)] } -async function fetchChallengerLeague() { +async function fetchChallengerLeague(platform: string) { const queue = 'RANKED_SOLO_5x5' const endpoint = `/lol/league/v4/challengerleagues/by-queue/${queue}` - const url = `${base}${endpoint}?api_key=${api_key}` + const baseUrl = getPlatformBaseUrl(platform) + const url = `${baseUrl}${endpoint}?api_key=${api_key}` const challengerLeagueResponse = await handleRateLimit(new URL(url)) @@ -126,10 +151,10 @@ async function fetchChallengerLeague() { return challengerLeague } -async function summonerGameList(puuid: string, startTime: string) { - const base = 'https://europe.api.riotgames.com' +async function summonerGameList(puuid: string, startTime: string, region: string) { + const baseUrl = getRegionalBaseUrl(region) const endpoint = `/lol/match/v5/matches/by-puuid/${puuid}/ids` - const url = `${base}${endpoint}?queue=420&type=ranked&startTime=${startTime}&api_key=${api_key}` + const url = `${baseUrl}${endpoint}?queue=420&type=ranked&startTime=${startTime}&api_key=${api_key}` const gameListResponse = await handleRateLimit(new URL(url)) handleError(gameListResponse) @@ -138,10 +163,10 @@ async function summonerGameList(puuid: string, startTime: string) { return gameList } -async function match(matchId: string) { - const base = 'https://europe.api.riotgames.com' +async function match(matchId: string, region: string) { + const baseUrl = getRegionalBaseUrl(region) const endpoint = `/lol/match/v5/matches/${matchId}` - const url = `${base}${endpoint}?api_key=${api_key}` + const url = `${baseUrl}${endpoint}?api_key=${api_key}` const matchResponse = await handleRateLimit(new URL(url)) handleError(matchResponse) @@ -150,10 +175,10 @@ async function match(matchId: string) { return match } -async function matchTimeline(matchId: string) { - const base = 'https://europe.api.riotgames.com' +async function matchTimeline(matchId: string, region: string) { + const baseUrl = getRegionalBaseUrl(region) const endpoint = `/lol/match/v5/matches/${matchId}/timeline` - const url = `${base}${endpoint}?api_key=${api_key}` + const url = `${baseUrl}${endpoint}?api_key=${api_key}` const timelineResponse = await handleRateLimit(new URL(url)) handleError(timelineResponse) @@ -162,17 +187,19 @@ async function matchTimeline(matchId: string) { return timeline } -async function alreadySeenGames(client: MongoClient, latestPatch: string) { +async function alreadySeenGames(client: MongoClient, latestPatch: string, platform: string) { const database = client.db('matches') - const matches = database.collection(latestPatch) + const collectionName = `${latestPatch}_${platform}` + const matches = database.collection(collectionName) const alreadySeen = await matches.distinct('metadata.matchId') return alreadySeen } -async function saveMatch(client: MongoClient, match: Match, latestPatch: string) { +async function saveMatch(client: MongoClient, match: Match, latestPatch: string, platform: string) { const database = client.db('matches') - const matches = database.collection(latestPatch) + const collectionName = `${latestPatch}_${platform}` + const matches = database.collection(collectionName) await matches.insertOne(match) } @@ -187,10 +214,15 @@ async function runWithPreloadedData() { const [latestPatch] = await fetchLatestPatchDate(client) console.log(`Latest patch: ${latestPatch}`) - // Check if we have matches for this patch + // Check if we have matches for this patch (including platform-specific collections) const matchesDb = client.db('matches') const collections = await matchesDb.listCollections().toArray() - const patchCollections = collections.map(c => c.name).filter(name => name === latestPatch) + const collectionNames = collections.map(c => c.name) + + // Find collections for this patch (both global and platform-specific) + const patchCollections = collectionNames.filter( + name => name === latestPatch || name.startsWith(`${latestPatch}_`) + ) if (patchCollections.length === 0) { console.error(`❌ No match data found for patch ${latestPatch}`) @@ -199,13 +231,21 @@ async function runWithPreloadedData() { return } - console.log(`Found ${patchCollections.length} match collection(s)`) + console.log( + `Found ${patchCollections.length} match collection(s): ${patchCollections.join(', ')}` + ) - // Generate stats for each patch with data - for (const patch of patchCollections) { - console.log(`Generating stats for patch ${patch}...`) - await champion_stat.makeChampionsStats(client, patch) - console.log(`Stats generated for patch ${patch}`) + // Extract platforms from collection names (e.g., "15.1_EUW1" -> "EUW1") + const platforms = patchCollections + .filter(name => name.startsWith(`${latestPatch}_`)) + .map(name => name.replace(`${latestPatch}_`, '')) + + // Generate stats for each platform + if (platforms.length > 0) { + await champion_stat.makeChampionsStats(client, latestPatch, platforms) + } else { + // Fallback for old-style collections without platform suffix + await champion_stat.makeChampionsStats(client, latestPatch) } console.log('🎉 All stats generated successfully!') diff --git a/match_collector/item_tree.ts b/match_collector/item_tree.ts index 5d95fca..8747e6e 100644 --- a/match_collector/item_tree.ts +++ b/match_collector/item_tree.ts @@ -1,5 +1,12 @@ type GoldAdvantageTag = 'ahead' | 'behind' | 'even' +type PlatformCounts = { + euw: number + eun: number + na: number + kr: number +} + type ItemTree = { data: number | undefined count: number @@ -12,6 +19,13 @@ type ItemTree = { evenCount: number meanGold: number } + + // Platform tracking + platformCount: PlatformCounts +} + +function initPlatformCounts(): PlatformCounts { + return { euw: 0, eun: 0, na: 0, kr: 0 } } function treeInit(): ItemTree { @@ -19,16 +33,8 @@ function treeInit(): ItemTree { data: undefined, count: 0, children: [], - boughtWhen: { aheadCount: 0, behindCount: 0, evenCount: 0, meanGold: 0 } - } -} - -function treeNode(data: number, count: number): ItemTree { - return { - data: data, - count: count, - children: [], - boughtWhen: { aheadCount: 0, behindCount: 0, evenCount: 0, meanGold: 0 } + boughtWhen: { aheadCount: 0, behindCount: 0, evenCount: 0, meanGold: 0 }, + platformCount: initPlatformCounts() } } @@ -49,6 +55,12 @@ function nodeMerge(itemtree: ItemTree, node: ItemTree) { child.boughtWhen.evenCount += node.boughtWhen.evenCount child.boughtWhen.behindCount += node.boughtWhen.behindCount + // Merge platform counts + child.platformCount.euw += node.platformCount.euw + child.platformCount.eun += node.platformCount.eun + child.platformCount.na += node.platformCount.na + child.platformCount.kr += node.platformCount.kr + next = child break } @@ -56,7 +68,13 @@ function nodeMerge(itemtree: ItemTree, node: ItemTree) { // If not found, add item node at this level if (next == null && item !== undefined) { - next = treeNode(item, count) + next = { + data: item, + count: count, + children: [], + boughtWhen: { ...node.boughtWhen }, + platformCount: { ...node.platformCount } + } itemtree.children.push(next) } @@ -68,11 +86,12 @@ function nodeMerge(itemtree: ItemTree, node: ItemTree) { */ function treeMerge( itemtree: ItemTree, - items: Array<{ itemId: number; goldAdvantage: GoldAdvantageTag }> + items: Array<{ itemId: number; goldAdvantage: GoldAdvantageTag; platform?: string }> ) { let current = itemtree for (const item of items) { + const platformKey = item.platform ? item.platform.toLowerCase() : null current = nodeMerge(current, { data: item.itemId, count: 1, @@ -82,7 +101,13 @@ function treeMerge( behindCount: item.goldAdvantage == 'behind' ? 1 : 0, meanGold: 0 }, - children: [] + children: [], + platformCount: { + euw: platformKey === 'euw1' ? 1 : 0, + eun: platformKey === 'eun1' ? 1 : 0, + na: platformKey === 'na1' ? 1 : 0, + kr: platformKey === 'kr' ? 1 : 0 + } }) } } @@ -96,7 +121,8 @@ function treeCutBranches(itemtree: ItemTree, thresholdCount: number, thresholdPe data: undefined, count: +Infinity, children: [], - boughtWhen: { aheadCount: 0, behindCount: 0, evenCount: 0, meanGold: 0 } + boughtWhen: { aheadCount: 0, behindCount: 0, evenCount: 0, meanGold: 0 }, + platformCount: initPlatformCounts() } ) itemtree.children.splice(itemtree.children.indexOf(leastUsedBranch), 1) @@ -137,6 +163,12 @@ function treeClone(tree: ItemTree): ItemTree { behindCount: tree.boughtWhen.behindCount, evenCount: tree.boughtWhen.evenCount, meanGold: tree.boughtWhen.meanGold + }, + platformCount: { + euw: tree.platformCount.euw, + eun: tree.platformCount.eun, + na: tree.platformCount.na, + kr: tree.platformCount.kr } } } @@ -148,6 +180,17 @@ function treeMergeTree(t1: ItemTree, t2: ItemTree): ItemTree { // Merge counts for the root t1.count += t2.count + // Merge platform counts + t1.platformCount.euw += t2.platformCount.euw + t1.platformCount.eun += t2.platformCount.eun + t1.platformCount.na += t2.platformCount.na + t1.platformCount.kr += t2.platformCount.kr + + // Merge boughtWhen + t1.boughtWhen.aheadCount += t2.boughtWhen.aheadCount + t1.boughtWhen.evenCount += t2.boughtWhen.evenCount + t1.boughtWhen.behindCount += t2.boughtWhen.behindCount + // Merge children from t2 into t1 for (const child2 of t2.children) { // Find matching child in t1 (same data value) @@ -225,6 +268,7 @@ function areTreeSimilars(t1: ItemTree, t2: ItemTree): number { export { ItemTree, + PlatformCounts, GoldAdvantageTag, treeMerge, treeInit,