import {cachedFetchJSON} from './cache'; render(); async function render(): Promise { const loader = document.querySelector('#loader') as HTMLElement; loader.style.display = 'block'; try { await _render(); } catch (e) { console.error(e); } loader.style.display = 'none'; } async function _render(): Promise { const allRecipes: readonly RecipeFull[] = await cachedFetchJSON('https://api.prunplanner.org/data/recipes/'); const cols = ratRecipes(allRecipes); const frm = buildingOutputs(allRecipes, 'FRM'); const hyf = buildingOutputs(allRecipes, 'HYF'); document.querySelector('#cols')!.append(...cols.map((col) => { const colEl = document.createElement('div'); colEl.className = 'col'; colEl.append(...Array.from(col).map((input) => { const matEl = document.createElement('div'); matEl.className = 'mat'; if (frm.has(input)) matEl.classList.add('frm'); if (hyf.has(input)) matEl.classList.add('hyf'); matEl.textContent = input; return matEl; })); return colEl; })); } function ratRecipes(allRecipes: readonly RecipeFull[]): Set[] { const ratRecipes = allRecipes.filter((recipe) => recipe.building_ticker === 'FP' && recipe.outputs.length === 1 && recipe.outputs[0].material_ticker === 'RAT'); const recipes: Recipe[] = ratRecipes.map((recipe) => { return { inputs: recipe.inputs.map((input) => input.material_ticker), catalogued: false, }; }); const cols: Set[] = [new Set(), new Set(), new Set()]; const seenInputs = new Set(); recipes[0].inputs.forEach((input, index) => { cols[index].add(input); seenInputs.add(input); }); recipes[0].catalogued = true; let allCatalogued; do { allCatalogued = true; for (const recipe of recipes) { if (recipe.catalogued) continue; const inputs = new Set(recipe.inputs); const newInputs = inputs.difference(seenInputs); if (newInputs.size === 1) { const newInput = newInputs.keys().next().value!; const seenCols = Array.from(inputs.difference(newInputs)).map((oldInput) => cols.findIndex((col) => col.has(oldInput))); const newCol = new Set([0, 1, 2]).difference(new Set(seenCols)).keys().next().value!; cols[newCol].add(newInput); seenInputs.add(newInput); recipe.catalogued = true; } else if (newInputs.size === 0) recipe.catalogued = true; else allCatalogued = false; } } while (!allCatalogued); return cols; } function buildingOutputs(allRecipes: readonly RecipeFull[], building: string): Set { const outputs = new Set(); for (const recipe of allRecipes) if (recipe.building_ticker === building) for (const output of recipe.outputs) outputs.add(output.material_ticker); return outputs; } interface RecipeFull { building_ticker: string; inputs: Array<{material_ticker: string}> outputs: Array<{material_ticker: string}> } interface Recipe { readonly inputs: Array catalogued: boolean; }