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()