| 
									
										
										
										
											2021-04-10 03:03:46 +02:00
										 |  |  | from __future__ import annotations | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-30 23:58:58 +02:00
										 |  |  | import orjson | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | import logging | 
					
						
							|  |  |  | import os | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | import string | 
					
						
							| 
									
										
										
										
											2023-04-09 20:58:24 +02:00
										 |  |  | import pkgutil | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | from collections import Counter | 
					
						
							|  |  |  | from concurrent.futures import ThreadPoolExecutor | 
					
						
							| 
									
										
										
										
											2022-06-18 13:40:02 +02:00
										 |  |  | from typing import Dict, Set, FrozenSet, Tuple, Union, List, Any | 
					
						
							| 
									
										
										
										
											2021-05-22 10:06:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | import Utils | 
					
						
							| 
									
										
										
										
											2021-06-25 23:32:13 +02:00
										 |  |  | from . import Options | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 20:58:24 +02:00
										 |  |  | factorio_tech_id = factorio_base_id = 2 ** 17 | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | # Factorio technologies are imported from a .json document in /data | 
					
						
							| 
									
										
										
										
											2021-07-31 15:13:55 +02:00
										 |  |  | source_folder = os.path.join(os.path.dirname(__file__), "data") | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | pool = ThreadPoolExecutor(1) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-18 13:40:02 +02:00
										 |  |  | def load_json_data(data_name: str) -> Union[List[str], Dict[str, Any]]: | 
					
						
							| 
									
										
										
										
											2023-09-30 23:58:58 +02:00
										 |  |  |     return orjson.loads(pkgutil.get_data(__name__, "data/" + data_name + ".json")) | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | techs_future = pool.submit(load_json_data, "techs") | 
					
						
							|  |  |  | recipes_future = pool.submit(load_json_data, "recipes") | 
					
						
							| 
									
										
										
										
											2022-06-19 18:06:27 +02:00
										 |  |  | resources_future = pool.submit(load_json_data, "resources") | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | machines_future = pool.submit(load_json_data, "machines") | 
					
						
							| 
									
										
										
										
											2022-06-18 13:40:02 +02:00
										 |  |  | fluids_future = pool.submit(load_json_data, "fluids") | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | items_future = pool.submit(load_json_data, "items") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-03 18:06:21 +02:00
										 |  |  | tech_table: Dict[str, int] = {} | 
					
						
							| 
									
										
										
										
											2021-04-24 01:16:49 +02:00
										 |  |  | technology_table: Dict[str, Technology] = {} | 
					
						
							| 
									
										
										
										
											2021-04-05 15:37:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 20:58:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | def always(state): | 
					
						
							|  |  |  |     return True | 
					
						
							| 
									
										
										
										
											2021-04-05 15:37:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-10 03:03:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-28 21:00:06 +02:00
										 |  |  | class FactorioElement: | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |     name: str | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         return f"{self.__class__.__name__}({self.name})" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __hash__(self): | 
					
						
							|  |  |  |         return hash(self.name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Technology(FactorioElement):  # maybe make subclass of Location? | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |     has_modifier: bool | 
					
						
							|  |  |  |     factorio_id: int | 
					
						
							|  |  |  |     ingredients: Set[str] | 
					
						
							|  |  |  |     progressive: Tuple[str] | 
					
						
							|  |  |  |     unlocks: Union[Set[str], bool]  # bool case is for progressive technologies | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, name: str, ingredients: Set[str], factorio_id: int, progressive: Tuple[str] = (), | 
					
						
							|  |  |  |                  has_modifier: bool = False, unlocks: Union[Set[str], bool] = None): | 
					
						
							| 
									
										
										
										
											2021-04-05 15:37:15 +02:00
										 |  |  |         self.name = name | 
					
						
							|  |  |  |         self.factorio_id = factorio_id | 
					
						
							|  |  |  |         self.ingredients = ingredients | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  |         self.progressive = progressive | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |         self.has_modifier = has_modifier | 
					
						
							|  |  |  |         if unlocks: | 
					
						
							|  |  |  |             self.unlocks = unlocks | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.unlocks = set() | 
					
						
							| 
									
										
										
										
											2021-04-05 15:37:15 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-24 01:16:49 +02:00
										 |  |  |     def build_rule(self, player: int): | 
					
						
							| 
									
										
										
										
											2021-04-09 22:10:04 +02:00
										 |  |  |         logging.debug(f"Building rules for {self.name}") | 
					
						
							| 
									
										
										
										
											2021-04-10 00:08:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-20 04:47:19 +01:00
										 |  |  |         return lambda state: all(state.has(f"Automated {ingredient}", player) | 
					
						
							|  |  |  |                                  for ingredient in self.ingredients) | 
					
						
							| 
									
										
										
										
											2021-04-08 19:53:24 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |     def get_prior_technologies(self) -> Set[Technology]: | 
					
						
							| 
									
										
										
										
											2021-04-10 03:03:46 +02:00
										 |  |  |         """Get Technologies that have to precede this one to resolve tree connections.""" | 
					
						
							|  |  |  |         technologies = set() | 
					
						
							|  |  |  |         for ingredient in self.ingredients: | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |             technologies |= required_technologies[ingredient]  # technologies that unlock the recipes | 
					
						
							| 
									
										
										
										
											2021-04-10 03:03:46 +02:00
										 |  |  |         return technologies | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:37:15 +02:00
										 |  |  |     def __hash__(self): | 
					
						
							|  |  |  |         return self.factorio_id | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-24 01:16:49 +02:00
										 |  |  |     def get_custom(self, world, allowed_packs: Set[str], player: int) -> CustomTechnology: | 
					
						
							|  |  |  |         return CustomTechnology(self, world, allowed_packs, player) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |     def useful(self) -> bool: | 
					
						
							|  |  |  |         return self.has_modifier or self.unlocks | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-24 01:16:49 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | class CustomTechnology(Technology): | 
					
						
							|  |  |  |     """A particularly configured Technology for a world.""" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, origin: Technology, world, allowed_packs: Set[str], player: int): | 
					
						
							|  |  |  |         ingredients = origin.ingredients & allowed_packs | 
					
						
							| 
									
										
										
										
											2021-10-14 10:20:56 -07:00
										 |  |  |         military_allowed = "military-science-pack" in allowed_packs \ | 
					
						
							| 
									
										
										
										
											2021-11-12 08:31:46 -08:00
										 |  |  |                            and ((ingredients & {"chemical-science-pack", "production-science-pack", "utility-science-pack"}) | 
					
						
							|  |  |  |                                 or origin.name == "rocket-silo") | 
					
						
							| 
									
										
										
										
											2021-04-24 01:16:49 +02:00
										 |  |  |         self.player = player | 
					
						
							| 
									
										
										
										
											2022-10-28 21:00:06 +02:00
										 |  |  |         if origin.name not in world.worlds[player].special_nodes: | 
					
						
							| 
									
										
										
										
											2021-10-14 10:20:56 -07:00
										 |  |  |             if military_allowed: | 
					
						
							|  |  |  |                 ingredients.add("military-science-pack") | 
					
						
							| 
									
										
										
										
											2021-04-24 01:16:49 +02:00
										 |  |  |             ingredients = list(ingredients) | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |             ingredients.sort()  # deterministic sample | 
					
						
							| 
									
										
										
										
											2021-04-24 01:16:49 +02:00
										 |  |  |             ingredients = world.random.sample(ingredients, world.random.randint(1, len(ingredients))) | 
					
						
							| 
									
										
										
										
											2021-10-14 10:20:56 -07:00
										 |  |  |         elif origin.name == "rocket-silo" and military_allowed: | 
					
						
							|  |  |  |             ingredients.add("military-science-pack") | 
					
						
							| 
									
										
										
										
											2021-04-24 01:16:49 +02:00
										 |  |  |         super(CustomTechnology, self).__init__(origin.name, ingredients, origin.factorio_id) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  | class Recipe(FactorioElement): | 
					
						
							| 
									
										
										
										
											2021-07-06 12:35:27 +02:00
										 |  |  |     name: str | 
					
						
							|  |  |  |     category: str | 
					
						
							|  |  |  |     ingredients: Dict[str, int] | 
					
						
							|  |  |  |     products: Dict[str, int] | 
					
						
							| 
									
										
										
										
											2021-07-24 01:41:41 +02:00
										 |  |  |     energy: float | 
					
						
							| 
									
										
										
										
											2021-07-06 12:35:27 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 01:41:41 +02:00
										 |  |  |     def __init__(self, name: str, category: str, ingredients: Dict[str, int], products: Dict[str, int], energy: float): | 
					
						
							| 
									
										
										
										
											2021-04-08 19:53:24 +02:00
										 |  |  |         self.name = name | 
					
						
							|  |  |  |         self.category = category | 
					
						
							| 
									
										
										
										
											2021-04-09 22:10:04 +02:00
										 |  |  |         self.ingredients = ingredients | 
					
						
							|  |  |  |         self.products = products | 
					
						
							| 
									
										
										
										
											2021-07-24 01:41:41 +02:00
										 |  |  |         self.energy = energy | 
					
						
							| 
									
										
										
										
											2021-04-09 22:10:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __repr__(self): | 
					
						
							|  |  |  |         return f"{self.__class__.__name__}({self.name})" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |     @property | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |     def crafting_machine(self) -> str: | 
					
						
							|  |  |  |         """cheapest crafting machine name able to run this recipe""" | 
					
						
							|  |  |  |         return machine_per_category[self.category] | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 22:10:04 +02:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def unlocking_technologies(self) -> Set[Technology]: | 
					
						
							|  |  |  |         """Unlocked by any of the returned technologies. Empty set indicates a starting recipe.""" | 
					
						
							|  |  |  |         return {technology_table[tech_name] for tech_name in recipe_sources.get(self.name, ())} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def recursive_unlocking_technologies(self) -> Set[Technology]: | 
					
						
							|  |  |  |         base = {technology_table[tech_name] for tech_name in recipe_sources.get(self.name, ())} | 
					
						
							|  |  |  |         for ingredient in self.ingredients: | 
					
						
							|  |  |  |             base |= required_technologies[ingredient] | 
					
						
							| 
									
										
										
										
											2021-11-26 02:37:15 +01:00
										 |  |  |         base |= required_technologies[self.crafting_machine] | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |         return base | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def rel_cost(self) -> float: | 
					
						
							|  |  |  |         ingredients = sum(self.ingredients.values()) | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |         return min(ingredients / amount for product, amount in self.products.items()) | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def base_cost(self) -> Dict[str, int]: | 
					
						
							|  |  |  |         ingredients = Counter() | 
					
						
							|  |  |  |         for ingredient, cost in self.ingredients.items(): | 
					
						
							|  |  |  |             if ingredient in all_product_sources: | 
					
						
							|  |  |  |                 for recipe in all_product_sources[ingredient]: | 
					
						
							| 
									
										
										
										
											2022-06-19 18:06:27 +02:00
										 |  |  |                     if recipe.ingredients: | 
					
						
							|  |  |  |                         ingredients.update({name: amount * cost / recipe.products[ingredient] for name, amount in | 
					
						
							|  |  |  |                                             recipe.base_cost.items()}) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         ingredients[ingredient] += recipe.energy * cost / recipe.products[ingredient] | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 ingredients[ingredient] += cost | 
					
						
							|  |  |  |         return ingredients | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 01:41:41 +02:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def total_energy(self) -> float: | 
					
						
							|  |  |  |         """Total required energy (crafting time) for single craft""" | 
					
						
							|  |  |  |         # TODO: multiply mining energy by 2 since drill has 0.5 speed | 
					
						
							|  |  |  |         total_energy = self.energy | 
					
						
							|  |  |  |         for ingredient, cost in self.ingredients.items(): | 
					
						
							|  |  |  |             if ingredient in all_product_sources: | 
					
						
							| 
									
										
										
										
											2021-11-06 11:49:03 -07:00
										 |  |  |                 selected_recipe_energy = float('inf') | 
					
						
							|  |  |  |                 for ingredient_recipe in all_product_sources[ingredient]: | 
					
						
							| 
									
										
										
										
											2021-07-24 01:41:41 +02:00
										 |  |  |                     craft_count = max((n for name, n in ingredient_recipe.products.items() if name == ingredient)) | 
					
						
							| 
									
										
										
										
											2021-11-06 11:49:03 -07:00
										 |  |  |                     recipe_energy = ingredient_recipe.total_energy / craft_count * cost | 
					
						
							|  |  |  |                     if recipe_energy < selected_recipe_energy: | 
					
						
							|  |  |  |                         selected_recipe_energy = recipe_energy | 
					
						
							|  |  |  |                 total_energy += selected_recipe_energy | 
					
						
							| 
									
										
										
										
											2021-07-24 01:41:41 +02:00
										 |  |  |         return total_energy | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  | class Machine(FactorioElement): | 
					
						
							|  |  |  |     def __init__(self, name, categories): | 
					
						
							|  |  |  |         self.name: str = name | 
					
						
							|  |  |  |         self.categories: set = categories | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-25 01:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  | recipe_sources: Dict[str, Set[str]] = {}  # recipe_name -> technology source | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | # recipes and technologies can share names in Factorio | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | for technology_name, data in sorted(techs_future.result().items()): | 
					
						
							| 
									
										
										
										
											2021-04-05 15:37:15 +02:00
										 |  |  |     current_ingredients = set(data["ingredients"]) | 
					
						
							| 
									
										
										
										
											2023-04-09 20:58:24 +02:00
										 |  |  |     technology = Technology(technology_name, current_ingredients, factorio_tech_id, | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |                             has_modifier=data["has_modifier"], unlocks=set(data["unlocks"])) | 
					
						
							| 
									
										
										
										
											2023-04-09 20:58:24 +02:00
										 |  |  |     factorio_tech_id += 1 | 
					
						
							| 
									
										
										
										
											2021-04-05 15:37:15 +02:00
										 |  |  |     tech_table[technology_name] = technology.factorio_id | 
					
						
							|  |  |  |     technology_table[technology_name] = technology | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |     for recipe_name in technology.unlocks: | 
					
						
							|  |  |  |         recipe_sources.setdefault(recipe_name, set()).add(technology_name) | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | del techs_future | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-06 21:11:58 +02:00
										 |  |  | recipes = {} | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  | all_product_sources: Dict[str, Set[Recipe]] = {"character": set()} | 
					
						
							| 
									
										
										
										
											2021-07-06 12:35:27 +02:00
										 |  |  | # add uranium mining to logic graph. TODO: add to automatic extractor for mod support | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | raw_recipes = recipes_future.result() | 
					
						
							|  |  |  | del recipes_future | 
					
						
							| 
									
										
										
										
											2022-06-19 18:06:27 +02:00
										 |  |  | for resource_name, resource_data in resources_future.result().items(): | 
					
						
							| 
									
										
										
										
											2022-06-19 21:52:42 +02:00
										 |  |  |     raw_recipes[f"mining-{resource_name}"] = { | 
					
						
							|  |  |  |         "ingredients": {resource_data["required_fluid"]: resource_data["fluid_amount"]} | 
					
						
							|  |  |  |         if "required_fluid" in resource_data else {}, | 
					
						
							|  |  |  |         "products": {data["name"]: data["amount"] for data in resource_data["products"].values()}, | 
					
						
							| 
									
										
										
										
											2022-06-19 18:06:27 +02:00
										 |  |  |         "energy": resource_data["mining_time"], | 
					
						
							|  |  |  |         "category": resource_data["category"] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | del resources_future | 
					
						
							| 
									
										
										
										
											2021-09-13 23:26:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-08 19:53:24 +02:00
										 |  |  | for recipe_name, recipe_data in raw_recipes.items(): | 
					
						
							|  |  |  |     # example: | 
					
						
							| 
									
										
										
										
											2021-07-06 12:35:27 +02:00
										 |  |  |     # "accumulator":{"ingredients":{"iron-plate":2,"battery":5},"products":{"accumulator":1},"category":"crafting"} | 
					
						
							| 
									
										
										
										
											2021-07-24 01:41:41 +02:00
										 |  |  |     # FIXME: add mining? | 
					
						
							|  |  |  |     recipe = Recipe(recipe_name, recipe_data["category"], recipe_data["ingredients"], | 
					
						
							|  |  |  |                     recipe_data["products"], recipe_data["energy"] if "energy" in recipe_data else 0) | 
					
						
							| 
									
										
										
										
											2021-07-06 13:06:45 +02:00
										 |  |  |     recipes[recipe_name] = recipe | 
					
						
							| 
									
										
										
										
											2021-07-06 12:35:27 +02:00
										 |  |  |     if set(recipe.products).isdisjoint( | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |             # prevents loop recipes like uranium centrifuging | 
					
						
							|  |  |  |             set(recipe.ingredients)) and ("empty-barrel" not in recipe.products or recipe.name == "empty-barrel") and \ | 
					
						
							|  |  |  |             not recipe_name.endswith("-reprocessing"): | 
					
						
							| 
									
										
										
										
											2021-04-09 22:10:04 +02:00
										 |  |  |         for product_name in recipe.products: | 
					
						
							| 
									
										
										
										
											2021-05-10 02:33:54 +02:00
										 |  |  |             all_product_sources.setdefault(product_name, set()).add(recipe) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | machines: Dict[str, Machine] = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | for name, categories in machines_future.result().items(): | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |     machine = Machine(name, set(categories)) | 
					
						
							|  |  |  |     machines[name] = machine | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-19 18:06:27 +02:00
										 |  |  | # add electric mining drill as a crafting machine to resolve basic-solid (mining) | 
					
						
							|  |  |  | machines["electric-mining-drill"] = Machine("electric-mining-drill", {"basic-solid"}) | 
					
						
							| 
									
										
										
										
											2022-05-21 02:36:06 +02:00
										 |  |  | machines["pumpjack"] = Machine("pumpjack", {"basic-fluid"}) | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  | machines["assembling-machine-1"].categories.add("crafting-with-fluid")  # mod enables this | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  | machines["character"].categories.add("basic-crafting")  # somehow this is implied and not exported | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | del machines_future | 
					
						
							| 
									
										
										
										
											2021-04-09 22:10:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # build requirements graph for all technology ingredients | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | all_ingredient_names: Set[str] = set() | 
					
						
							|  |  |  | for technology in technology_table.values(): | 
					
						
							|  |  |  |     all_ingredient_names |= technology.ingredients | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  | def unlock_just_tech(recipe: Recipe, _done) -> Set[Technology]: | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |     current_technologies = recipe.unlocking_technologies | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |     for ingredient_name in recipe.ingredients: | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |         current_technologies |= recursively_get_unlocking_technologies(ingredient_name, _done, | 
					
						
							|  |  |  |                                                                        unlock_func=unlock_just_tech) | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |     return current_technologies | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-25 01:31:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  | def unlock(recipe: Recipe, _done) -> Set[Technology]: | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |     current_technologies = recipe.unlocking_technologies | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |     for ingredient_name in recipe.ingredients: | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |         current_technologies |= recursively_get_unlocking_technologies(ingredient_name, _done, unlock_func=unlock) | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |     current_technologies |= required_category_technologies[recipe.category] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return current_technologies | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-25 01:31:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | def recursively_get_unlocking_technologies(ingredient_name, _done=None, unlock_func=unlock_just_tech) -> Set[ | 
					
						
							|  |  |  |     Technology]: | 
					
						
							| 
									
										
										
										
											2021-04-09 22:10:04 +02:00
										 |  |  |     if _done: | 
					
						
							|  |  |  |         if ingredient_name in _done: | 
					
						
							|  |  |  |             return set() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             _done.add(ingredient_name) | 
					
						
							|  |  |  |     else: | 
					
						
							| 
									
										
										
										
											2021-04-10 00:21:56 +02:00
										 |  |  |         _done = {ingredient_name} | 
					
						
							| 
									
										
										
										
											2021-05-10 02:33:54 +02:00
										 |  |  |     recipes = all_product_sources.get(ingredient_name) | 
					
						
							|  |  |  |     if not recipes: | 
					
						
							| 
									
										
										
										
											2021-04-09 22:10:04 +02:00
										 |  |  |         return set() | 
					
						
							| 
									
										
										
										
											2021-05-10 02:33:54 +02:00
										 |  |  |     current_technologies = set() | 
					
						
							|  |  |  |     for recipe in recipes: | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |         current_technologies |= unlock_func(recipe, _done) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-09 22:10:04 +02:00
										 |  |  |     return current_technologies | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  | required_machine_technologies: Dict[str, FrozenSet[Technology]] = {} | 
					
						
							|  |  |  | for ingredient_name in machines: | 
					
						
							|  |  |  |     required_machine_technologies[ingredient_name] = frozenset(recursively_get_unlocking_technologies(ingredient_name)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | logical_machines = {} | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  | machine_tech_cost = {} | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  | for machine in machines.values(): | 
					
						
							|  |  |  |     for category in machine.categories: | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |         current_cost, current_machine = machine_tech_cost.get(category, (10000, "character")) | 
					
						
							|  |  |  |         machine_cost = len(required_machine_technologies[machine.name]) | 
					
						
							|  |  |  |         if machine_cost < current_cost: | 
					
						
							|  |  |  |             machine_tech_cost[category] = machine_cost, machine.name | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | machine_per_category: Dict[str: str] = {} | 
					
						
							|  |  |  | for category, (cost, machine_name) in machine_tech_cost.items(): | 
					
						
							|  |  |  |     machine_per_category[category] = machine_name | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-18 13:40:02 +02:00
										 |  |  | del machine_tech_cost | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # required technologies to be able to craft recipes from a certain category | 
					
						
							|  |  |  | required_category_technologies: Dict[str, FrozenSet[FrozenSet[Technology]]] = {} | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  | for category_name, machine_name in machine_per_category.items(): | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |     techs = set() | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |     techs |= recursively_get_unlocking_technologies(machine_name) | 
					
						
							| 
									
										
										
										
											2021-05-19 06:52:53 +02:00
										 |  |  |     required_category_technologies[category_name] = frozenset(techs) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  | required_technologies: Dict[str, FrozenSet[Technology]] = Utils.KeyedDefaultDict(lambda ingredient_name: frozenset( | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |     recursively_get_unlocking_technologies(ingredient_name, unlock_func=unlock))) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-21 02:36:06 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-20 12:35:51 -08:00
										 |  |  | def get_rocket_requirements(silo_recipe: Recipe, part_recipe: Recipe, satellite_recipe: Recipe) -> Set[str]: | 
					
						
							| 
									
										
										
										
											2021-07-24 01:41:41 +02:00
										 |  |  |     techs = set() | 
					
						
							| 
									
										
										
										
											2021-07-25 22:12:03 +02:00
										 |  |  |     if silo_recipe: | 
					
						
							|  |  |  |         for ingredient in silo_recipe.ingredients: | 
					
						
							|  |  |  |             techs |= recursively_get_unlocking_technologies(ingredient) | 
					
						
							| 
									
										
										
										
											2021-07-24 01:41:41 +02:00
										 |  |  |     for ingredient in part_recipe.ingredients: | 
					
						
							| 
									
										
										
										
											2021-05-22 10:06:21 +02:00
										 |  |  |         techs |= recursively_get_unlocking_technologies(ingredient) | 
					
						
							| 
									
										
										
										
											2021-11-20 12:35:51 -08:00
										 |  |  |     if satellite_recipe: | 
					
						
							| 
									
										
										
										
											2021-11-20 16:27:17 -08:00
										 |  |  |         techs |= satellite_recipe.unlocking_technologies | 
					
						
							| 
									
										
										
										
											2021-11-20 12:35:51 -08:00
										 |  |  |         for ingredient in satellite_recipe.ingredients: | 
					
						
							|  |  |  |             techs |= recursively_get_unlocking_technologies(ingredient) | 
					
						
							| 
									
										
										
										
											2021-05-22 10:06:21 +02:00
										 |  |  |     return {tech.name for tech in techs} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-19 18:06:27 +02:00
										 |  |  | free_sample_exclusions: Set[str] = all_ingredient_names | {"rocket-part"} | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | # progressive technologies | 
					
						
							|  |  |  | # auto-progressive | 
					
						
							| 
									
										
										
										
											2021-08-02 19:27:43 +02:00
										 |  |  | progressive_rows: Dict[str, Union[List[str], Tuple[str, ...]]] = {} | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | progressive_incs = set() | 
					
						
							|  |  |  | for tech_name in tech_table: | 
					
						
							|  |  |  |     if tech_name.endswith("-1"): | 
					
						
							|  |  |  |         progressive_rows[tech_name] = [] | 
					
						
							|  |  |  |     elif tech_name[-2] == "-" and tech_name[-1] in string.digits: | 
					
						
							|  |  |  |         progressive_incs.add(tech_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | for root, progressive in progressive_rows.items(): | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |     seeking = root[:-1] + str(int(root[-1]) + 1) | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  |     while seeking in progressive_incs: | 
					
						
							|  |  |  |         progressive.append(seeking) | 
					
						
							|  |  |  |         progressive_incs.remove(seeking) | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |         seeking = seeking[:-1] + str(int(seeking[-1]) + 1) | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # make root entry the progressive name | 
					
						
							|  |  |  | for old_name in set(progressive_rows): | 
					
						
							|  |  |  |     prog_name = "progressive-" + old_name.rsplit("-", 1)[0] | 
					
						
							|  |  |  |     progressive_rows[prog_name] = tuple([old_name] + progressive_rows[old_name]) | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |     del (progressive_rows[old_name]) | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # no -1 start | 
					
						
							|  |  |  | base_starts = set() | 
					
						
							|  |  |  | for remnant in progressive_incs: | 
					
						
							|  |  |  |     if remnant[-1] == "2": | 
					
						
							|  |  |  |         base_starts.add(remnant[:-2]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | for root in base_starts: | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |     seeking = root + "-2" | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  |     progressive = [root] | 
					
						
							|  |  |  |     while seeking in progressive_incs: | 
					
						
							|  |  |  |         progressive.append(seeking) | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |         seeking = seeking[:-1] + str(int(seeking[-1]) + 1) | 
					
						
							|  |  |  |     progressive_rows["progressive-" + root] = tuple(progressive) | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # science packs | 
					
						
							| 
									
										
										
										
											2021-07-08 05:09:34 +02:00
										 |  |  | progressive_rows["progressive-science-pack"] = tuple(Options.MaxSciencePack.get_ordered_science_packs())[1:] | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # manual progressive | 
					
						
							| 
									
										
										
										
											2021-07-09 04:49:19 +02:00
										 |  |  | progressive_rows["progressive-processing"] = ( | 
					
						
							|  |  |  |     "steel-processing", | 
					
						
							|  |  |  |     "oil-processing", "sulfur-processing", "advanced-oil-processing", "coal-liquefaction", | 
					
						
							|  |  |  |     "uranium-processing", "kovarex-enrichment-process", "nuclear-fuel-reprocessing") | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | progressive_rows["progressive-rocketry"] = ("rocketry", "explosive-rocketry", "atomic-bomb") | 
					
						
							|  |  |  | progressive_rows["progressive-vehicle"] = ("automobilism", "tank", "spidertron") | 
					
						
							| 
									
										
										
										
											2021-08-01 02:51:20 +02:00
										 |  |  | progressive_rows["progressive-train-network"] = ("railway", "fluid-wagon", | 
					
						
							| 
									
										
										
										
											2021-08-02 19:12:42 +02:00
										 |  |  |                                                  "automated-rail-transportation", "rail-signals") | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | progressive_rows["progressive-engine"] = ("engine", "electric-engine") | 
					
						
							|  |  |  | progressive_rows["progressive-armor"] = ("heavy-armor", "modular-armor", "power-armor", "power-armor-mk2") | 
					
						
							|  |  |  | progressive_rows["progressive-personal-battery"] = ("battery-equipment", "battery-mk2-equipment") | 
					
						
							|  |  |  | progressive_rows["progressive-energy-shield"] = ("energy-shield-equipment", "energy-shield-mk2-equipment") | 
					
						
							|  |  |  | progressive_rows["progressive-wall"] = ("stone-wall", "gate") | 
					
						
							|  |  |  | progressive_rows["progressive-follower"] = ("defender", "distractor", "destroyer") | 
					
						
							|  |  |  | progressive_rows["progressive-inserter"] = ("fast-inserter", "stack-inserter") | 
					
						
							| 
									
										
										
										
											2021-08-02 19:12:42 +02:00
										 |  |  | progressive_rows["progressive-turret"] = ("gun-turret", "laser-turret") | 
					
						
							|  |  |  | progressive_rows["progressive-flamethrower"] = ("flamethrower",)  # leaving out flammables, as they do nothing | 
					
						
							| 
									
										
										
										
											2021-08-06 08:14:16 +02:00
										 |  |  | progressive_rows["progressive-personal-roboport-equipment"] = ("personal-roboport-equipment", | 
					
						
							|  |  |  |                                                                "personal-roboport-mk2-equipment") | 
					
						
							| 
									
										
										
										
											2023-04-09 20:58:24 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | sorted_rows = sorted(progressive_rows) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-02 19:12:42 +02:00
										 |  |  | # integrate into | 
					
						
							| 
									
										
										
										
											2021-08-02 19:27:43 +02:00
										 |  |  | source_target_mapping: Dict[str, str] = { | 
					
						
							|  |  |  |     "progressive-braking-force": "progressive-train-network", | 
					
						
							| 
									
										
										
										
											2021-08-02 19:12:42 +02:00
										 |  |  |     "progressive-inserter-capacity-bonus": "progressive-inserter", | 
					
						
							| 
									
										
										
										
											2021-08-03 01:14:20 +02:00
										 |  |  |     "progressive-refined-flammables": "progressive-flamethrower" | 
					
						
							| 
									
										
										
										
											2021-08-02 19:12:42 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | for source, target in source_target_mapping.items(): | 
					
						
							|  |  |  |     progressive_rows[target] += progressive_rows[source] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-12 18:05:46 +02:00
										 |  |  | base_tech_table = tech_table.copy()  # without progressive techs | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | base_technology_table = technology_table.copy() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | progressive_tech_table: Dict[str, int] = {} | 
					
						
							|  |  |  | progressive_technology_table: Dict[str, Technology] = {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-02 19:12:42 +02:00
										 |  |  | for root in sorted_rows: | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  |     progressive = progressive_rows[root] | 
					
						
							| 
									
										
										
										
											2022-03-26 01:12:54 +01:00
										 |  |  |     assert all(tech in tech_table for tech in progressive), "declared a progressive technology without base technology" | 
					
						
							| 
									
										
										
										
											2023-04-09 20:58:24 +02:00
										 |  |  |     factorio_tech_id += 1 | 
					
						
							|  |  |  |     progressive_technology = Technology(root, technology_table[progressive[0]].ingredients, factorio_tech_id, | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |                                         progressive, | 
					
						
							|  |  |  |                                         has_modifier=any(technology_table[tech].has_modifier for tech in progressive), | 
					
						
							|  |  |  |                                         unlocks=any(technology_table[tech].unlocks for tech in progressive)) | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  |     progressive_tech_table[root] = progressive_technology.factorio_id | 
					
						
							|  |  |  |     progressive_technology_table[root] = progressive_technology | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | tech_to_progressive_lookup: Dict[str, str] = {} | 
					
						
							|  |  |  | for technology in progressive_technology_table.values(): | 
					
						
							| 
									
										
										
										
											2021-08-02 19:12:42 +02:00
										 |  |  |     if technology.name not in source_target_mapping: | 
					
						
							|  |  |  |         for progressive in technology.progressive: | 
					
						
							|  |  |  |             tech_to_progressive_lookup[progressive] = technology.name | 
					
						
							| 
									
										
										
										
											2021-07-04 22:21:53 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | tech_table.update(progressive_tech_table) | 
					
						
							|  |  |  | technology_table.update(progressive_technology_table) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # techs that are never progressive | 
					
						
							|  |  |  | common_tech_table: Dict[str, int] = {tech_name: tech_id for tech_name, tech_id in base_tech_table.items() | 
					
						
							|  |  |  |                                      if tech_name not in progressive_tech_table} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  | useless_technologies: Set[str] = {tech_name for tech_name in common_tech_table | 
					
						
							|  |  |  |                                   if not technology_table[tech_name].useful()} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  | rel_cost = { | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  |     "wood": 10000, | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  |     "iron-ore": 1, | 
					
						
							|  |  |  |     "copper-ore": 1, | 
					
						
							|  |  |  |     "stone": 1, | 
					
						
							|  |  |  |     "crude-oil": 0.5, | 
					
						
							|  |  |  |     "water": 0.001, | 
					
						
							|  |  |  |     "coal": 1, | 
					
						
							|  |  |  |     "raw-fish": 1000, | 
					
						
							|  |  |  |     "steam": 0.01, | 
					
						
							|  |  |  |     "used-up-uranium-fuel-cell": 1000 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-18 13:40:02 +02:00
										 |  |  | exclusion_list: Set[str] = all_ingredient_names | {"rocket-part", "used-up-uranium-fuel-cell"} | 
					
						
							|  |  |  | fluids: Set[str] = set(fluids_future.result()) | 
					
						
							|  |  |  | del fluids_future | 
					
						
							| 
									
										
										
										
											2021-07-07 10:14:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-04 05:40:51 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 17:44:24 +02:00
										 |  |  | @Utils.cache_argsless | 
					
						
							|  |  |  | def get_science_pack_pools() -> Dict[str, Set[str]]: | 
					
						
							|  |  |  |     def get_estimated_difficulty(recipe: Recipe): | 
					
						
							|  |  |  |         base_ingredients = recipe.base_cost | 
					
						
							|  |  |  |         cost = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for ingredient_name, amount in base_ingredients.items(): | 
					
						
							|  |  |  |             cost += rel_cost.get(ingredient_name, 1) * amount | 
					
						
							|  |  |  |         return cost | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-26 02:37:15 +01:00
										 |  |  |     science_pack_pools: Dict[str, Set[str]] = {} | 
					
						
							| 
									
										
										
										
											2022-06-18 13:40:02 +02:00
										 |  |  |     already_taken = exclusion_list.copy() | 
					
						
							| 
									
										
										
										
											2021-07-09 17:44:24 +02:00
										 |  |  |     current_difficulty = 5 | 
					
						
							|  |  |  |     for science_pack in Options.MaxSciencePack.get_ordered_science_packs(): | 
					
						
							|  |  |  |         current = science_pack_pools[science_pack] = set() | 
					
						
							|  |  |  |         for name, recipe in recipes.items(): | 
					
						
							|  |  |  |             if (science_pack != "automation-science-pack" or not recipe.recursive_unlocking_technologies) \ | 
					
						
							|  |  |  |                     and get_estimated_difficulty(recipe) < current_difficulty: | 
					
						
							|  |  |  |                 current |= set(recipe.products) | 
					
						
							| 
									
										
										
										
											2022-06-18 13:40:02 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-13 23:26:45 +02:00
										 |  |  |         if science_pack == "automation-science-pack": | 
					
						
							| 
									
										
										
										
											2022-06-18 13:40:02 +02:00
										 |  |  |             # Can't handcraft automation science if fluids end up in its recipe, making the seed impossible. | 
					
						
							|  |  |  |             current -= fluids | 
					
						
							| 
									
										
										
										
											2021-11-25 09:44:01 -08:00
										 |  |  |         elif science_pack == "logistic-science-pack": | 
					
						
							| 
									
										
										
										
											2021-11-25 10:17:23 -08:00
										 |  |  |             current |= {"steam"} | 
					
						
							| 
									
										
										
										
											2022-06-18 13:40:02 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 17:44:24 +02:00
										 |  |  |         current -= already_taken | 
					
						
							|  |  |  |         already_taken |= current | 
					
						
							|  |  |  |         current_difficulty *= 2 | 
					
						
							| 
									
										
										
										
											2022-06-18 13:40:02 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 01:41:41 +02:00
										 |  |  |     return science_pack_pools | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | item_stack_sizes: Dict[str, int] = items_future.result() | 
					
						
							|  |  |  | non_stacking_items: Set[str] = {item for item, stack in item_stack_sizes.items() if stack == 1} | 
					
						
							|  |  |  | stacking_items: Set[str] = set(item_stack_sizes) - non_stacking_items | 
					
						
							| 
									
										
										
										
											2022-07-08 06:35:33 -07:00
										 |  |  | valid_ingredients: Set[str] = stacking_items | fluids | 
					
						
							| 
									
										
										
										
											2022-06-18 09:15:14 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | # cleanup async helpers | 
					
						
							|  |  |  | pool.shutdown() | 
					
						
							|  |  |  | del pool | 
					
						
							| 
									
										
										
										
											2023-04-09 20:58:24 +02:00
										 |  |  | del factorio_tech_id |