From 635897574f8eb504ef1fd3d30fd79769d074f73a Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 5 Apr 2021 15:37:15 +0200 Subject: [PATCH] clean up technology handling a bit --- FactorioClient.py | 2 +- worlds/__init__.py | 3 ++ worlds/factorio/Technologies.py | 66 ++++++++++++++++++++------------- worlds/factorio/__init__.py | 22 +++-------- 4 files changed, 50 insertions(+), 43 deletions(-) diff --git a/FactorioClient.py b/FactorioClient.py index 7e8282d7..ada9ce38 100644 --- a/FactorioClient.py +++ b/FactorioClient.py @@ -142,7 +142,7 @@ async def factorio_server_watcher(ctx: FactorioContext): while ctx.send_index < len(ctx.items_received): item_id = ctx.items_received[ctx.send_index].item if item_id not in lookup_id_to_name: - logging.error(f"Unknown item ID: {item_id}") + logging.error(f"Cannot send unknown item ID: {item_id}") else: item_name = lookup_id_to_name[item_id] factorio_server_logger.info(f"Sending {item_name} to Nauvis.") diff --git a/worlds/__init__.py b/worlds/__init__.py index 39080fec..937c40d5 100644 --- a/worlds/__init__.py +++ b/worlds/__init__.py @@ -9,6 +9,7 @@ from .alttp.Items import lookup_id_to_name as alttp from .hk.Items import lookup_id_to_name as hk from .factorio import Technologies lookup_any_item_id_to_name = {**alttp, **hk, **Technologies.lookup_id_to_name} +assert len(alttp) + len(hk) + len(Technologies.lookup_id_to_name) == len(lookup_any_item_id_to_name) lookup_any_item_name_to_id = {name: id for id, name in lookup_any_item_id_to_name.items()} @@ -16,6 +17,8 @@ from .alttp import Regions from .hk import Locations lookup_any_location_id_to_name = {**Regions.lookup_id_to_name, **Locations.lookup_id_to_name, **Technologies.lookup_id_to_name} +assert len(Regions.lookup_id_to_name) + len(Locations.lookup_id_to_name) + len(Technologies.lookup_id_to_name) == \ + len(lookup_any_location_id_to_name) lookup_any_location_name_to_id = {name: id for id, name in lookup_any_location_id_to_name.items()} diff --git a/worlds/factorio/Technologies.py b/worlds/factorio/Technologies.py index 169c8aaf..3bca7b33 100644 --- a/worlds/factorio/Technologies.py +++ b/worlds/factorio/Technologies.py @@ -1,44 +1,58 @@ # Factorio technologies are imported from a .json document in /data -from typing import Dict -import os +from typing import Dict, Set import json - import Utils -factorio_id = 2**17 +factorio_id = 2 ** 17 source_file = Utils.local_path("data", "factorio", "techs.json") with open(source_file) as f: raw = json.load(f) tech_table = {} +technology_table = {} +requirements = {} # tech_name -> Set[required_technologies] -requirements = {} -ingredients = {} -all_ingredients = set() -# TODO: export this dynamically, or filter it during export -starting_ingredient_recipes = {"automation-science-pack"} +class Technology(): # maybe make subclass of Location? + def __init__(self, name, ingredients): + self.name = name + global factorio_id + self.factorio_id = factorio_id + factorio_id += 1 + self.ingredients = ingredients + + def get_required_technologies(self): + requirements = set() + for ingredient in self.ingredients: + if ingredient in recipe_sources: # no source likely means starting item + requirements |= recipe_sources[ingredient] # technically any, not all, need to improve later + return requirements + + def __hash__(self): + return self.factorio_id + # recipes and technologies can share names in Factorio -for technology in sorted(raw): - data = raw[technology] - tech_table[technology] = factorio_id - factorio_id += 1 - if data["requires"]: - requirements[technology] = set(data["requires"]) - current_ingredients = set(data["ingredients"])-starting_ingredient_recipes - if current_ingredients: - all_ingredients |= current_ingredients - ingredients[technology] = {"recipe-"+ingredient for ingredient in current_ingredients} +for technology_name in sorted(raw): + data = raw[technology_name] -recipe_sources = {} + factorio_id += 1 + # not used yet + # if data["requires"]: + # requirements[technology] = set(data["requires"]) + current_ingredients = set(data["ingredients"]) + technology = Technology(technology_name, current_ingredients) + tech_table[technology_name] = technology.factorio_id + technology_table[technology_name] = technology + + +recipe_sources = {} # recipe_name -> technology source for technology, data in raw.items(): - recipe_source = all_ingredients & set(data["unlocks"]) - for recipe in recipe_source: - recipe_sources["recipe-"+recipe] = technology + for recipe in data["unlocks"]: + recipe_sources.setdefault(recipe, set()).add(technology) -all_ingredients_recipe = {"recipe-"+ingredient for ingredient in all_ingredients} -del(raw) -lookup_id_to_name: Dict[int, str] = {item_id: item_name for item_name, item_id in tech_table.items()} \ No newline at end of file + +del (raw) +lookup_id_to_name: Dict[int, str] = {item_id: item_name for item_name, item_id in tech_table.items()} diff --git a/worlds/factorio/__init__.py b/worlds/factorio/__init__.py index 3675705b..35fb6f21 100644 --- a/worlds/factorio/__init__.py +++ b/worlds/factorio/__init__.py @@ -2,7 +2,7 @@ import logging from BaseClasses import Region, Entrance, Location, MultiWorld, Item -from .Technologies import tech_table, requirements, ingredients, all_ingredients, recipe_sources, all_ingredients_recipe +from .Technologies import tech_table, requirements, recipe_sources, technology_table static_nodes = {"automation", "logistics"} @@ -30,11 +30,6 @@ def factorio_create_regions(world: MultiWorld, player: int): tech = Location(player, tech_name, tech_id, nauvis) nauvis.locations.append(tech) tech.game = "Factorio" - for ingredient in all_ingredients_recipe: # register science packs as events - ingredient_location = Location(player, ingredient, 0, nauvis) - ingredient_location.item = Item(ingredient, True, 0, player) - ingredient_location.event = ingredient_location.locked = True - menu.locations.append(ingredient_location) crash.connect(nauvis) world.regions += [menu, nauvis] @@ -42,18 +37,13 @@ def factorio_create_regions(world: MultiWorld, player: int): def set_rules(world: MultiWorld, player: int): if world.logic[player] != 'nologic': from worlds.generic import Rules - for tech_name in tech_table: - # vanilla layout, to be implemented - # rules = requirements.get(tech_name, set()) | ingredients.get(tech_name, set()) + for tech_name, technology in technology_table.items(): # loose nodes - rules = ingredients.get(tech_name, set()) + rules = technology.get_required_technologies() if rules: location = world.get_location(tech_name, player) Rules.set_rule(location, lambda state, rules=rules: all(state.has(rule, player) for rule in rules)) - for recipe, technology in recipe_sources.items(): - Rules.set_rule(world.get_location(recipe, player), lambda state, tech=technology: state.has(tech, player)) - - - world.completion_condition[player] = lambda state: all(state.has(ingredient, player) - for ingredient in all_ingredients_recipe) + # get all technologies + world.completion_condition[player] = lambda state: all(state.has(technology, player) + for technology in tech_table)