Multiple changes
- backend: add summoner spells - backend: add build variants - backend: builds are now storing full tree with runes (keystones) - backend: build trees are split on starter items and merged on runes - frontend: computing core tree now - frontend: variant selectors
This commit is contained in:
@@ -1,64 +1,89 @@
|
||||
import { isEmpty } from './helpers'
|
||||
|
||||
/**
|
||||
* Trims the build tree to only show the first path
|
||||
* Removes alternate build paths to keep the UI clean
|
||||
* Gets all late game items from the item tree (items beyond first level)
|
||||
* Returns a flat array of unique items with their counts
|
||||
*/
|
||||
export function trimBuilds(builds: Builds): void {
|
||||
if (!builds?.tree?.children) return
|
||||
export function getLateGameItems(build: Build): Array<{ data: number; count: number }> {
|
||||
const lateGameItems: Array<{ data: number; count: number }> = []
|
||||
const itemCounts = new Map<number, number>()
|
||||
|
||||
// Keep only the first child (primary build path)
|
||||
builds.tree.children.splice(1, builds.tree.children.length - 1)
|
||||
// Collect late items
|
||||
function collectLateItems(tree: ItemTree, depth: number = 0): void {
|
||||
if (depth >= 3 && tree.data !== undefined && tree.count > 0) {
|
||||
const existing = itemCounts.get(tree.data) || 0
|
||||
itemCounts.set(tree.data, existing + tree.count)
|
||||
}
|
||||
|
||||
// Also trim grandchildren to first path only
|
||||
if (builds.tree.children[0]?.children) {
|
||||
builds.tree.children[0].children.splice(1, builds.tree.children[0].children.length - 1)
|
||||
for (const child of tree.children) {
|
||||
collectLateItems(child, depth + 1)
|
||||
}
|
||||
}
|
||||
|
||||
collectLateItems(build.items)
|
||||
|
||||
// Convert map to array
|
||||
for (const [data, count] of itemCounts.entries()) {
|
||||
lateGameItems.push({ data, count })
|
||||
}
|
||||
|
||||
lateGameItems.sort((a, b) => b.count - a.count)
|
||||
|
||||
console.log(lateGameItems)
|
||||
|
||||
// Sort by count descending
|
||||
return lateGameItems.filter(item => !treeToArray(getCoreItems(build)).includes(item.data))
|
||||
}
|
||||
|
||||
function treeToArray(tree: ItemTree): Array<number> {
|
||||
const arr: Array<number> = []
|
||||
|
||||
if (tree.data != null) arr.push(tree.data)
|
||||
|
||||
for (const child of tree.children) arr.push(...treeToArray(child))
|
||||
|
||||
return arr
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes late game items that appear in the core build tree
|
||||
* Prevents duplicate items from being shown
|
||||
* Creates a deep copy of an ItemTree trimmed to a maximum depth
|
||||
* @param tree - The item tree to copy and trim
|
||||
* @param maxDepth - The maximum depth to keep (inclusive)
|
||||
* @param currentDepth - The current depth during recursion
|
||||
* @returns A new ItemTree with children trimmed beyond maxDepth
|
||||
*/
|
||||
export function trimLateGameItems(builds: Builds): void {
|
||||
if (!builds?.tree || isEmpty(builds.lateGame)) return
|
||||
function trimTreeDepth(tree: ItemTree, maxDepth: number, currentDepth: number = 0): ItemTree {
|
||||
const trimmedTree: ItemTree = {
|
||||
count: tree.count,
|
||||
data: tree.data,
|
||||
children: []
|
||||
}
|
||||
|
||||
const coreItemIds = new Set<number>()
|
||||
|
||||
// Collect all item IDs from the tree
|
||||
function collectItemIds(tree: ItemTree): void {
|
||||
if (tree.data !== undefined) {
|
||||
coreItemIds.add(tree.data)
|
||||
}
|
||||
// If we haven't reached maxDepth, include children
|
||||
if (currentDepth < maxDepth) {
|
||||
for (const child of tree.children || []) {
|
||||
collectItemIds(child)
|
||||
trimmedTree.children.push(trimTreeDepth(child, maxDepth, currentDepth + 1))
|
||||
}
|
||||
}
|
||||
|
||||
collectItemIds(builds.tree)
|
||||
|
||||
// Remove late game items that appear in core
|
||||
builds.lateGame = builds.lateGame.filter(item => !coreItemIds.has(item.data))
|
||||
return trimmedTree
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the index of the build with the highest pickrate
|
||||
*/
|
||||
export function getHighestPickrateBuildIndex(runes: Array<{ pickrate: number }>): number {
|
||||
if (runes.length === 0) return 0
|
||||
function trimTreeChildrensAtDepth(tree: ItemTree, maxChildren: number, depth: number) {
|
||||
if (depth == 0) {
|
||||
if (tree.children.length > maxChildren) {
|
||||
tree.children.splice(maxChildren, tree.children.length - maxChildren)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
return runes.reduce(
|
||||
(maxIdx, rune, idx, arr) => (rune.pickrate > arr[maxIdx].pickrate ? idx : maxIdx),
|
||||
0
|
||||
)
|
||||
for (const c of tree.children) {
|
||||
trimTreeChildrensAtDepth(c, maxChildren, depth - 1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first core item for each build variant
|
||||
*/
|
||||
export function getFirstCoreItems(runes: unknown[], builds: Builds): number[] {
|
||||
return runes.map(() => {
|
||||
const tree = builds?.tree
|
||||
return tree?.children?.[0]?.data ?? tree?.data ?? 0
|
||||
})
|
||||
export function getCoreItems(build: Build): ItemTree {
|
||||
const tree = trimTreeDepth(build.items, 3)
|
||||
trimTreeChildrensAtDepth(tree, 1, 0)
|
||||
trimTreeChildrensAtDepth(tree, 1, 1)
|
||||
trimTreeChildrensAtDepth(tree, 3, 2)
|
||||
return tree
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user