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,