|
@@ -109,41 +109,56 @@ def calc_consumption_profit(month: str, prices: typing.Mapping[str, float],
|
|
|
|
|
|
|
|
def get_consumption_ratios(month: str) -> typing.Mapping[str, typing.Mapping[str, float]]:
|
|
def get_consumption_ratios(month: str) -> typing.Mapping[str, typing.Mapping[str, float]]:
|
|
|
with open(f'rawData/{month}-recipes.json', 'r') as f:
|
|
with open(f'rawData/{month}-recipes.json', 'r') as f:
|
|
|
- raw_recipes: typing.Sequence[typing.Mapping] = json.load(f)
|
|
|
|
|
|
|
+ recipes: typing.Mapping[str, Recipe] = {recipe['RecipeName']: recipe for recipe in json.load(f)}
|
|
|
|
|
+ with open(f'rawData/{month}-recipe-ratios.json', 'r') as f:
|
|
|
|
|
+ recipe_ratios: dict[str, typing.Mapping[str, float]] = json.load(f)
|
|
|
with open('rawData/buildings.json', 'r') as f:
|
|
with open('rawData/buildings.json', 'r') as f:
|
|
|
buildings: typing.Mapping[str, typing.Mapping] = {b['Ticker']: b for b in json.load(f)}
|
|
buildings: typing.Mapping[str, typing.Mapping] = {b['Ticker']: b for b in json.load(f)}
|
|
|
- worker_daily_consumption = {
|
|
|
|
|
- 'Pioneers': [('COF', 0.5), ('DW', 4), ('RAT', 4), ('OVE', 0.5), ('PWO', 0.2)],
|
|
|
|
|
- 'Settlers': [('DW', 5), ('RAT', 6), ('KOM', 1), ('EXO', 0.5), ('REP', 0.2), ('PT', 0.5)],
|
|
|
|
|
- 'Technicians': [('DW', 7.5), ('RAT', 7), ('ALE', 1), ('MED', 0.5), ('SC', 0.1), ('HMS', 0.5), ('SCN', 0.1)],
|
|
|
|
|
- 'Engineers': [('DW', 10), ('MED', 0.5), ('GIN', 1), ('FIM', 7), ('VG', 0.2), ('HSS', 0.2), ('PDA', 0.1)],
|
|
|
|
|
- 'Scientists': [('DW', 10), ('MED', 0.5), ('WIN', 1), ('MEA', 7), ('NST', 0.1), ('LC', 0.2), ('WS', 0.05)],
|
|
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
outputs: dict[str, typing.Mapping[str, float]] = {}
|
|
outputs: dict[str, typing.Mapping[str, float]] = {}
|
|
|
- duplicates = set()
|
|
|
|
|
- for recipe in raw_recipes:
|
|
|
|
|
|
|
+ duplicates = set() # mats that can be produced by more than 1 recipe
|
|
|
|
|
+ for recipe in recipes.values():
|
|
|
for output in recipe['Outputs']:
|
|
for output in recipe['Outputs']:
|
|
|
output_ticker = output['Ticker']
|
|
output_ticker = output['Ticker']
|
|
|
if output_ticker in outputs:
|
|
if output_ticker in outputs:
|
|
|
duplicates.add(output_ticker)
|
|
duplicates.add(output_ticker)
|
|
|
continue
|
|
continue
|
|
|
- ratios = {
|
|
|
|
|
- input['Ticker']: input['Amount'] / output['Amount'] for input in recipe['Inputs']
|
|
|
|
|
- }
|
|
|
|
|
- building = buildings[recipe['BuildingTicker']]
|
|
|
|
|
- for worker, mats in worker_daily_consumption.items():
|
|
|
|
|
- workers = building[worker]
|
|
|
|
|
- for mat, per_100 in mats:
|
|
|
|
|
- consumed_per_day = workers * per_100 / 100
|
|
|
|
|
- # assume 160% efficiency
|
|
|
|
|
- consumed_per_unit_output = consumed_per_day / output['Amount'] * (recipe['TimeMs'] / (24*60*60*1000) / 1.6)
|
|
|
|
|
- ratios[mat] = ratios.get(mat, 0) + consumed_per_unit_output
|
|
|
|
|
|
|
+ ratios = consumption_ratio_for_recipe(recipe, output, buildings)
|
|
|
outputs[output_ticker] = ratios
|
|
outputs[output_ticker] = ratios
|
|
|
for dupe in duplicates:
|
|
for dupe in duplicates:
|
|
|
- del outputs[dupe]
|
|
|
|
|
|
|
+ recipe_ratio = recipe_ratios.pop(dupe)
|
|
|
|
|
+ assert dupe == 'SIO' or abs(sum(recipe_ratio.values()) - 1.0) < 1e-6, f"recipe ratio for {dupe} doesn't sum to 1"
|
|
|
|
|
+ ratios = collections.defaultdict(float)
|
|
|
|
|
+ for recipe_name, weight in recipe_ratio.items():
|
|
|
|
|
+ recipe = recipes[recipe_name]
|
|
|
|
|
+ (output,) = (o for o in recipe['Outputs'] if o['Ticker'] == dupe)
|
|
|
|
|
+ for input_item, ratio in consumption_ratio_for_recipe(recipe, output, buildings).items():
|
|
|
|
|
+ ratios[input_item] += weight * ratio
|
|
|
|
|
+ outputs[dupe] = ratios
|
|
|
|
|
+ assert len(recipe_ratios) == 0, f'unused recipe ratios: {recipe_ratios.keys()}'
|
|
|
return outputs
|
|
return outputs
|
|
|
|
|
|
|
|
|
|
+worker_daily_consumption = {
|
|
|
|
|
+ 'Pioneers': [('COF', 0.5), ('DW', 4), ('RAT', 4), ('OVE', 0.5), ('PWO', 0.2)],
|
|
|
|
|
+ 'Settlers': [('DW', 5), ('RAT', 6), ('KOM', 1), ('EXO', 0.5), ('REP', 0.2), ('PT', 0.5)],
|
|
|
|
|
+ 'Technicians': [('DW', 7.5), ('RAT', 7), ('ALE', 1), ('MED', 0.5), ('SC', 0.1), ('HMS', 0.5), ('SCN', 0.1)],
|
|
|
|
|
+ 'Engineers': [('DW', 10), ('MED', 0.5), ('GIN', 1), ('FIM', 7), ('VG', 0.2), ('HSS', 0.2), ('PDA', 0.1)],
|
|
|
|
|
+ 'Scientists': [('DW', 10), ('MED', 0.5), ('WIN', 1), ('MEA', 7), ('NST', 0.1), ('LC', 0.2), ('WS', 0.05)],
|
|
|
|
|
+}
|
|
|
|
|
+def consumption_ratio_for_recipe(recipe: Recipe, output: RecipeMat, buildings: dict) -> typing.Mapping[str, float]:
|
|
|
|
|
+ ratios = {
|
|
|
|
|
+ input['Ticker']: input['Amount'] / output['Amount'] for input in recipe['Inputs']
|
|
|
|
|
+ }
|
|
|
|
|
+ building = buildings[recipe['BuildingTicker']]
|
|
|
|
|
+ for worker, mats in worker_daily_consumption.items():
|
|
|
|
|
+ workers = building[worker]
|
|
|
|
|
+ for mat, per_100 in mats:
|
|
|
|
|
+ consumed_per_day = workers * per_100 / 100
|
|
|
|
|
+ # assume 160% efficiency
|
|
|
|
|
+ consumed_per_unit_output = consumed_per_day / output['Amount'] * (recipe['TimeMs'] / (24*60*60*1000) / 1.6)
|
|
|
|
|
+ ratios[mat] = ratios.get(mat, 0) + consumed_per_unit_output
|
|
|
|
|
+ return ratios
|
|
|
|
|
+
|
|
|
def write_data(month: str, data: dict[str, list[Row]],
|
|
def write_data(month: str, data: dict[str, list[Row]],
|
|
|
prod_data: typing.Mapping[str, ProdData], company_data: CompanyData) -> None:
|
|
prod_data: typing.Mapping[str, ProdData], company_data: CompanyData) -> None:
|
|
|
with open(f'www/data/prod-data-{month}.json', 'w') as f:
|
|
with open(f'www/data/prod-data-{month}.json', 'w') as f:
|
|
@@ -190,6 +205,17 @@ class Price(typing.TypedDict):
|
|
|
VWAP30D: float | None
|
|
VWAP30D: float | None
|
|
|
Traded30D: int | None
|
|
Traded30D: int | None
|
|
|
|
|
|
|
|
|
|
+class Recipe(typing.TypedDict):
|
|
|
|
|
+ BuildingTicker: str
|
|
|
|
|
+ RecipeName: str
|
|
|
|
|
+ Inputs: typing.Sequence[RecipeMat]
|
|
|
|
|
+ Outputs: typing.Sequence[RecipeMat]
|
|
|
|
|
+ TimeMs: int
|
|
|
|
|
+
|
|
|
|
|
+class RecipeMat(typing.TypedDict):
|
|
|
|
|
+ Ticker: str
|
|
|
|
|
+ Amount: float
|
|
|
|
|
+
|
|
|
class ProdData(typing.TypedDict):
|
|
class ProdData(typing.TypedDict):
|
|
|
amount: float
|
|
amount: float
|
|
|
volume: float
|
|
volume: float
|