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.

165 lines
6.7 KiB

  1. from json.decoder import JSONDecodeError
  2. import json
  3. import re
  4. import sys
  5. ITEMS = {}
  6. ################################################################################################################################
  7. # file operations #
  8. # this section is dedicated to operations that save, load, and parse data stored to disk #
  9. # || || || || || || || || || || || #
  10. # \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ #
  11. ################################################################################################################################
  12. def to_dict(obj):
  13. return obj.__dict_repr__
  14. def from_dict(d):
  15. name = d['name']
  16. type = d.pop('type')
  17. cls = globals()[type]
  18. instance = cls(**d)
  19. instance.__dict__.update(d)
  20. global ITEMS
  21. ITEMS[name] = instance
  22. def to_json():
  23. global ITEMS
  24. if ITEMS:
  25. ret = list(map(to_dict,ITEMS.values()))
  26. with open('__cache__','w') as file:
  27. json.dump(ret,file)
  28. else:
  29. with open('__cache__','w') as file:
  30. json.dump([],file)
  31. def from_json():
  32. global ITEMS
  33. with open('__cache__') as file:
  34. data = json.load(file)
  35. for item in data:
  36. from_dict(item)
  37. ################################################################################################################################
  38. # user input #
  39. # this section is dedicated to operations that will not have any other purpose other than collecting data #
  40. # || || || || || || || || || || || #
  41. # \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ #
  42. ################################################################################################################################
  43. class user_input:
  44. def __init__(self,comp_name):
  45. valid = ('raw','compound')
  46. error = True
  47. while error:
  48. try:
  49. type = int(input("~{}~\ntype (0. {} | 1. {}): ".format(comp_name,*valid)))
  50. error = False
  51. except ValueError:
  52. pass
  53. self.type = valid[type]
  54. if self.type == 'compound':
  55. self.subs = {}
  56. i = 1
  57. while True:
  58. inp = input('sub #{} ({{comp_name}} * {{count}}): '.format(i))
  59. if inp == 'end':
  60. break
  61. sr = re.split(r' *\* *',inp)
  62. name = sr[0]
  63. if len(sr) == 1:
  64. count = 1
  65. else:
  66. count = int(sr[1])
  67. self.subs[name] = count
  68. i += 1
  69. def recursive_fill_in(comp_name):
  70. global ITEMS
  71. if not comp_name in ITEMS.keys():
  72. res = user_input(comp_name)
  73. if res.type == 'raw':
  74. ITEMS[comp_name] = raw_material(comp_name)
  75. else:
  76. ITEMS[comp_name] = complex_item(comp_name,res.subs)
  77. for sub in res.subs.keys():
  78. recursive_fill_in(sub)
  79. ################################################################################################################################
  80. # complexity #
  81. # this section is dedicated to operations that involve all processing and understanding of the input data #
  82. # || || || || || || || || || || || #
  83. # \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/ #
  84. ################################################################################################################################
  85. class item:
  86. def __init__(self,name):
  87. self.name = name
  88. self.__dict_repr__ = {
  89. 'name':name,
  90. 'type':type(self).__name__
  91. }
  92. class raw_material(item):
  93. def __init__(self,name,**placeholder):
  94. super().__init__(name)
  95. self.complexity = 0
  96. class complex_item(item):
  97. def __init__(self,name,sub_components,**placeholder):
  98. super().__init__(name)
  99. self.sub_components = sub_components
  100. self.__dict_repr__['sub_components'] = sub_components
  101. self.naive_complexity = True
  102. self.__dict_repr__['naive_complexity'] = True
  103. self.all_sub = set(self.sub_components.keys())
  104. def recalculate_total_completixy(self):
  105. for name in self.sub_components.keys():
  106. component = ITEMS[name]
  107. if isinstance(component,complex_item):
  108. if component.naive_complexity:
  109. component.recalculate_total_completixy()
  110. component.naive_complexity = False
  111. self.all_sub.update(component.all_sub)
  112. @property
  113. def complexity(self):
  114. if self.naive_complexity:
  115. self.recalculate_total_completixy()
  116. self.naive_complexity = False
  117. return len(self.all_sub)
  118. def get_raw_material_cost(self,ret = {}):
  119. for name,count in self.sub_components.items():
  120. component = ITEMS[name]
  121. if isinstance(component,raw_material):
  122. prev = ret.get(name,0)
  123. ret[name] = prev + count
  124. elif isinstance(component,complex_item):
  125. component.get_raw_material_cost(ret)
  126. return ret
  127. def get_component_cost(self,ret = {}):
  128. for name,count in self.sub_components.items():
  129. component = ITEMS[name]
  130. if isinstance(component,complex_item):
  131. prev = ret.get(name,0)
  132. ret[name] = prev + count
  133. component.get_component_cost(ret)
  134. return ret
  135. def get_build_info(self):
  136. material_view = self.get_raw_material_cost()
  137. component_view = self.get_component_cost()
  138. build_order = component_view.items()
  139. build_order = sorted(build_order,key = lambda item: ITEMS[item[0]].complexity)
  140. return material_view,build_order
  141. def main():
  142. import os
  143. if not os.path.exists('__cache__'):
  144. to_json()
  145. try:
  146. from_json()
  147. except JSONDecodeError:
  148. to_json()
  149. from_json()
  150. name = sys.argv[1]
  151. recursive_fill_in(name)
  152. print(*ITEMS[name].get_build_info(),sep='\n')
  153. to_json()
  154. if __name__ == "__main__":
  155. main()