2021-04-10 01:03:46 +00:00
|
|
|
from __future__ import annotations
|
2021-04-01 09:40:58 +00:00
|
|
|
# Factorio technologies are imported from a .json document in /data
|
2021-07-04 20:21:53 +00:00
|
|
|
from typing import Dict, Set, FrozenSet, Tuple
|
2021-07-07 08:14:58 +00:00
|
|
|
from collections import Counter, defaultdict
|
2021-05-19 04:52:53 +00:00
|
|
|
import os
|
2021-04-01 09:40:58 +00:00
|
|
|
import json
|
2021-07-04 20:21:53 +00:00
|
|
|
import string
|
2021-05-22 08:06:21 +00:00
|
|
|
|
2021-04-01 09:40:58 +00:00
|
|
|
import Utils
|
2021-04-09 20:10:04 +00:00
|
|
|
import logging
|
2021-05-22 08:06:21 +00:00
|
|
|
import functools
|
2021-04-01 09:40:58 +00:00
|
|
|
|
2021-06-25 21:32:13 +00:00
|
|
|
from . import Options
|
|
|
|
|
2021-04-05 13:37:15 +00:00
|
|
|
factorio_id = 2 ** 17
|
2021-07-31 13:13:55 +00:00
|
|
|
source_folder = os.path.join(os.path.dirname(__file__), "data")
|
2021-05-19 04:52:53 +00:00
|
|
|
|
|
|
|
with open(os.path.join(source_folder, "techs.json")) as f:
|
2021-04-01 09:40:58 +00:00
|
|
|
raw = json.load(f)
|
2021-05-19 04:52:53 +00:00
|
|
|
with open(os.path.join(source_folder, "recipes.json")) as f:
|
2021-04-08 17:53:24 +00:00
|
|
|
raw_recipes = json.load(f)
|
2021-05-19 04:52:53 +00:00
|
|
|
with open(os.path.join(source_folder, "machines.json")) as f:
|
|
|
|
raw_machines = json.load(f)
|
2021-05-03 16:06:21 +00:00
|
|
|
tech_table: Dict[str, int] = {}
|
2021-04-23 23:16:49 +00:00
|
|
|
technology_table: Dict[str, Technology] = {}
|
2021-04-05 13:37:15 +00:00
|
|
|
|
2021-04-09 22:08:59 +00:00
|
|
|
always = lambda state: True
|
2021-04-05 13:37:15 +00:00
|
|
|
|
2021-04-10 01:03:46 +00:00
|
|
|
|
2021-05-19 04:52:53 +00:00
|
|
|
class FactorioElement():
|
|
|
|
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-07-04 20:21:53 +00:00
|
|
|
def __init__(self, name: str, ingredients: Set[str], factorio_id: int, progressive: Tuple[str] = ()):
|
2021-04-05 13:37:15 +00:00
|
|
|
self.name = name
|
|
|
|
self.factorio_id = factorio_id
|
|
|
|
self.ingredients = ingredients
|
2021-07-04 20:21:53 +00:00
|
|
|
self.progressive = progressive
|
2021-04-05 13:37:15 +00:00
|
|
|
|
2021-04-23 23:16:49 +00:00
|
|
|
def build_rule(self, player: int):
|
2021-04-09 20:10:04 +00:00
|
|
|
logging.debug(f"Building rules for {self.name}")
|
2021-04-09 22:08:59 +00:00
|
|
|
|
2021-05-22 08:06:21 +00:00
|
|
|
return lambda state, technologies=technologies: all(state.has(f"Automated {ingredient}", player)
|
|
|
|
for ingredient in self.ingredients)
|
2021-04-08 17:53:24 +00:00
|
|
|
|
2021-05-19 04:52:53 +00:00
|
|
|
def get_prior_technologies(self) -> Set[Technology]:
|
2021-04-10 01:03:46 +00:00
|
|
|
"""Get Technologies that have to precede this one to resolve tree connections."""
|
|
|
|
technologies = set()
|
|
|
|
for ingredient in self.ingredients:
|
2021-05-19 04:52:53 +00:00
|
|
|
technologies |= required_technologies[ingredient] # technologies that unlock the recipes
|
2021-04-10 01:03:46 +00:00
|
|
|
return technologies
|
|
|
|
|
2021-04-05 13:37:15 +00:00
|
|
|
def __hash__(self):
|
|
|
|
return self.factorio_id
|
2021-04-01 09:40:58 +00:00
|
|
|
|
2021-04-23 23:16:49 +00:00
|
|
|
def get_custom(self, world, allowed_packs: Set[str], player: int) -> CustomTechnology:
|
|
|
|
return CustomTechnology(self, world, allowed_packs, player)
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
self.player = player
|
2021-06-16 21:41:38 +00:00
|
|
|
if origin.name not in world.worlds[player].static_nodes:
|
2021-04-23 23:16:49 +00:00
|
|
|
ingredients = list(ingredients)
|
2021-05-19 04:52:53 +00:00
|
|
|
ingredients.sort() # deterministic sample
|
2021-04-23 23:16:49 +00:00
|
|
|
ingredients = world.random.sample(ingredients, world.random.randint(1, len(ingredients)))
|
|
|
|
super(CustomTechnology, self).__init__(origin.name, ingredients, origin.factorio_id)
|
|
|
|
|
|
|
|
|
2021-05-19 04:52:53 +00:00
|
|
|
class Recipe(FactorioElement):
|
2021-07-06 10:35:27 +00:00
|
|
|
name: str
|
|
|
|
category: str
|
|
|
|
ingredients: Dict[str, int]
|
|
|
|
products: Dict[str, int]
|
2021-07-23 23:41:41 +00:00
|
|
|
energy: float
|
2021-07-06 10:35:27 +00:00
|
|
|
|
2021-07-23 23:41:41 +00:00
|
|
|
def __init__(self, name: str, category: str, ingredients: Dict[str, int], products: Dict[str, int], energy: float):
|
2021-04-08 17:53:24 +00:00
|
|
|
self.name = name
|
|
|
|
self.category = category
|
2021-04-09 20:10:04 +00:00
|
|
|
self.ingredients = ingredients
|
|
|
|
self.products = products
|
2021-07-23 23:41:41 +00:00
|
|
|
self.energy = energy
|
2021-04-09 20:10:04 +00:00
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
return f"{self.__class__.__name__}({self.name})"
|
|
|
|
|
2021-05-19 04:52:53 +00:00
|
|
|
@property
|
2021-07-07 08:14:58 +00:00
|
|
|
def crafting_machine(self) -> str:
|
|
|
|
"""cheapest crafting machine name able to run this recipe"""
|
|
|
|
return machine_per_category[self.category]
|
2021-05-19 04:52:53 +00:00
|
|
|
|
2021-04-09 20:10:04 +00: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 08:14:58 +00: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]
|
|
|
|
return base
|
|
|
|
|
|
|
|
@property
|
|
|
|
def rel_cost(self) -> float:
|
|
|
|
ingredients = sum(self.ingredients.values())
|
|
|
|
return min(ingredients/amount for product, amount in self.products.items())
|
|
|
|
|
|
|
|
@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]:
|
|
|
|
ingredients.update({name: amount*cost/recipe.products[ingredient] for name, amount in recipe.base_cost.items()})
|
|
|
|
else:
|
|
|
|
ingredients[ingredient] += cost
|
|
|
|
return ingredients
|
2021-04-01 09:40:58 +00:00
|
|
|
|
2021-07-23 23:41:41 +00: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:
|
|
|
|
for ingredient_recipe in all_product_sources[ingredient]: # FIXME: this may select the wrong recipe
|
|
|
|
craft_count = max((n for name, n in ingredient_recipe.products.items() if name == ingredient))
|
|
|
|
total_energy += ingredient_recipe.total_energy / craft_count * cost
|
|
|
|
break
|
|
|
|
return total_energy
|
|
|
|
|
2021-05-19 04:52:53 +00:00
|
|
|
class Machine(FactorioElement):
|
|
|
|
def __init__(self, name, categories):
|
|
|
|
self.name: str = name
|
|
|
|
self.categories: set = categories
|
|
|
|
|
2021-06-24 23:31:48 +00:00
|
|
|
|
2021-04-01 09:40:58 +00:00
|
|
|
# recipes and technologies can share names in Factorio
|
2021-04-05 13:37:15 +00:00
|
|
|
for technology_name in sorted(raw):
|
|
|
|
data = raw[technology_name]
|
|
|
|
current_ingredients = set(data["ingredients"])
|
2021-04-23 23:16:49 +00:00
|
|
|
technology = Technology(technology_name, current_ingredients, factorio_id)
|
|
|
|
factorio_id += 1
|
2021-04-05 13:37:15 +00:00
|
|
|
tech_table[technology_name] = technology.factorio_id
|
|
|
|
technology_table[technology_name] = technology
|
|
|
|
|
2021-04-09 20:10:04 +00:00
|
|
|
recipe_sources: Dict[str, str] = {} # recipe_name -> technology source
|
2021-04-01 09:40:58 +00:00
|
|
|
|
|
|
|
for technology, data in raw.items():
|
2021-04-09 20:10:04 +00:00
|
|
|
for recipe_name in data["unlocks"]:
|
|
|
|
recipe_sources.setdefault(recipe_name, set()).add(technology)
|
2021-04-01 09:40:58 +00:00
|
|
|
|
2021-04-05 13:37:15 +00:00
|
|
|
del (raw)
|
2021-07-04 20:21:53 +00:00
|
|
|
|
2021-06-06 19:11:58 +00:00
|
|
|
recipes = {}
|
2021-05-19 04:52:53 +00:00
|
|
|
all_product_sources: Dict[str, Set[Recipe]] = {"character": set()}
|
2021-07-06 10:35:27 +00:00
|
|
|
# add uranium mining to logic graph. TODO: add to automatic extractor for mod support
|
2021-07-23 23:41:41 +00:00
|
|
|
raw_recipes["uranium-ore"] = {"ingredients": {"sulfuric-acid": 1}, "products": {"uranium-ore": 1}, "category": "mining", "energy": 2}
|
2021-07-06 10:35:27 +00:00
|
|
|
|
2021-04-08 17:53:24 +00:00
|
|
|
for recipe_name, recipe_data in raw_recipes.items():
|
|
|
|
# example:
|
2021-07-06 10:35:27 +00:00
|
|
|
# "accumulator":{"ingredients":{"iron-plate":2,"battery":5},"products":{"accumulator":1},"category":"crafting"}
|
2021-07-23 23:41:41 +00: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 11:06:45 +00:00
|
|
|
recipes[recipe_name] = recipe
|
2021-07-06 10:35:27 +00:00
|
|
|
if set(recipe.products).isdisjoint(
|
2021-07-07 08:14:58 +00: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 20:10:04 +00:00
|
|
|
for product_name in recipe.products:
|
2021-05-10 00:33:54 +00:00
|
|
|
all_product_sources.setdefault(product_name, set()).add(recipe)
|
|
|
|
|
2021-05-19 04:52:53 +00:00
|
|
|
del (raw_recipes)
|
|
|
|
|
|
|
|
machines: Dict[str, Machine] = {}
|
|
|
|
|
|
|
|
for name, categories in raw_machines.items():
|
|
|
|
machine = Machine(name, set(categories))
|
|
|
|
machines[name] = machine
|
|
|
|
|
2021-07-06 10:35:27 +00:00
|
|
|
# add electric mining drill as a crafting machine to resolve uranium-ore
|
|
|
|
machines["electric-mining-drill"] = Machine("electric-mining-drill", {"mining"})
|
2021-07-07 08:14:58 +00:00
|
|
|
machines["assembling-machine-1"].categories.add("crafting-with-fluid") # mod enables this
|
|
|
|
machines["character"].categories.add("basic-crafting") # somehow this is implied and not exported
|
2021-05-19 04:52:53 +00:00
|
|
|
del (raw_machines)
|
2021-04-09 20:10:04 +00: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 04:52:53 +00:00
|
|
|
def unlock_just_tech(recipe: Recipe, _done) -> Set[Technology]:
|
2021-07-07 08:14:58 +00:00
|
|
|
current_technologies = recipe.unlocking_technologies
|
2021-05-19 04:52:53 +00:00
|
|
|
for ingredient_name in recipe.ingredients:
|
2021-07-07 08:14:58 +00:00
|
|
|
current_technologies |= recursively_get_unlocking_technologies(ingredient_name, _done,
|
|
|
|
unlock_func=unlock_just_tech)
|
2021-05-19 04:52:53 +00:00
|
|
|
return current_technologies
|
|
|
|
|
2021-06-24 23:31:48 +00:00
|
|
|
|
2021-05-19 04:52:53 +00:00
|
|
|
def unlock(recipe: Recipe, _done) -> Set[Technology]:
|
2021-07-07 08:14:58 +00:00
|
|
|
current_technologies = recipe.unlocking_technologies
|
2021-05-19 04:52:53 +00:00
|
|
|
for ingredient_name in recipe.ingredients:
|
2021-07-07 08:14:58 +00:00
|
|
|
current_technologies |= recursively_get_unlocking_technologies(ingredient_name, _done, unlock_func=unlock)
|
2021-05-19 04:52:53 +00:00
|
|
|
current_technologies |= required_category_technologies[recipe.category]
|
|
|
|
|
|
|
|
return current_technologies
|
|
|
|
|
2021-06-24 23:31:48 +00:00
|
|
|
|
|
|
|
def recursively_get_unlocking_technologies(ingredient_name, _done=None, unlock_func=unlock_just_tech) -> Set[
|
|
|
|
Technology]:
|
2021-04-09 20:10:04 +00:00
|
|
|
if _done:
|
|
|
|
if ingredient_name in _done:
|
|
|
|
return set()
|
|
|
|
else:
|
|
|
|
_done.add(ingredient_name)
|
|
|
|
else:
|
2021-04-09 22:21:56 +00:00
|
|
|
_done = {ingredient_name}
|
2021-05-10 00:33:54 +00:00
|
|
|
recipes = all_product_sources.get(ingredient_name)
|
|
|
|
if not recipes:
|
2021-04-09 20:10:04 +00:00
|
|
|
return set()
|
2021-05-10 00:33:54 +00:00
|
|
|
current_technologies = set()
|
|
|
|
for recipe in recipes:
|
2021-05-19 04:52:53 +00:00
|
|
|
current_technologies |= unlock_func(recipe, _done)
|
|
|
|
|
2021-04-09 20:10:04 +00:00
|
|
|
return current_technologies
|
|
|
|
|
|
|
|
|
2021-05-19 04:52:53 +00: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 08:14:58 +00:00
|
|
|
machine_tech_cost = {}
|
2021-05-19 04:52:53 +00:00
|
|
|
for machine in machines.values():
|
|
|
|
for category in machine.categories:
|
2021-07-07 08:14:58 +00: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
|
|
|
|
|
|
|
|
del (machine_tech_cost)
|
2021-05-19 04:52:53 +00:00
|
|
|
|
|
|
|
# required technologies to be able to craft recipes from a certain category
|
|
|
|
required_category_technologies: Dict[str, FrozenSet[FrozenSet[Technology]]] = {}
|
2021-07-07 08:14:58 +00:00
|
|
|
for category_name, machine_name in machine_per_category.items():
|
2021-05-19 04:52:53 +00:00
|
|
|
techs = set()
|
2021-07-07 08:14:58 +00:00
|
|
|
techs |= recursively_get_unlocking_technologies(machine_name)
|
2021-05-19 04:52:53 +00:00
|
|
|
required_category_technologies[category_name] = frozenset(techs)
|
|
|
|
|
2021-07-07 08:14:58 +00:00
|
|
|
required_technologies: Dict[str, FrozenSet[Technology]] = Utils.KeyedDefaultDict(lambda ingredient_name : frozenset(
|
|
|
|
recursively_get_unlocking_technologies(ingredient_name, unlock_func=unlock)))
|
|
|
|
|
2021-05-19 04:52:53 +00:00
|
|
|
|
2021-04-09 20:10:04 +00:00
|
|
|
advancement_technologies: Set[str] = set()
|
2021-07-07 08:14:58 +00:00
|
|
|
for ingredient_name in all_ingredient_names:
|
|
|
|
technologies = required_technologies[ingredient_name]
|
2021-04-09 20:10:04 +00:00
|
|
|
advancement_technologies |= {technology.name for technology in technologies}
|
2021-05-22 08:06:21 +00:00
|
|
|
|
2021-06-24 23:31:48 +00:00
|
|
|
|
2021-05-22 08:06:21 +00:00
|
|
|
@functools.lru_cache(10)
|
2021-07-23 23:41:41 +00:00
|
|
|
def get_rocket_requirements(silo_recipe: Recipe, part_recipe: Recipe) -> Set[str]:
|
|
|
|
techs = set()
|
|
|
|
for ingredient in silo_recipe.ingredients:
|
|
|
|
techs |= recursively_get_unlocking_technologies(ingredient)
|
|
|
|
for ingredient in part_recipe.ingredients:
|
2021-05-22 08:06:21 +00:00
|
|
|
techs |= recursively_get_unlocking_technologies(ingredient)
|
|
|
|
return {tech.name for tech in techs}
|
|
|
|
|
|
|
|
|
2021-06-24 23:31:48 +00:00
|
|
|
free_sample_blacklist = all_ingredient_names | {"rocket-part"}
|
|
|
|
|
2021-05-22 08:06:21 +00:00
|
|
|
rocket_recipes = {
|
|
|
|
Options.MaxSciencePack.option_space_science_pack:
|
|
|
|
{"rocket-control-unit": 10, "low-density-structure": 10, "rocket-fuel": 10},
|
|
|
|
Options.MaxSciencePack.option_utility_science_pack:
|
|
|
|
{"speed-module": 10, "steel-plate": 10, "solid-fuel": 10},
|
|
|
|
Options.MaxSciencePack.option_production_science_pack:
|
|
|
|
{"speed-module": 10, "steel-plate": 10, "solid-fuel": 10},
|
|
|
|
Options.MaxSciencePack.option_chemical_science_pack:
|
|
|
|
{"advanced-circuit": 10, "steel-plate": 10, "solid-fuel": 10},
|
|
|
|
Options.MaxSciencePack.option_military_science_pack:
|
|
|
|
{"defender-capsule": 10, "stone-wall": 10, "coal": 10},
|
|
|
|
Options.MaxSciencePack.option_logistic_science_pack:
|
|
|
|
{"electronic-circuit": 10, "stone-brick": 10, "coal": 10},
|
|
|
|
Options.MaxSciencePack.option_automation_science_pack:
|
|
|
|
{"copper-cable": 10, "iron-plate": 10, "wood": 10}
|
2021-06-24 23:31:48 +00:00
|
|
|
}
|
2021-07-07 08:14:58 +00:00
|
|
|
|
|
|
|
advancement_technologies |= {tech.name for tech in required_technologies["rocket-silo"]}
|
2021-07-04 20:21:53 +00:00
|
|
|
|
|
|
|
# progressive technologies
|
|
|
|
# auto-progressive
|
|
|
|
progressive_rows = {}
|
|
|
|
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():
|
|
|
|
seeking = root[:-1]+str(int(root[-1])+1)
|
|
|
|
while seeking in progressive_incs:
|
|
|
|
progressive.append(seeking)
|
|
|
|
progressive_incs.remove(seeking)
|
|
|
|
seeking = seeking[:-1]+str(int(seeking[-1])+1)
|
|
|
|
|
|
|
|
# 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])
|
|
|
|
del(progressive_rows[old_name])
|
|
|
|
|
|
|
|
# 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:
|
|
|
|
seeking = root+"-2"
|
|
|
|
progressive = [root]
|
|
|
|
while seeking in progressive_incs:
|
|
|
|
progressive.append(seeking)
|
|
|
|
seeking = seeking[:-1]+str(int(seeking[-1])+1)
|
|
|
|
progressive_rows["progressive-"+root] = tuple(progressive)
|
|
|
|
|
|
|
|
# science packs
|
2021-07-08 03:09:34 +00:00
|
|
|
progressive_rows["progressive-science-pack"] = tuple(Options.MaxSciencePack.get_ordered_science_packs())[1:]
|
2021-07-04 20:21:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
# manual progressive
|
2021-07-09 02:49:19 +00: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 20:21:53 +00:00
|
|
|
progressive_rows["progressive-rocketry"] = ("rocketry", "explosive-rocketry", "atomic-bomb")
|
|
|
|
progressive_rows["progressive-vehicle"] = ("automobilism", "tank", "spidertron")
|
|
|
|
progressive_rows["progressive-train-network"] = ("railway", "fluid-wagon", "automated-rail-transportation", "rail-signals")
|
|
|
|
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-07-12 16:05:46 +00:00
|
|
|
base_tech_table = tech_table.copy() # without progressive techs
|
2021-07-04 20:21:53 +00:00
|
|
|
base_technology_table = technology_table.copy()
|
|
|
|
|
|
|
|
progressive_tech_table: Dict[str, int] = {}
|
|
|
|
progressive_technology_table: Dict[str, Technology] = {}
|
|
|
|
|
|
|
|
for root in sorted(progressive_rows):
|
|
|
|
progressive = progressive_rows[root]
|
|
|
|
assert all(tech in tech_table for tech in progressive)
|
|
|
|
factorio_id += 1
|
|
|
|
progressive_technology = Technology(root, technology_table[progressive_rows[root][0]].ingredients, factorio_id,
|
|
|
|
progressive)
|
|
|
|
progressive_tech_table[root] = progressive_technology.factorio_id
|
|
|
|
progressive_technology_table[root] = progressive_technology
|
|
|
|
if any(tech in advancement_technologies for tech in progressive):
|
|
|
|
advancement_technologies.add(root)
|
|
|
|
|
|
|
|
tech_to_progressive_lookup: Dict[str, str] = {}
|
|
|
|
for technology in progressive_technology_table.values():
|
|
|
|
for progressive in technology.progressive:
|
|
|
|
tech_to_progressive_lookup[progressive] = technology.name
|
|
|
|
|
|
|
|
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-07-07 08:14:58 +00:00
|
|
|
lookup_id_to_name: Dict[int, str] = {item_id: item_name for item_name, item_id in tech_table.items()}
|
|
|
|
|
|
|
|
rel_cost = {
|
|
|
|
"wood" : 10000,
|
|
|
|
"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
|
|
|
|
}
|
|
|
|
|
|
|
|
# forbid liquids for now, TODO: allow a single liquid per assembler
|
|
|
|
blacklist = all_ingredient_names | {"rocket-part", "crude-oil", "water", "sulfuric-acid", "petroleum-gas", "light-oil",
|
|
|
|
"heavy-oil", "lubricant", "steam"}
|
|
|
|
|
2021-07-09 15:44:24 +00: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
|
|
|
|
|
|
|
|
|
|
|
|
science_pack_pools = {}
|
|
|
|
already_taken = blacklist.copy()
|
|
|
|
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)
|
|
|
|
current -= already_taken
|
|
|
|
already_taken |= current
|
|
|
|
current_difficulty *= 2
|
2021-07-23 23:41:41 +00:00
|
|
|
return science_pack_pools
|