Beispiel #1
0
def logic_bootstrap(
    configuration: LayoutConfiguration,
    game: GameDescription,
    patches: GamePatches,
) -> Tuple[Logic, State]:
    """
    Core code for starting a new Logic/State.
    :param configuration:
    :param game:
    :param patches:
    :return:
    """

    # global state for easy printing functions
    debug._gd = game

    game = copy.deepcopy(game)
    logic = Logic(game, configuration)
    starting_state = calculate_starting_state(logic, patches)

    if configuration.trick_level == LayoutTrickLevel.MINIMAL_RESTRICTIONS:
        _add_minimal_restrictions_initial_resources(starting_state.resources,
                                                    game.resource_database)

    difficulty_level, static_resources = static_resources_for_layout_logic(
        configuration.trick_level, game.resource_database)

    starting_state.resources = merge_resources(static_resources,
                                               starting_state.resources)
    starting_state.resources[
        game.resource_database.difficulty_resource] = difficulty_level

    game.simplify_connections(starting_state.resources)

    return logic, starting_state
Beispiel #2
0
def logic_bootstrap(
    configuration: EchoesConfiguration,
    game: GameDescription,
    patches: GamePatches,
) -> Tuple[GameDescription, State]:
    """
    Core code for starting a new Logic/State.
    :param configuration:
    :param game:
    :param patches:
    :return:
    """
    game = copy.deepcopy(game)
    starting_state = calculate_starting_state(game, patches)

    if configuration.trick_level.minimal_logic:
        _add_minimal_logic_initial_resources(
            starting_state.resources, game.resource_database,
            configuration.major_items_configuration)

    static_resources = trick_resources_for_configuration(
        configuration.trick_level, game.resource_database)
    static_resources.update(version_resources_for_game(game.resource_database))
    static_resources.update(
        misc_resources_for_configuration(configuration,
                                         game.resource_database))

    for resource, quantity in static_resources.items():
        starting_state.resources[resource] = quantity

    game.patch_requirements(starting_state.resources,
                            configuration.damage_strictness.value)

    return game, starting_state
Beispiel #3
0
def test_basic_search_with_translator_gate(has_translator: bool,
                                           echoes_resource_database):
    # Setup
    scan_visor = echoes_resource_database.get_item(10)

    node_a = GenericNode("Node A", True, None, 0)
    node_b = GenericNode("Node B", True, None, 1)
    node_c = GenericNode("Node C", True, None, 2)
    translator_node = TranslatorGateNode("Translator Gate", True, None, 3,
                                         TranslatorGate(1), scan_visor)

    world_list = WorldList([
        World("Test World", "Test Dark World", 1, [
            Area(
                "Test Area A", False, 10, 0, True,
                [node_a, node_b, node_c, translator_node], {
                    node_a: {
                        node_b: Requirement.trivial(),
                        translator_node: Requirement.trivial(),
                    },
                    node_b: {
                        node_a: Requirement.trivial(),
                    },
                    node_c: {
                        translator_node: Requirement.trivial(),
                    },
                    translator_node: {
                        node_a: Requirement.trivial(),
                        node_c: Requirement.trivial(),
                    },
                })
        ])
    ])
    game_specific = EchoesGameSpecific(energy_per_tank=100,
                                       safe_zone_heal_per_second=1,
                                       beam_configurations=(),
                                       dangerous_energy_tank=False)
    game = GameDescription(RandovaniaGame.PRIME2,
                           DockWeaknessDatabase([], [], [], []),
                           echoes_resource_database, game_specific,
                           Requirement.impossible(), None, {}, world_list)

    patches = game.create_game_patches()
    patches = patches.assign_gate_assignment({TranslatorGate(1): scan_visor})
    initial_state = State({scan_visor: 1 if has_translator else 0}, (), 99,
                          node_a, patches, None, echoes_resource_database,
                          game.world_list)

    # Run
    reach = reach_with_all_safe_resources(game, initial_state)

    # Assert
    if has_translator:
        assert set(
            reach.safe_nodes) == {node_a, node_b, translator_node, node_c}
    else:
        assert set(reach.safe_nodes) == {node_a, node_b}
Beispiel #4
0
def test_basic_search_with_translator_gate(has_translator: bool,
                                           echoes_resource_database):
    # Setup
    scan_visor = echoes_resource_database.get_by_type_and_index(
        ResourceType.ITEM, 10)

    node_a = GenericNode("Node A", True, 0)
    node_b = GenericNode("Node B", True, 1)
    node_c = GenericNode("Node C", True, 2)
    translator_node = TranslatorGateNode("Translator Gate", True, 3,
                                         TranslatorGate(1), scan_visor)

    world_list = WorldList([
        World("Test World", "Test Dark World", 1, [
            Area(
                "Test Area A", False, 10, 0,
                [node_a, node_b, node_c, translator_node], {
                    node_a: {
                        node_b: RequirementSet.trivial(),
                        translator_node: RequirementSet.trivial(),
                    },
                    node_b: {
                        node_a: RequirementSet.trivial(),
                    },
                    node_c: {
                        translator_node: RequirementSet.trivial(),
                    },
                    translator_node: {
                        node_a: RequirementSet.trivial(),
                        node_c: RequirementSet.trivial(),
                    },
                })
        ])
    ])
    game = GameDescription(0, "",
                           DockWeaknessDatabase([], [], [],
                                                []), echoes_resource_database,
                           RequirementSet.impossible(), None, {}, world_list)

    patches = game.create_game_patches()
    patches = patches.assign_gate_assignment({TranslatorGate(1): scan_visor})
    initial_state = State({scan_visor: 1 if has_translator else 0}, (), 99,
                          node_a, patches, None, echoes_resource_database)

    # Run
    reach = reach_with_all_safe_resources(game, initial_state)

    # Assert
    if has_translator:
        assert set(
            reach.safe_nodes) == {node_a, node_b, translator_node, node_c}
    else:
        assert set(reach.safe_nodes) == {node_a, node_b}
def _create_elevators_field(patches: GamePatches, game: GameDescription) -> list:
    """
    Creates the elevator entries in the patcher file
    :param patches:
    :param game:
    :return:
    """

    world_list = game.world_list
    nodes_by_teleporter_id = {
        node.teleporter_instance_id: node
        for node in game.all_editable_teleporter_nodes()
    }

    if len(patches.elevator_connection) != len(nodes_by_teleporter_id):
        raise ValueError("Invalid elevator count. Expected {}, got {}.".format(
            len(nodes_by_teleporter_id), len(patches.elevator_connection)
        ))

    elevators = [
        {
            "instance_id": instance_id,
            "origin_location": world_list.node_to_area_location(nodes_by_teleporter_id[instance_id]).as_json,
            "target_location": connection.as_json,
            "room_name": _pretty_name_for_elevator(world_list, nodes_by_teleporter_id[instance_id], connection)
        }
        for instance_id, connection in patches.elevator_connection.items()
    ]

    return elevators
Beispiel #6
0
def decode_data_with_world_reader(
        data: Dict) -> Tuple[WorldReader, GameDescription]:
    data = game_migration.migrate_to_current(copy.deepcopy(data))

    game = RandovaniaGame(data["game"])

    resource_database = read_resource_database(game, data["resource_database"])
    dock_weakness_database = read_dock_weakness_database(
        data["dock_weakness_database"], resource_database)

    layers = frozen_lib.wrap(data["layers"])
    world_reader = WorldReader(resource_database, dock_weakness_database)
    world_list = world_reader.read_world_list(data["worlds"])

    victory_condition = read_requirement(data["victory_condition"],
                                         resource_database)
    starting_location = AreaIdentifier.from_json(data["starting_location"])
    initial_states = read_initial_states(data["initial_states"],
                                         resource_database)
    minimal_logic = read_minimal_logic_db(data["minimal_logic"])

    return world_reader, GameDescription(
        game=game,
        resource_database=resource_database,
        layers=layers,
        dock_weakness_database=dock_weakness_database,
        world_list=world_list,
        victory_condition=victory_condition,
        starting_location=starting_location,
        initial_states=initial_states,
        minimal_logic=minimal_logic,
    )
Beispiel #7
0
def decode_data_with_world_reader(
        data: Dict) -> Tuple[WorldReader, GameDescription]:
    game = data["game"]
    game_name = data["game_name"]

    resource_database = read_resource_database(data["resource_database"])
    dock_weakness_database = read_dock_weakness_database(
        data["dock_weakness_database"], resource_database)
    if game == 2:
        game_specific = read_game_specific(data["game_specific"],
                                           resource_database)
    else:
        game_specific = None

    world_reader = WorldReader(resource_database, dock_weakness_database)
    world_list = world_reader.read_world_list(data["worlds"])

    victory_condition = read_requirement(data["victory_condition"],
                                         resource_database)
    starting_location = AreaLocation.from_json(data["starting_location"])
    initial_states = read_initial_states(data["initial_states"],
                                         resource_database)

    return world_reader, GameDescription(
        game=game,
        game_name=game_name,
        resource_database=resource_database,
        game_specific=game_specific,
        dock_weakness_database=dock_weakness_database,
        world_list=world_list,
        victory_condition=victory_condition,
        starting_location=starting_location,
        initial_states=initial_states,
    )
def create_base_patches(
    configuration: LayoutConfiguration,
    rng: Random,
    game: GameDescription,
) -> GamePatches:
    """

    :param configuration:
    :param rng:
    :param game:
    :return:
    """

    # TODO: we shouldn't need the seed_number!

    patches = game.create_game_patches()
    patches = add_elevator_connections_to_patches(configuration, rng, patches)

    # Gates
    patches = patches.assign_gate_assignment(
        gate_assignment_for_configuration(configuration,
                                          game.resource_database, rng))

    # Starting Location
    patches = patches.assign_starting_location(
        starting_location_for_configuration(configuration, game, rng))

    # Hints
    if rng is not None:
        patches = add_default_hints_to_patches(rng, patches, game.world_list)

    return patches
Beispiel #9
0
def create_base_patches(
    configuration: EchoesConfiguration,
    rng: Random,
    game: GameDescription,
    is_multiworld: bool,
) -> GamePatches:
    """
    """
    patches = dataclasses.replace(game.create_game_patches(),
                                  game_specific=create_game_specific(
                                      configuration, game))

    patches = add_elevator_connections_to_patches(configuration, rng, patches)

    # Gates
    if configuration.game == RandovaniaGame.PRIME2:
        patches = patches.assign_gate_assignment(
            gate_assignment_for_configuration(configuration,
                                              game.resource_database, rng))

    # Starting Location
    patches = patches.assign_starting_location(
        starting_location_for_configuration(configuration, game, rng))

    # Hints
    if rng is not None and configuration.game == RandovaniaGame.PRIME2:
        patches = add_echoes_default_hints_to_patches(
            rng,
            patches,
            game.world_list,
            num_joke=2,
            is_multiworld=is_multiworld)

    return patches
Beispiel #10
0
def create_base_patches(
    configuration: LayoutConfiguration,
    rng: Random,
    game: GameDescription,
) -> GamePatches:
    """

    :param configuration:
    :param rng:
    :param game:
    :return:
    """
    patches = dataclasses.replace(game.create_game_patches(),
                                  game_specific=create_game_specific(
                                      configuration, game))

    patches = add_elevator_connections_to_patches(configuration, rng, patches)

    # Gates
    patches = patches.assign_gate_assignment(
        gate_assignment_for_configuration(configuration,
                                          game.resource_database, rng))

    # Starting Location
    patches = patches.assign_starting_location(
        starting_location_for_configuration(configuration, game, rng))

    # Hints
    if rng is not None:
        patches = add_default_hints_to_patches(rng,
                                               patches,
                                               game.world_list,
                                               num_joke=2)

    return patches
Beispiel #11
0
def decode_data(
        data: Dict,
        add_self_as_requirement_to_resources: bool = True) -> GameDescription:
    game = data["game"]
    game_name = data["game_name"]

    resource_database = read_resource_database(data["resource_database"])
    pickup_database = read_pickup_database(data["pickup_database"],
                                           resource_database)
    dock_weakness_database = read_dock_weakness_database(
        data["dock_weakness_database"], resource_database)

    world_reader = WorldReader(resource_database, dock_weakness_database,
                               add_self_as_requirement_to_resources)
    world_list = world_reader.read_world_list(data["worlds"])

    victory_condition = read_requirement_set(data["victory_condition"],
                                             resource_database)
    starting_location = AreaLocation.from_json(data["starting_location"])
    initial_states = read_initial_states(data["initial_states"],
                                         resource_database)

    return GameDescription(
        game=game,
        game_name=game_name,
        resource_database=resource_database,
        pickup_database=pickup_database,
        dock_weakness_database=dock_weakness_database,
        world_list=world_list,
        victory_condition=victory_condition,
        starting_location=starting_location,
        initial_states=initial_states,
        add_self_as_requirement_to_resources=
        add_self_as_requirement_to_resources,
    )
Beispiel #12
0
    def logic_bootstrap(
        self,
        configuration: BaseConfiguration,
        game: GameDescription,
        patches: GamePatches,
    ) -> Tuple[GameDescription, State]:
        """
        Core code for starting a new Logic/State.
        :param configuration:
        :param game:
        :param patches:
        :return:
        """
        if not game.mutable:
            raise ValueError("Running logic_bootstrap with non-mutable game")

        game.world_list.ensure_has_node_cache()
        starting_state = self.calculate_starting_state(game, patches,
                                                       configuration)

        if configuration.trick_level.minimal_logic:
            self._add_minimal_logic_initial_resources(
                starting_state.resources, game,
                configuration.major_items_configuration)

        static_resources = ResourceCollection.with_database(
            game.resource_database)
        static_resources.add_resource_gain(
            self.trick_resources_for_configuration(configuration.trick_level,
                                                   game.resource_database))
        static_resources.add_resource_gain(
            self.event_resources_for_configuration(configuration,
                                                   game.resource_database))
        static_resources.add_resource_gain(
            self.version_resources_for_game(configuration,
                                            game.resource_database))
        static_resources.add_resource_gain(
            self.misc_resources_for_configuration(configuration,
                                                  game.resource_database))

        for resource, quantity in static_resources.as_resource_gain():
            starting_state.resources.set_resource(resource, quantity)

        game.patch_requirements(starting_state.resources,
                                configuration.damage_strictness.value)

        return game, starting_state
Beispiel #13
0
def logic_bootstrap(
    configuration: LayoutConfiguration,
    game: GameDescription,
    patches: GamePatches,
) -> Tuple[GameDescription, State]:
    """
    Core code for starting a new Logic/State.
    :param configuration:
    :param game:
    :param patches:
    :return:
    """

    # global state for easy printing functions
    debug._gd = game

    game = copy.deepcopy(game)
    starting_state = calculate_starting_state(game, patches)

    if configuration.trick_level_configuration.global_level == LayoutTrickLevel.MINIMAL_RESTRICTIONS:
        major_items_config = configuration.major_items_configuration
        _add_minimal_restrictions_initial_resources(
            starting_state.resources,
            game.resource_database,
            major_items_config.progressive_grapple,
            major_items_config.progressive_suit,
        )

    difficulty_level, static_resources = static_resources_for_layout_logic(
        configuration.trick_level_configuration, game.resource_database)
    add_resources_into_another(starting_state.resources, static_resources)
    add_resources_into_another(
        starting_state.resources,
        _create_vanilla_translator_resources(game.resource_database,
                                             configuration.elevators))
    starting_state.resources[
        game.resource_database.difficulty_resource] = difficulty_level

    # All version differences are patched out from the game
    starting_state.resources[find_resource_info_with_long_name(
        game.resource_database.version, "NTSC")] = 1

    game.patch_requirements(starting_state.resources,
                            configuration.damage_strictness.value)

    return game, starting_state
Beispiel #14
0
def logic_bootstrap(
    configuration: LayoutConfiguration,
    game: GameDescription,
    patches: GamePatches,
) -> Tuple[GameDescription, State]:
    """
    Core code for starting a new Logic/State.
    :param configuration:
    :param game:
    :param patches:
    :return:
    """

    # global state for easy printing functions
    debug._gd = game

    game = copy.deepcopy(game)
    starting_state = calculate_starting_state(game, patches)

    if configuration.trick_level_configuration.global_level == LayoutTrickLevel.MINIMAL_RESTRICTIONS:
        major_items_config = configuration.major_items_configuration
        _add_minimal_restrictions_initial_resources(
            starting_state.resources,
            game.resource_database,
            major_items_config.progressive_grapple,
            major_items_config.progressive_suit,
        )

    difficulty_level, static_resources = static_resources_for_layout_logic(
        configuration.trick_level_configuration, game.resource_database)
    add_resources_into_another(starting_state.resources, static_resources)
    add_resources_into_another(
        starting_state.resources,
        _create_vanilla_translator_resources(
            game.resource_database, configuration.translator_configuration))
    starting_state.resources[
        game.resource_database.difficulty_resource] = difficulty_level

    game.simplify_connections(starting_state.resources)

    return game, starting_state
Beispiel #15
0
def test_basic_search_with_translator_gate(has_translator: bool, echoes_resource_database, echoes_game_patches):
    # Setup
    scan_visor = echoes_resource_database.get_item("DarkVisor")
    nc = functools.partial(NodeIdentifier.create, "Test World", "Test Area A")

    node_a = GenericNode(nc("Node A"), True, None, "", ("default",), {})
    node_b = GenericNode(nc("Node B"), True, None, "", ("default",), {})
    node_c = GenericNode(nc("Node C"), True, None, "", ("default",), {})
    translator_node = ConfigurableNode(translator_identif := nc("Translator Gate"),
                                       True, None, "", ("default",), {})

    world_list = WorldList([
        World("Test World", [
            Area("Test Area A", None, True, [node_a, node_b, node_c, translator_node],
                 {
                     node_a: {
                         node_b: Requirement.trivial(),
                         translator_node: Requirement.trivial(),
                     },
                     node_b: {
                         node_a: Requirement.trivial(),
                     },
                     node_c: {
                         translator_node: Requirement.trivial(),
                     },
                     translator_node: {
                         node_a: Requirement.trivial(),
                         node_c: Requirement.trivial(),
                     },
                 },
                 {}
                 )
        ], {})
    ])
    game = GameDescription(RandovaniaGame.METROID_PRIME_ECHOES, DockWeaknessDatabase([], {}, (None, None)),
                           echoes_resource_database, ("default",), Requirement.impossible(),
                           None, {}, None, world_list)

    patches = echoes_game_patches.assign_node_configuration({
        translator_identif: ResourceRequirement(scan_visor, 1, False)
    })
    initial_state = State({scan_visor: 1 if has_translator else 0}, (), 99,
                          node_a, patches, None, StateGameData(echoes_resource_database, game.world_list, 100, 99))

    # Run
    reach = reach_lib.reach_with_all_safe_resources(game, initial_state)

    # Assert
    if has_translator:
        assert set(reach.safe_nodes) == {node_a, node_b, translator_node, node_c}
    else:
        assert set(reach.safe_nodes) == {node_a, node_b}
Beispiel #16
0
def logic_bootstrap(configuration: LayoutConfiguration,
                    game: GameDescription,
                    patches: GamePatches,
                    ) -> Tuple[GameDescription, State]:
    """
    Core code for starting a new Logic/State.
    :param configuration:
    :param game:
    :param patches:
    :return:
    """

    # global state for easy printing functions
    debug._gd = game

    game = copy.deepcopy(game)
    starting_state = calculate_starting_state(game, patches)

    if configuration.trick_level_configuration.minimal_logic:
        major_items_config = configuration.major_items_configuration
        _add_minimal_logic_initial_resources(starting_state.resources,
                                             game.resource_database,
                                             major_items_config.progressive_grapple,
                                             major_items_config.progressive_suit,
                                             )

    static_resources = trick_resources_for_configuration(configuration.trick_level_configuration,
                                                         game.resource_database)
    static_resources.update(version_resources_for_game(game.resource_database))
    static_resources.update(misc_resources_for_configuration(configuration, game.resource_database))

    for resource, quantity in static_resources.items():
        starting_state.resources[resource] = quantity

    game.patch_requirements(starting_state.resources, configuration.damage_strictness.value)

    return game, starting_state
Beispiel #17
0
def find_database_errors(game: GameDescription) -> list[str]:
    copy = game.get_mutable()
    derived_nodes.create_derived_nodes(copy)

    result = []

    for layer in copy.layers:
        if layer_name_re.match(layer) is None:
            result.append(
                f"Layer '{layer}' doesn't match {layer_name_re.pattern}")

    for world in copy.world_list.worlds:
        result.extend(find_world_errors(copy, world))
    result.extend(find_invalid_strongly_connected_components(copy))

    return result
Beispiel #18
0
def pretty_print_area(game: GameDescription, area: Area, print_function=print):
    print_function(area.name)
    print_function("Asset id: {}".format(area.area_asset_id))
    for node in area.nodes:
        print_function(f"> {node.name}; Heals? {node.heal}")
        for target_node, requirement in game.world_list.potential_nodes_from(
                node, game.create_game_patches()):
            if target_node is None:
                print_function("  > None?")
            else:
                print_function("  > {}".format(
                    game.world_list.node_name(target_node)))
                for level, text in pretty_print_requirement(
                        requirement.simplify()):
                    print_function("      {}{}".format("    " * level, text))
        print_function()
Beispiel #19
0
 def create_from_game(
     cls,
     game: GameDescription,
     player_index: int,
     configuration: BaseConfiguration,
 ) -> GamePatches:
     game.world_list.ensure_has_node_cache()
     return GamePatches(
         game,
         player_index,
         configuration,
         pickup_assignment={},
         elevator_connection=game.get_default_elevator_connection(),
         dock_connection=[None] * len(game.world_list.all_nodes),
         dock_weakness=[None] * len(game.world_list.all_nodes),
         configurable_nodes={},
         starting_items=ResourceCollection.with_database(
             game.resource_database),
         starting_location=game.starting_location,
         hints={},
     )
Beispiel #20
0
    def create_base_patches(self,
                            configuration: BaseConfiguration,
                            rng: Random,
                            game: GameDescription,
                            is_multiworld: bool,
                            player_index: int,
                            rng_required: bool = True
                            ) -> GamePatches:
        """
        """
        patches = GamePatches(player_index, configuration, {}, game.get_default_elevator_connection(),
                              {}, {}, {}, {}, game.starting_location, {})

        # Elevators
        try:
            patches = self.add_elevator_connections_to_patches(configuration, rng, patches)
        except MissingRng as e:
            if rng_required:
                raise e

        # Configurable Nodes
        try:
            patches = patches.assign_node_configuration(
                self.configurable_node_assignment(configuration, game, rng)
            )
        except MissingRng as e:
            if rng_required:
                raise e

        # Starting Location
        try:
            patches = patches.assign_starting_location(
                self.starting_location_for_configuration(configuration, game, rng))
        except MissingRng as e:
            if rng_required:
                raise e

        return patches
Beispiel #21
0
                    for connection in connections.values():
                        connection.pop(node, None)

                    if area.default_node == node.name:
                        has_default_node = False

            areas.append(
                Area(
                    name=area.name,
                    default_node=area.default_node
                    if has_default_node else None,
                    valid_starting_location=area.valid_starting_location,
                    nodes=nodes,
                    connections=connections,
                    extra=area.extra,
                ))

        worlds.append(dataclasses.replace(world, areas=areas))

    return GameDescription(
        game=game.game,
        resource_database=game.resource_database,
        layers=game.layers,
        dock_weakness_database=game.dock_weakness_database,
        world_list=WorldList(worlds),
        victory_condition=game.victory_condition,
        starting_location=game.starting_location,
        initial_states=game.initial_states,
        minimal_logic=game.minimal_logic,
    )