Some optimizations

This commit is contained in:
Fabian Dill 2021-07-09 17:44:24 +02:00
parent 2600e9a805
commit 4c7ef593be
4 changed files with 105 additions and 89 deletions

View File

@ -672,11 +672,12 @@ def create_playthrough(world):
pathpairs = zip_longest(pathsiter, pathsiter) pathpairs = zip_longest(pathsiter, pathsiter)
return list(pathpairs) return list(pathpairs)
world.spoiler.paths = dict() world.spoiler.paths = {}
for player in range(1, world.players + 1): topology_worlds = (player for player in world.player_ids if world.worlds[player].topology_present)
for player in topology_worlds:
world.spoiler.paths.update( world.spoiler.paths.update(
{str(location): get_path(state, location.parent_region) for sphere in collection_spheres for location in {str(location): get_path(state, location.parent_region) for sphere in collection_spheres for location in
sphere if location.player == player and world.worlds[player].topology_present}) sphere if location.player == player})
if player in world.alttp_player_ids: if player in world.alttp_player_ids:
for path in dict(world.spoiler.paths).values(): for path in dict(world.spoiler.paths).values():
if any(exit == 'Pyramid Fairy' for (_, exit) in path): if any(exit == 'Pyramid Fairy' for (_, exit) in path):

142
Utils.py
View File

@ -69,6 +69,21 @@ def parse_player_names(names, players, teams):
return ret return ret
def cache_argsless(function):
if function.__code__.co_argcount:
raise Exception("Can only cache 0 argument functions with this cache.")
result = sentinel = object()
def _wrap():
nonlocal result
if result is sentinel:
result = function()
return result
return _wrap
def is_bundled() -> bool: def is_bundled() -> bool:
return getattr(sys, 'frozen', False) return getattr(sys, 'frozen', False)
@ -132,7 +147,7 @@ def close_console():
parse_yaml = safe_load parse_yaml = safe_load
unsafe_parse_yaml = functools.partial(load, Loader=Loader) unsafe_parse_yaml = functools.partial(load, Loader=Loader)
@cache_argsless
def get_public_ipv4() -> str: def get_public_ipv4() -> str:
import socket import socket
import urllib.request import urllib.request
@ -148,7 +163,7 @@ def get_public_ipv4() -> str:
pass # we could be offline, in a local game, so no point in erroring out pass # we could be offline, in a local game, so no point in erroring out
return ip return ip
@cache_argsless
def get_public_ipv6() -> str: def get_public_ipv6() -> str:
import socket import socket
import urllib.request import urllib.request
@ -161,70 +176,68 @@ def get_public_ipv6() -> str:
pass # we could be offline, in a local game, or ipv6 may not be available pass # we could be offline, in a local game, or ipv6 may not be available
return ip return ip
@cache_argsless
def get_default_options() -> dict: def get_default_options() -> dict:
if not hasattr(get_default_options, "options"): # Refer to host.yaml for comments as to what all these options mean.
# Refer to host.yaml for comments as to what all these options mean. options = {
options = { "general_options": {
"general_options": { "output_path": "output",
"output_path": "output", },
}, "factorio_options": {
"factorio_options": { "executable": "factorio\\bin\\x64\\factorio",
"executable": "factorio\\bin\\x64\\factorio", },
}, "lttp_options": {
"lttp_options": { "rom_file": "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc",
"rom_file": "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc", "sni": "SNI",
"sni": "SNI", "rom_start": True,
"rom_start": True,
}, },
"server_options": { "server_options": {
"host": None, "host": None,
"port": 38281, "port": 38281,
"password": None, "password": None,
"multidata": None, "multidata": None,
"savefile": None, "savefile": None,
"disable_save": False, "disable_save": False,
"loglevel": "info", "loglevel": "info",
"server_password": None, "server_password": None,
"disable_item_cheat": False, "disable_item_cheat": False,
"location_check_points": 1, "location_check_points": 1,
"hint_cost": 10, "hint_cost": 10,
"forfeit_mode": "goal", "forfeit_mode": "goal",
"remaining_mode": "goal", "remaining_mode": "goal",
"auto_shutdown": 0, "auto_shutdown": 0,
"compatibility": 2, "compatibility": 2,
"log_network": 0 "log_network": 0
}, },
"multi_mystery_options": { "multi_mystery_options": {
"teams": 1, "teams": 1,
"enemizer_path": "EnemizerCLI/EnemizerCLI.Core.exe", "enemizer_path": "EnemizerCLI/EnemizerCLI.Core.exe",
"player_files_path": "Players", "player_files_path": "Players",
"players": 0, "players": 0,
"weights_file_path": "weights.yaml", "weights_file_path": "weights.yaml",
"meta_file_path": "meta.yaml", "meta_file_path": "meta.yaml",
"pre_roll": False, "pre_roll": False,
"create_spoiler": 1, "create_spoiler": 1,
"zip_roms": 0, "zip_roms": 0,
"zip_diffs": 2, "zip_diffs": 2,
"zip_apmcs": 1, "zip_apmcs": 1,
"zip_spoiler": 0, "zip_spoiler": 0,
"zip_multidata": 1, "zip_multidata": 1,
"zip_format": 1, "zip_format": 1,
"glitch_triforce_room": 1, "glitch_triforce_room": 1,
"race": 0, "race": 0,
"cpu_threads": 0, "cpu_threads": 0,
"max_attempts": 0, "max_attempts": 0,
"take_first_working": False, "take_first_working": False,
"keep_all_seeds": False, "keep_all_seeds": False,
"log_output_path": "Output Logs", "log_output_path": "Output Logs",
"log_level": None, "log_level": None,
"plando_options": "bosses", "plando_options": "bosses",
}
} }
}
get_default_options.options = options return options
return get_default_options.options
blacklisted_options = {"multi_mystery_options.cpu_threads", blacklisted_options = {"multi_mystery_options.cpu_threads",
@ -254,7 +267,7 @@ def update_options(src: dict, dest: dict, filename: str, keys: list) -> dict:
dest[key] = update_options(value, dest[key], filename, new_keys) dest[key] = update_options(value, dest[key], filename, new_keys)
return dest return dest
@cache_argsless
def get_options() -> dict: def get_options() -> dict:
if not hasattr(get_options, "options"): if not hasattr(get_options, "options"):
locations = ("options.yaml", "host.yaml", locations = ("options.yaml", "host.yaml",
@ -368,7 +381,7 @@ def get_adjuster_settings(romfile: str) -> typing.Tuple[str, bool]:
return romfile, adjusted return romfile, adjusted
return romfile, False return romfile, False
@cache_argsless
def get_unique_identifier(): def get_unique_identifier():
uuid = persistent_load().get("client", {}).get("uuid", None) uuid = persistent_load().get("client", {}).get("uuid", None)
if uuid: if uuid:
@ -407,6 +420,7 @@ def restricted_loads(s):
"""Helper function analogous to pickle.loads().""" """Helper function analogous to pickle.loads()."""
return RestrictedUnpickler(io.BytesIO(s)).load() return RestrictedUnpickler(io.BytesIO(s)).load()
class KeyedDefaultDict(collections.defaultdict): class KeyedDefaultDict(collections.defaultdict):
def __missing__(self, key): def __missing__(self, key):
self[key] = value = self.default_factory(key) self[key] = value = self.default_factory(key)

View File

@ -393,27 +393,27 @@ rel_cost = {
blacklist = all_ingredient_names | {"rocket-part", "crude-oil", "water", "sulfuric-acid", "petroleum-gas", "light-oil", blacklist = all_ingredient_names | {"rocket-part", "crude-oil", "water", "sulfuric-acid", "petroleum-gas", "light-oil",
"heavy-oil", "lubricant", "steam"} "heavy-oil", "lubricant", "steam"}
@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
def get_estimated_difficulty(recipe: Recipe): for ingredient_name, amount in base_ingredients.items():
base_ingredients = recipe.base_cost cost += rel_cost.get(ingredient_name, 1) * amount
cost = 0 return cost
for ingredient_name, amount in base_ingredients.items():
if ingredient_name not in rel_cost:
raise Exception((recipe.name, ingredient_name))
cost += rel_cost.get(ingredient_name, 1) * amount
return cost
science_pack_pools = {} science_pack_pools = {}
already_taken = blacklist.copy() already_taken = blacklist.copy()
current_difficulty = 5 current_difficulty = 5
for science_pack in Options.MaxSciencePack.get_ordered_science_packs(): for science_pack in Options.MaxSciencePack.get_ordered_science_packs():
current = science_pack_pools[science_pack] = set() current = science_pack_pools[science_pack] = set()
for name, recipe in recipes.items(): for name, recipe in recipes.items():
if (science_pack != "automation-science-pack" or not recipe.recursive_unlocking_technologies) \ if (science_pack != "automation-science-pack" or not recipe.recursive_unlocking_technologies) \
and get_estimated_difficulty(recipe) < current_difficulty: and get_estimated_difficulty(recipe) < current_difficulty:
current |= set(recipe.products) current |= set(recipe.products)
current -= already_taken current -= already_taken
already_taken |= current already_taken |= current
current_difficulty *= 2 current_difficulty *= 2
return science_pack_pools

View File

@ -4,7 +4,7 @@ from BaseClasses import Region, Entrance, Location, Item
from .Technologies import base_tech_table, recipe_sources, base_technology_table, advancement_technologies, \ from .Technologies import base_tech_table, recipe_sources, base_technology_table, advancement_technologies, \
all_ingredient_names, required_technologies, get_rocket_requirements, rocket_recipes, \ all_ingredient_names, required_technologies, get_rocket_requirements, rocket_recipes, \
progressive_technology_table, common_tech_table, tech_to_progressive_lookup, progressive_tech_table, \ progressive_technology_table, common_tech_table, tech_to_progressive_lookup, progressive_tech_table, \
science_pack_pools, Recipe, recipes, technology_table get_science_pack_pools, Recipe, recipes, technology_table
from .Shapes import get_shapes from .Shapes import get_shapes
from .Mod import generate_mod from .Mod import generate_mod
from .Options import factorio_options from .Options import factorio_options
@ -127,6 +127,7 @@ class Factorio(World):
def set_custom_recipes(self): def set_custom_recipes(self):
original_rocket_part = recipes["rocket-part"] original_rocket_part = recipes["rocket-part"]
science_pack_pools = get_science_pack_pools()
valid_pool = sorted(science_pack_pools[self.world.max_science_pack[self.player].get_max_pack()]) valid_pool = sorted(science_pack_pools[self.world.max_science_pack[self.player].get_max_pack()])
self.world.random.shuffle(valid_pool) self.world.random.shuffle(valid_pool)
self.custom_recipes = {"rocket-part": Recipe("rocket-part", original_rocket_part.category, self.custom_recipes = {"rocket-part": Recipe("rocket-part", original_rocket_part.category,