from __future__ import annotations import collections import typing import cache import roi def main() -> None: blueprint = { 'FFC': 1, 'FSE': 1, 'LFE': 2, 'MFE': 2, 'QCR': 1, 'SFE': 1, 'LCB': 1, 'MFL': 1, 'MSL': 1, 'LHP': 94, 'SSC': 128, 'BR1': 1, 'CQM': 1, } prices: typing.Mapping[str, RawPrice] = {p['MaterialTicker']: p for p in cache.get('https://refined-prun.github.io/refined-prices/all.json') if p['ExchangeCode'] == 'IC1'} recipes = recipe_for_mats() buildings: dict[str, set[str]] = collections.defaultdict(set) cost = 0.0 for mat, amount in blueprint.items(): cost += analyze_mat(0, mat, amount, buildings, prices, recipes) print() print(f'total cost: {cost:,}') for building, mats in buildings.items(): print(f'{building:3}: {", ".join(mats)}') def recipe_for_mats() -> dict[str, roi.Recipe]: all_recipes: list[roi.Recipe] = cache.get('https://api.prunplanner.org/data/recipes/') mat_recipes = collections.defaultdict(list) # all ways to make a mat for recipe in all_recipes: for output in recipe['outputs']: mat_recipes[output['material_ticker']].append(recipe) mat_recipe: dict[str, roi.Recipe] = {} # mats for which there's only one recipe to make for mat, recipes in mat_recipes.items(): if len(recipes) == 1: mat_recipe[mat] = recipes[0] return mat_recipe def analyze_mat(level: int, mat: str, amount: float, buildings: dict[str, set[str]], prices: typing.Mapping[str, RawPrice], recipes: dict[str, roi.Recipe]) -> float: price = prices[mat] traded = price['AverageTraded30D'] or 0 if (price['Supply'] > amount * 10 and traded > 0) or (price['Supply'] > amount * 4 and amount < traded): print('\t' * level + f'{amount:g}×{mat} buy: {price["Ask"]:9,}, daily traded {traded:5.1f}, supply {price["Supply"]:4}') assert price['Ask'] is not None return price['Ask'] * amount else: if (recipe := recipes.get(mat)) is None: print('\t' * level + f'{amount:g}×{mat} make (unknown recipe)') return 0 else: building = recipe['building_ticker'] print('\t' * level + f'{amount:g}×{mat} make ({building})') buildings[building].add(mat) total_cost = 0.0 for input_mat in recipe['inputs']: input_amount = input_mat['material_amount'] * amount / recipe['outputs'][0]['material_amount'] total_cost += analyze_mat(level + 1, input_mat['material_ticker'], input_amount, buildings, prices, recipes) print('\t' * level + f'\tcost: {total_cost:9,.2f}') return total_cost class RawPrice(typing.TypedDict): MaterialTicker: str ExchangeCode: str Ask: float | None AverageTraded30D: float | None # averaged daily traded volume over last 30 days Supply: int if __name__ == '__main__': main()