diff --git a/Mystery.py b/Mystery.py index 2a62a05a..71b03f0c 100644 --- a/Mystery.py +++ b/Mystery.py @@ -538,6 +538,12 @@ def roll_settings(weights: dict, plando_options: typing.Set[str] = frozenset(("b setattr(ret, option_name, option.from_any(get_choice(option_name, weights))) else: setattr(ret, option_name, option.from_any(option.default)) + elif ret.game == "Minecraft": + for option_name, option in Options.minecraft_options.items(): + if option_name in weights: + setattr(ret, option_name, option.from_any(get_choice(option_name, weights))) + else: + setattr(ret, option_name, option.from_any(option.default)) else: raise Exception(f"Unsupported game {ret.game}") return ret diff --git a/Options.py b/Options.py index 73eb65f4..896850a0 100644 --- a/Options.py +++ b/Options.py @@ -236,7 +236,7 @@ hollow_knight_skip_options: typing.Dict[str, type(Option)] = { "SHADESKIPS": Toggle, } -hollow_knight_options: typing.Dict[str, Option] = {**hollow_knight_randomize_options, **hollow_knight_skip_options} +hollow_knight_options: typing.Dict[str, type(Option)] = {**hollow_knight_randomize_options, **hollow_knight_skip_options} class MaxSciencePack(Choice): @@ -276,6 +276,7 @@ class TechTreeLayout(Choice): option_small_diamonds = 1 option_medium_diamonds = 2 option_pyramid = 3 + option_funnel = 4 default = 0 class Visibility(Choice): @@ -289,6 +290,8 @@ factorio_options: typing.Dict[str, type(Option)] = {"max_science_pack": MaxScien "free_samples": FreeSamples, "visibility": Visibility} +minecraft_options: typing.Dict[str, type(Option)] = {} + if __name__ == "__main__": import argparse diff --git a/playerSettings.yaml b/playerSettings.yaml index 4ea47641..b530968d 100644 --- a/playerSettings.yaml +++ b/playerSettings.yaml @@ -27,6 +27,7 @@ game: A Link to the Past: 1 Hollow Knight: 1 Factorio: 1 + Minecraft: 1 # Shared Options supported by all games: accessibility: items: 0 # Guarantees you will be able to acquire all items, but you may not be able to access all locations @@ -41,6 +42,7 @@ tech_tree_layout: small_diamonds: 1 medium_diamonds: 1 pyramid: 1 + funnel: 1 max_science_pack: automation_science_pack: 0 logistic_science_pack: 0 diff --git a/worlds/__init__.py b/worlds/__init__.py index 941158b1..38fcf0c9 100644 --- a/worlds/__init__.py +++ b/worlds/__init__.py @@ -32,3 +32,4 @@ class Games(str, enum.Enum): HK = "Hollow Knight" LTTP = "A Link to the Past" Factorio = "Factorio" + Minecraft = "Minecraft" diff --git a/worlds/factorio/Shapes.py b/worlds/factorio/Shapes.py new file mode 100644 index 00000000..1daa42f8 --- /dev/null +++ b/worlds/factorio/Shapes.py @@ -0,0 +1,95 @@ +from typing import Dict, List, Set + +from BaseClasses import MultiWorld +from Options import TechTreeLayout +from worlds.factorio.Technologies import technology_table + +def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]: + prerequisites: Dict[str, Set[str]] = {} + layout = world.tech_tree_layout[player].value + if layout == TechTreeLayout.option_small_diamonds: + slice_size = 4 + tech_names: List[str] = list(set(technology_table) - 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(technology_table[tech_name].ingredients)) + diamond_0, diamond_1, diamond_2, diamond_3 = slice + + # 0 | + # 1 2 | + # 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(technology_table) - 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(technology_table[tech_name].ingredients)) + + # 0 | + # 1 2 | + # 3 4 5 | + # 6 7 | + # 8 V + + 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], slice[4]} + prerequisites[slice[7]] = {slice[4], slice[5]} + + prerequisites[slice[8]] = {slice[6], slice[7]} + + elif layout == TechTreeLayout.option_pyramid: + slice_size = 1 + tech_names: List[str] = list(set(technology_table) - world._static_nodes) + tech_names.sort() + world.random.shuffle(tech_names) + tech_names.sort(key=lambda tech_name: len(technology_table[tech_name].ingredients)) + previous_slice = [] + while len(tech_names) > slice_size: + slice = tech_names[:slice_size] + 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 + + elif layout == TechTreeLayout.option_funnel: + + + tech_names: List[str] = list(set(technology_table) - 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)) + + import logging + logging.info(slice_size) + tech_names.sort() + world.random.shuffle(tech_names) + tech_names.sort(key=lambda tech_name: len(technology_table[tech_name].ingredients)) + previous_slice = [] + while slice_size: + slice = tech_names[:slice_size] + 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 + + world.tech_tree_layout_prerequisites[player] = prerequisites + return prerequisites diff --git a/worlds/factorio/__init__.py b/worlds/factorio/__init__.py index 13f955b3..50102e01 100644 --- a/worlds/factorio/__init__.py +++ b/worlds/factorio/__init__.py @@ -1,14 +1,10 @@ -import logging -from typing import List, Dict, Set - from BaseClasses import Region, Entrance, Location, MultiWorld, Item -from Options import TechTreeLayout from .Technologies import tech_table, recipe_sources, technology_table, advancement_technologies, required_technologies - -static_nodes = {"automation", "logistics"} +from .Shapes import get_shapes def gen_factorio(world: MultiWorld, player: int): + static_nodes = world._static_nodes = {"automation", "logistics"} # turn dynamic/option? for tech_name, tech_id in tech_table.items(): tech_item = Item(tech_name, tech_name in advancement_technologies, tech_id, player) tech_item.game = "Factorio" @@ -36,73 +32,6 @@ def factorio_create_regions(world: MultiWorld, player: int): world.regions += [menu, nauvis] -def get_shapes(world: MultiWorld, player: int) -> Dict[str, List[str]]: - prerequisites:Dict[str, Set[str]] = {} - layout = world.tech_tree_layout[player].value - if layout == TechTreeLayout.option_small_diamonds: - slice_size = 4 - tech_names: List[str] = list(set(technology_table)-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(technology_table[tech_name].ingredients)) - diamond_0, diamond_1, diamond_2, diamond_3 = slice - - # 0 | - # 1 2 | - # 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(technology_table)-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(technology_table[tech_name].ingredients)) - - # 0 | - # 1 2 | - # 3 4 5 | - # 6 7 | - # 8 V - - 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], slice[4]} - prerequisites[slice[7]] = {slice[4], slice[5]} - - prerequisites[slice[8]] = {slice[6], slice[7]} - - elif layout == TechTreeLayout.option_pyramid: - slice_size = 1 - tech_names: List[str] = list(set(technology_table)-static_nodes) - tech_names.sort() - world.random.shuffle(tech_names) - tech_names.sort(key=lambda tech_name: len(technology_table[tech_name].ingredients)) - previous_slice = [] - while len(tech_names) > slice_size: - slice = tech_names[:slice_size] - 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 - - world.tech_tree_layout_prerequisites[player] = prerequisites - return prerequisites - - def set_rules(world: MultiWorld, player: int): shapes = get_shapes(world, player) if world.logic[player] != 'nologic': @@ -118,7 +47,6 @@ def set_rules(world: MultiWorld, player: int): Rules.add_rule(location, lambda state, locations=locations: all(state.can_reach(loc) for loc in locations)) - # get all technologies world.completion_condition[player] = lambda state: all(state.has(technology, player) for technology in advancement_technologies) diff --git a/worlds/minecraft/__init__.py b/worlds/minecraft/__init__.py new file mode 100644 index 00000000..e69de29b