You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
6.7 KiB
166 lines
6.7 KiB
from json.decoder import JSONDecodeError
|
|
import json
|
|
import re
|
|
import sys
|
|
ITEMS = {}
|
|
################################################################################################################################
|
|
# file operations #
|
|
# this section is dedicated to operations that save, load, and parse data stored to disk #
|
|
# || || || || || || || || || || || #
|
|
# \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ #
|
|
################################################################################################################################
|
|
def to_dict(obj):
|
|
return obj.__dict_repr__
|
|
|
|
def from_dict(d):
|
|
name = d['name']
|
|
type = d.pop('type')
|
|
cls = globals()[type]
|
|
instance = cls(**d)
|
|
instance.__dict__.update(d)
|
|
global ITEMS
|
|
ITEMS[name] = instance
|
|
|
|
def to_json():
|
|
global ITEMS
|
|
if ITEMS:
|
|
ret = list(map(to_dict,ITEMS.values()))
|
|
with open('__cache__','w') as file:
|
|
json.dump(ret,file)
|
|
else:
|
|
with open('__cache__','w') as file:
|
|
json.dump([],file)
|
|
def from_json():
|
|
global ITEMS
|
|
with open('__cache__') as file:
|
|
data = json.load(file)
|
|
for item in data:
|
|
from_dict(item)
|
|
|
|
################################################################################################################################
|
|
# user input #
|
|
# this section is dedicated to operations that will not have any other purpose other than collecting data #
|
|
# || || || || || || || || || || || #
|
|
# \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ #
|
|
################################################################################################################################
|
|
class user_input:
|
|
def __init__(self,comp_name):
|
|
valid = ('raw','compound')
|
|
error = True
|
|
while error:
|
|
try:
|
|
type = int(input("~{}~\ntype (0. {} | 1. {}): ".format(comp_name,*valid)))
|
|
error = False
|
|
except ValueError:
|
|
pass
|
|
self.type = valid[type]
|
|
if self.type == 'compound':
|
|
self.subs = {}
|
|
i = 1
|
|
while True:
|
|
inp = input('sub #{} ({{comp_name}} * {{count}}): '.format(i))
|
|
if inp == 'end':
|
|
break
|
|
sr = re.split(r' *\* *',inp)
|
|
name = sr[0]
|
|
if len(sr) == 1:
|
|
count = 1
|
|
else:
|
|
count = int(sr[1])
|
|
self.subs[name] = count
|
|
i += 1
|
|
|
|
def recursive_fill_in(comp_name):
|
|
global ITEMS
|
|
if not comp_name in ITEMS.keys():
|
|
res = user_input(comp_name)
|
|
if res.type == 'raw':
|
|
ITEMS[comp_name] = raw_material(comp_name)
|
|
else:
|
|
ITEMS[comp_name] = complex_item(comp_name,res.subs)
|
|
for sub in res.subs.keys():
|
|
recursive_fill_in(sub)
|
|
|
|
################################################################################################################################
|
|
# complexity #
|
|
# this section is dedicated to operations that involve all processing and understanding of the input data #
|
|
# || || || || || || || || || || || #
|
|
# \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ #
|
|
################################################################################################################################
|
|
class item:
|
|
def __init__(self,name):
|
|
self.name = name
|
|
self.__dict_repr__ = {
|
|
'name':name,
|
|
'type':type(self).__name__
|
|
}
|
|
class raw_material(item):
|
|
def __init__(self,name,**placeholder):
|
|
super().__init__(name)
|
|
self.complexity = 0
|
|
|
|
class complex_item(item):
|
|
def __init__(self,name,sub_components,**placeholder):
|
|
super().__init__(name)
|
|
self.sub_components = sub_components
|
|
self.__dict_repr__['sub_components'] = sub_components
|
|
self.naive_complexity = True
|
|
self.__dict_repr__['naive_complexity'] = True
|
|
self.all_sub = set(self.sub_components.keys())
|
|
|
|
def recalculate_total_completixy(self):
|
|
for name in self.sub_components.keys():
|
|
component = ITEMS[name]
|
|
if isinstance(component,complex_item):
|
|
if component.naive_complexity:
|
|
component.recalculate_total_completixy()
|
|
component.naive_complexity = False
|
|
self.all_sub.update(component.all_sub)
|
|
|
|
@property
|
|
def complexity(self):
|
|
if self.naive_complexity:
|
|
self.recalculate_total_completixy()
|
|
self.naive_complexity = False
|
|
return len(self.all_sub)
|
|
|
|
def get_raw_material_cost(self,ret = {}):
|
|
for name,count in self.sub_components.items():
|
|
component = ITEMS[name]
|
|
if isinstance(component,raw_material):
|
|
prev = ret.get(name,0)
|
|
ret[name] = prev + count
|
|
elif isinstance(component,complex_item):
|
|
component.get_raw_material_cost(ret)
|
|
return ret
|
|
|
|
def get_component_cost(self,ret = {}):
|
|
for name,count in self.sub_components.items():
|
|
component = ITEMS[name]
|
|
if isinstance(component,complex_item):
|
|
prev = ret.get(name,0)
|
|
ret[name] = prev + count
|
|
component.get_component_cost(ret)
|
|
return ret
|
|
|
|
def get_build_info(self):
|
|
material_view = self.get_raw_material_cost()
|
|
component_view = self.get_component_cost()
|
|
build_order = component_view.items()
|
|
build_order = sorted(build_order,key = lambda item: ITEMS[item[0]].complexity)
|
|
return material_view,build_order
|
|
def main():
|
|
import os
|
|
if not os.path.exists('__cache__'):
|
|
to_json()
|
|
try:
|
|
from_json()
|
|
except JSONDecodeError:
|
|
to_json()
|
|
from_json()
|
|
name = sys.argv[1]
|
|
recursive_fill_in(name)
|
|
print(*ITEMS[name].get_build_info(),sep='\n')
|
|
to_json()
|
|
if __name__ == "__main__":
|
|
main()
|