Initial conversion to NamedTuple representation

This commit is contained in:
Danny Robson 2019-07-14 13:17:28 +10:00
parent 4b2f52e7d5
commit 27743d9e6d
9 changed files with 57 additions and 25 deletions

View File

@ -3,6 +3,7 @@
"component" "component"
], ],
"machine": "assembler", "machine": "assembler",
"stack_size": 100,
"recipes": [ "recipes": [
{ {
"input": { "input": {
@ -14,7 +15,6 @@
}, },
"crafting_time": 12, "crafting_time": 12,
"clicks": 3, "clicks": 3,
"stack_size": 100,
"requires": "caterium_electronics" "requires": "caterium_electronics"
} }
] ]

View File

@ -2,7 +2,7 @@
"type": [ "type": [
"machine" "machine"
], ],
"is": "miner", "alias": "miner",
"machine": "_craft_bench", "machine": "_craft_bench",
"power_usage": 5, "power_usage": 5,
"size": [ "size": [

View File

@ -2,7 +2,7 @@
"type": [ "type": [
"machine" "machine"
], ],
"is": "miner", "alias": "miner",
"machine": "_craft_bench", "machine": "_craft_bench",
"power_usage": 12, "power_usage": 12,
"size": [ "size": [

View File

@ -3,6 +3,7 @@
"machine" "machine"
], ],
"machine": "_craft_bench", "machine": "_craft_bench",
"size": [ 6, 10, 10 ],
"power_usage": 50, "power_usage": 50,
"recipes": [ "recipes": [
{ {
@ -12,8 +13,7 @@
}, },
"output": { "output": {
"smelter": 1 "smelter": 1
}, }
"size": [ 6, 10, 10 ]
} }
] ]
} }

View File

@ -3,6 +3,7 @@
"machine" "machine"
], ],
"machine": "_craft_bench", "machine": "_craft_bench",
"size": [ 6, 10, 10 ],
"power_usage": 4, "power_usage": 4,
"recipes": [ "recipes": [
{ {
@ -12,8 +13,7 @@
}, },
"output": { "output": {
"smelter": 1 "smelter": 1
}, }
"size": [ 6, 10, 10 ]
} }
] ]
} }

View File

@ -16,7 +16,7 @@ def graph(cookbook: satisfactory.Cookbook, targets: Iterable[str]):
while remain: while remain:
output = remain.pop() output = remain.pop()
for need in cookbook.recipes(output)[0]['input']: for need in cookbook.recipes(output)[0].input:
print(f"{need} -> {output}") print(f"{need} -> {output}")
if need not in seen: if need not in seen:
remain.add(need) remain.add(need)

View File

@ -15,9 +15,9 @@ def basic_rate(recipe: Dict) -> Fraction:
:param recipe: :param recipe:
:return: :return:
""" """
for output, count in recipe['output'].items(): for output, count in recipe.output.items():
return Fraction( return Fraction(
count, recipe['crafting_time'] count, recipe.crafting_time
) )
@ -48,17 +48,17 @@ def required_rates(
continue continue
# Assume we're using the default recipe # Assume we're using the default recipe
dst_recipe = recipes[dst_name]['recipes'][0] dst_recipe = recipes[dst_name].recipes[0]
# Calculate the fraction of the base rate we need to satisfy, and # Calculate the fraction of the base rate we need to satisfy, and
# append our (scaled) inputs to the request queue. # append our (scaled) inputs to the request queue.
normal_rate = basic_rate(dst_recipe) normal_rate = basic_rate(dst_recipe)
scale = dst_rate / normal_rate scale = dst_rate / normal_rate
for src_name, src_count in dst_recipe['input'].items(): for src_name, src_count in dst_recipe.input.items():
src_rate = Fraction( src_rate = Fraction(
src_count, src_count,
dst_recipe['crafting_time'] dst_recipe.crafting_time
) * scale ) * scale
remain.append({src_name: src_rate}) remain.append({src_name: src_rate})
@ -90,11 +90,11 @@ def required_machines(recipes: satisfactory.Cookbook, rates: Dict[str, Fraction]
descriptor = recipes[name] descriptor = recipes[name]
normal_rate = Fraction( normal_rate = Fraction(
descriptor['recipes'][0]['output'][name], descriptor.recipes[0].output[name],
descriptor['recipes'][0]['crafting_time'] descriptor.recipes[0].crafting_time
) )
machine = descriptor['machine'] machine = descriptor.machine
required_machines[machine][name] += requested_rate / normal_rate required_machines[machine][name] += requested_rate / normal_rate
return required_machines return required_machines
@ -115,7 +115,8 @@ def required_power(recipes: satisfactory.Cookbook, machines: Dict[str, int]) ->
for machine, buckets in machines.items(): for machine, buckets in machines.items():
for result, rate in buckets.items(): for result, rate in buckets.items():
count = int(math.ceil(rate)) count = int(math.ceil(rate))
total += count * recipes[machine]['power_usage'] source = recipes[machine]
total += count * source.power_usage
print(machine, result, math.ceil(rate)) print(machine, result, math.ceil(rate))
return total return total
@ -164,7 +165,7 @@ def main():
# Create an initial name:rate request for all of the target items, then # Create an initial name:rate request for all of the target items, then
# create a plan for their creation. # create a plan for their creation.
request = [{n: basic_rate(recipes[n]['recipes'][0]) for n in args.item}] request = [{n: basic_rate(recipes[n].recipes[0]) for n in args.item}]
plan(recipes, request) plan(recipes, request)

View File

@ -1,11 +1,34 @@
import os import os
import json import json
import logging
from typing import Dict, Generator, Iterable from typing import Dict, Generator, Iterable, NamedTuple, Set, List, Optional
class Recipe(NamedTuple):
input: Dict[str, int]
output: Dict[str, int]
crafting_time: Optional[int] = None
clicks: Optional[int] = None
stage: Optional[str] = None
requires: Optional[str] = None
class Item(NamedTuple):
type: Set[str]
machine: str
recipes: List[Recipe]
alias: Optional[str] = None
stack_size: Optional[int] = None
power_usage: Optional[int] = None
size: Optional[List[int]] = None
requires: Optional[str] = None
class Cookbook(object): class Cookbook(object):
__recipes: Dict[str, Dict] __recipes: Dict[str, Item]
def __init__(self, root: str): def __init__(self, root: str):
self.__recipes = dict() self.__recipes = dict()
@ -14,8 +37,16 @@ class Cookbook(object):
for f in files: for f in files:
path = os.path.join(dirname, f) path = os.path.join(dirname, f)
name, _ = os.path.splitext(f) name, _ = os.path.splitext(f)
with open(path, 'r') as src: with open(path, 'r') as src:
self.__recipes[name] = json.load(src) j = json.load(src)
try:
recipes = list(Recipe(**r) for r in j['recipes'])
j['recipes'] = recipes
self.__recipes[name] = Item(**j)
except Exception as ex:
logging.error(f"Could not load {name}: {ex}")
def __getitem__(self, item: str) -> Dict[str, Dict]: def __getitem__(self, item: str) -> Dict[str, Dict]:
return self.__recipes[item] return self.__recipes[item]
@ -31,14 +62,14 @@ class Cookbook(object):
:param name: The target item name :param name: The target item name
:return: A sequence of possible recipes for the target item :return: A sequence of possible recipes for the target item
""" """
return self.__recipes[name]['recipes'] return self.__recipes[name].recipes
def is_component(self, name: str) -> bool: def is_component(self, name: str) -> bool:
""" """
:param name: The name of an item :param name: The name of an item
:return: Whether the item is a component; ie, craftable. :return: Whether the item is a component; ie, craftable.
""" """
return 'component' in self.__recipes[name]['type'] return 'component' in self.__recipes[name].type
def components(self) -> Generator[str, None, None]: def components(self) -> Generator[str, None, None]:
""" """
@ -51,7 +82,7 @@ class Cookbook(object):
:param name: The name of an item :param name: The name of an item
:return: Whether the item must have harvested (rather than crafted) :return: Whether the item must have harvested (rather than crafted)
""" """
return 'resource' in self.__recipes[name]['type'] return 'resource' in self.__recipes[name].type
def resources(self) -> Generator[str, None, None]: def resources(self) -> Generator[str, None, None]:
""" """