From 7907838c243acc7f24029a5ca834d79e5c5a0109 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 6 Jun 2021 20:26:40 +0200 Subject: [PATCH] Factorio: Revamp Tech Tree Layouts --- Options.py | 12 ++- playerSettings.yaml | 10 ++- worlds/factorio/Shapes.py | 176 +++++++++++++++++++++++++++++--------- 3 files changed, 154 insertions(+), 44 deletions(-) diff --git a/Options.py b/Options.py index 09274a4a..ca653bd3 100644 --- a/Options.py +++ b/Options.py @@ -311,8 +311,16 @@ class TechTreeLayout(Choice): option_single = 0 option_small_diamonds = 1 option_medium_diamonds = 2 - option_pyramid = 3 - option_funnel = 4 + option_large_diamonds = 3 + option_small_pyramids = 4 + option_medium_pyramids = 5 + option_large_pyramids = 6 + option_small_funnels = 7 + option_medium_funnels = 8 + option_large_funnels = 9 + option_funnels = 4 + alias_pyramid = 6 + alias_funnel = 9 default = 0 diff --git a/playerSettings.yaml b/playerSettings.yaml index 26d83b9e..7ad7c495 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -25,7 +25,6 @@ name: YourName{number} # Your name in-game. Spaces will be replaced with undersc #{NUMBER} will be replaced with the counter value of the name if the counter value is greater than 1. game: A Link to the Past: 1 - Hollow Knight: 1 Factorio: 1 Minecraft: 1 # Shared Options supported by all games: @@ -49,8 +48,13 @@ tech_tree_layout: single: 1 small_diamonds: 1 medium_diamonds: 1 - pyramid: 1 - funnel: 1 + large_diamonds: 1 + small_pyramids: 1 + medium_pyramids: 1 + large_pyramids: 1 + small_funnels: 1 + medium_funnels: 1 + large_funnels: 1 max_science_pack: automation_science_pack: 0 logistic_science_pack: 0 diff --git a/worlds/factorio/Shapes.py b/worlds/factorio/Shapes.py index d85cdd5b..2bde14eb 100644 --- a/worlds/factorio/Shapes.py +++ b/worlds/factorio/Shapes.py @@ -2,21 +2,29 @@ from typing import Dict, List, Set from BaseClasses import MultiWorld from Options import TechTreeLayout -from worlds.factorio.Technologies import technology_table + +funnel_layers = {TechTreeLayout.option_small_funnels: 3, + TechTreeLayout.option_medium_funnels: 4, + TechTreeLayout.option_large_funnels: 5} + +funnel_slice_sizes = {TechTreeLayout.option_small_funnels: 6, + TechTreeLayout.option_medium_funnels: 10, + TechTreeLayout.option_large_funnels: 15} def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]: prerequisites: Dict[str, Set[str]] = {} layout = world.tech_tree_layout[player].value custom_technologies = world.custom_data[player]["custom_technologies"] + tech_names: List[str] = list(set(custom_technologies) - world._static_nodes) + tech_names.sort() + world.random.shuffle(tech_names) + if layout == TechTreeLayout.option_small_diamonds: slice_size = 4 - tech_names: List[str] = list(set(custom_technologies) - world._static_nodes) - tech_names.sort() - world.random.shuffle(tech_names) while len(tech_names) > slice_size: slice = tech_names[:slice_size] tech_names = tech_names[slice_size:] - slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients)) + slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].get_prior_technologies())) diamond_0, diamond_1, diamond_2, diamond_3 = slice # 0 | @@ -24,15 +32,13 @@ def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]: # 3 V prerequisites[diamond_3] = {diamond_1, diamond_2} prerequisites[diamond_2] = prerequisites[diamond_1] = {diamond_0} + elif layout == TechTreeLayout.option_medium_diamonds: slice_size = 9 - tech_names: List[str] = list(set(custom_technologies) - world._static_nodes) - tech_names.sort() - world.random.shuffle(tech_names) while len(tech_names) > slice_size: slice = tech_names[:slice_size] tech_names = tech_names[slice_size:] - slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients)) + slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].get_prior_technologies())) # 0 | # 1 2 | @@ -52,44 +58,136 @@ def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]: prerequisites[slice[8]] = {slice[6], slice[7]} - elif layout == TechTreeLayout.option_pyramid: - slice_size = 1 - tech_names: List[str] = list(set(custom_technologies) - world._static_nodes) - tech_names.sort() - world.random.shuffle(tech_names) - tech_names.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients)) - previous_slice = [] + elif layout == TechTreeLayout.option_large_diamonds: + slice_size = 16 while len(tech_names) > slice_size: slice = tech_names[:slice_size] - world.random.shuffle(slice) tech_names = tech_names[slice_size:] - for i, tech_name in enumerate(previous_slice): - prerequisites.setdefault(slice[i], set()).add(tech_name) - prerequisites.setdefault(slice[i + 1], set()).add(tech_name) - previous_slice = slice - slice_size += 1 + slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].get_prior_technologies())) - elif layout == TechTreeLayout.option_funnel: + # 0 | + # 1 2 | + # 3 4 5 | + # 6 7 8 9 | + # 10 11 12 | + # 13 14 | + # 15 | + prerequisites[slice[1]] = {slice[0]} + prerequisites[slice[2]] = {slice[0]} - tech_names: List[str] = list(set(custom_technologies) - world._static_nodes) - # find largest inverse pyramid - # https://www.wolframalpha.com/input/?i=x+=+1/2+(n++++1)+(2++++n)+solve+for+n - import math - slice_size = int(0.5*(math.sqrt(8*len(tech_names)+1)-3)) - tech_names.sort() - world.random.shuffle(tech_names) - tech_names.sort(key=lambda tech_name: len(custom_technologies[tech_name].ingredients)) - previous_slice = [] - while slice_size: + prerequisites[slice[3]] = {slice[1]} + prerequisites[slice[4]] = {slice[1], slice[2]} + prerequisites[slice[5]] = {slice[2]} + + prerequisites[slice[6]] = {slice[3]} + prerequisites[slice[7]] = {slice[3], slice[4]} + prerequisites[slice[8]] = {slice[4], slice[5]} + prerequisites[slice[9]] = {slice[5]} + + prerequisites[slice[10]] = {slice[6], slice[7]} + prerequisites[slice[11]] = {slice[7], slice[8]} + prerequisites[slice[12]] = {slice[8], slice[9]} + + prerequisites[slice[13]] = {slice[10], slice[11]} + prerequisites[slice[14]] = {slice[11], slice[12]} + + prerequisites[slice[15]] = {slice[13], slice[14]} + + elif layout == TechTreeLayout.option_small_pyramids: + slice_size = 6 + while len(tech_names) > slice_size: slice = tech_names[:slice_size] - world.random.shuffle(slice) tech_names = tech_names[slice_size:] - if previous_slice: - for i, tech_name in enumerate(slice): - prerequisites.setdefault(tech_name, set()).update(previous_slice[i:i+2]) - previous_slice = slice - slice_size -= 1 + slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].get_prior_technologies())) + + # 0 | + # 1 2 | + # 3 4 5 | + + prerequisites[slice[1]] = {slice[0]} + prerequisites[slice[2]] = {slice[0]} + + prerequisites[slice[3]] = {slice[1]} + prerequisites[slice[4]] = {slice[1], slice[2]} + prerequisites[slice[5]] = {slice[2]} + + elif layout == TechTreeLayout.option_medium_pyramids: + slice_size = 10 + while len(tech_names) > slice_size: + slice = tech_names[:slice_size] + tech_names = tech_names[slice_size:] + slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].get_prior_technologies())) + + # 0 | + # 1 2 | + # 3 4 5 | + # 6 7 8 9 | + + + prerequisites[slice[1]] = {slice[0]} + prerequisites[slice[2]] = {slice[0]} + + prerequisites[slice[3]] = {slice[1]} + prerequisites[slice[4]] = {slice[1], slice[2]} + prerequisites[slice[5]] = {slice[2]} + + prerequisites[slice[6]] = {slice[3]} + prerequisites[slice[7]] = {slice[3], slice[4]} + prerequisites[slice[8]] = {slice[4], slice[5]} + prerequisites[slice[9]] = {slice[5]} + + elif layout == TechTreeLayout.option_large_pyramids: + slice_size = 15 + while len(tech_names) > slice_size: + slice = tech_names[:slice_size] + tech_names = tech_names[slice_size:] + slice.sort(key=lambda tech_name: len(custom_technologies[tech_name].get_prior_technologies())) + + # 0 | + # 1 2 | + # 3 4 5 | + # 6 7 8 9 | + # 10 11 12 13 14 | + + + prerequisites[slice[1]] = {slice[0]} + prerequisites[slice[2]] = {slice[0]} + + prerequisites[slice[3]] = {slice[1]} + prerequisites[slice[4]] = {slice[1], slice[2]} + prerequisites[slice[5]] = {slice[2]} + + prerequisites[slice[6]] = {slice[3]} + prerequisites[slice[7]] = {slice[3], slice[4]} + prerequisites[slice[8]] = {slice[4], slice[5]} + prerequisites[slice[9]] = {slice[5]} + + prerequisites[slice[10]] = {slice[6]} + prerequisites[slice[11]] = {slice[6], slice[7]} + prerequisites[slice[12]] = {slice[7], slice[8]} + prerequisites[slice[13]] = {slice[8], slice[9]} + prerequisites[slice[14]] = {slice[9]} + + elif layout in funnel_layers: + slice_size = funnel_slice_sizes[layout] + + world.random.shuffle(tech_names) + tech_names.sort(key=lambda tech_name: len(custom_technologies[tech_name].get_prior_technologies())) + + while len(tech_names) > slice_size: + tech_names = tech_names[slice_size:] + current_tech_names = tech_names[:slice_size] + layer_size = funnel_layers[layout] + previous_slice = [] + for layer in range(funnel_layers[layout]): + slice = current_tech_names[:layer_size] + current_tech_names = current_tech_names[layer_size:] + if previous_slice: + for i, tech_name in enumerate(slice): + prerequisites.setdefault(tech_name, set()).update(previous_slice[i:i+2]) + previous_slice = slice + layer_size -= 1 world.tech_tree_layout_prerequisites[player] = prerequisites return prerequisites