예제 #1
0
def test_add_elevator_connections_to_patches_random(echoes_game_data):
    # Setup
    game = data_reader.decode_data(echoes_game_data)
    permalink = dataclasses.replace(Permalink.default(),
                                    layout_configuration=dataclasses.replace(
                                        LayoutConfiguration.default(),
                                        elevators=LayoutElevators.RANDOMIZED))
    expected = dataclasses.replace(GamePatches.with_game(game),
                                   elevator_connection={
                                       589851:
                                       AreaLocation(1039999561, 1868895730),
                                       1572998:
                                       AreaLocation(1039999561, 3479543630),
                                       1966093:
                                       AreaLocation(2252328306, 408633584),
                                       2097251:
                                       AreaLocation(1119434212, 3331021649),
                                       136970379:
                                       AreaLocation(2252328306, 2068511343),
                                       3342446:
                                       AreaLocation(1039999561, 3205424168),
                                       3538975:
                                       AreaLocation(1119434212, 2806956034),
                                       152:
                                       AreaLocation(1006255871, 2889020216),
                                       393260:
                                       AreaLocation(464164546, 3145160350),
                                       524321:
                                       AreaLocation(464164546, 900285955),
                                       589949:
                                       AreaLocation(1006255871, 2278776548),
                                       122:
                                       AreaLocation(464164546, 3528156989),
                                       1245307:
                                       AreaLocation(1006255871, 1345979968),
                                       2949235:
                                       AreaLocation(1006255871, 1287880522),
                                       129:
                                       AreaLocation(1006255871, 2918020398),
                                       2162826:
                                       AreaLocation(1006255871, 1660916974),
                                       4522032:
                                       AreaLocation(1006255871, 3455543403),
                                       38:
                                       AreaLocation(1119434212, 1473133138),
                                       1245332:
                                       AreaLocation(2252328306, 2399252740),
                                       1638535:
                                       AreaLocation(2252328306, 2556480432),
                                   })

    # Run
    result = base_patches_factory.add_elevator_connections_to_patches(
        permalink.layout_configuration,
        Random(permalink.seed_number),
        GamePatches.with_game(game),
    )

    # Assert
    assert result == expected
예제 #2
0
def test_add_elevator_connections_to_patches_vanilla(echoes_game_data):
    # Setup
    game = data_reader.decode_data(echoes_game_data)
    permalink = Permalink.default()

    # Run
    result = base_patches_factory.add_elevator_connections_to_patches(
        permalink.layout_configuration, Random(permalink.seed_number),
        GamePatches.with_game(game))

    # Assert
    assert result == GamePatches.with_game(game)
def serialize_single(player_index: int, num_players: int,
                     patches: GamePatches) -> dict:
    """
    Encodes a given GamePatches into a JSON-serializable dict.
    :param player_index:
    :param num_players:
    :param patches:
    :return:
    """
    game = filtered_database.game_description_for_layout(patches.configuration)
    world_list = game.world_list

    dock_weakness_to_type = {}
    for dock_type, weaknesses in game.dock_weakness_database.weaknesses.items(
    ):
        for weakness in weaknesses.values():
            dock_weakness_to_type[weakness] = dock_type

    result = {
        # This field helps schema migrations, if nothing else
        "game": patches.configuration.game.value,
        "starting_location": patches.starting_location.as_string,
        "starting_items": {
            resource_info.long_name: quantity
            for resource_info, quantity in
            patches.starting_items.as_resource_gain()
        },
        "teleporters": {
            source.identifier.as_string: connection.as_string
            for source, connection in patches.all_elevator_connections()
        },
        "dock_weakness": {
            dock.identifier.as_string: {
                "type": dock_weakness_to_type[weakness].short_name,
                "name": weakness.name,
            }
            for dock, weakness in patches.all_dock_weaknesses()
        },
        "configurable_nodes": {
            identifier.as_string: data_writer.write_requirement(requirement)
            for identifier, requirement in patches.configurable_nodes.items()
        },
        "locations": {
            key: value
            for key, value in _pickup_assignment_to_item_locations(
                world_list, patches.pickup_assignment, num_players).items()
        },
        "hints": {
            identifier.as_string: hint.as_json
            for identifier, hint in patches.hints.items()
        }
    }
    return result
def add_default_hints_to_patches(
    rng: Random,
    patches: GamePatches,
    world_list: WorldList,
) -> GamePatches:
    """
    Adds hints for the locations
    :param rng:
    :param patches:
    :param world_list:
    :return:
    """

    for node in world_list.all_nodes:
        if isinstance(
                node,
                LogbookNode) and node.lore_type == LoreType.LUMINOTH_WARRIOR:
            patches = patches.assign_hint(
                node.resource(),
                Hint(
                    HintType.KEYBEARER,
                    PrecisionPair(HintLocationPrecision.DETAILED,
                                  HintItemPrecision.PRECISE_CATEGORY),
                    PickupIndex(node.hint_index)))

    # TODO: this should be a flag in PickupNode
    indices_with_hint = [
        (PickupIndex(24), HintType.LIGHT_SUIT_LOCATION),  # Light Suit
        (PickupIndex(43), HintType.GUARDIAN),  # Dark Suit (Amorbis)
        (PickupIndex(79), HintType.GUARDIAN),  # Dark Visor (Chykka)
        (PickupIndex(115), HintType.GUARDIAN),  # Annihilator Beam (Quadraxis)
    ]
    all_logbook_assets = [
        node.resource() for node in world_list.all_nodes
        if isinstance(node, LogbookNode) and node.resource() not in
        patches.hints and node.lore_type.holds_generic_hint
    ]

    rng.shuffle(indices_with_hint)
    rng.shuffle(all_logbook_assets)

    for index, hint_type in indices_with_hint:
        if not all_logbook_assets:
            break

        logbook_asset = all_logbook_assets.pop()
        patches = patches.assign_hint(
            logbook_asset, Hint(hint_type, PrecisionPair.detailed(), index))

    return patches
예제 #5
0
def test_run_filler(mock_retcon_playthrough_filler: MagicMock,
                    echoes_game_description,
                    pickup
                    ):
    # Setup
    configuration = LayoutConfiguration.default()
    rng = Random(5000)
    status_update = MagicMock()
    item_pool = [pickup]
    patches = GamePatches.with_game(echoes_game_description)

    logbook_nodes = [node for node in echoes_game_description.world_list.all_nodes if isinstance(node, LogbookNode)]

    mock_retcon_playthrough_filler.return_value = patches.assign_hint(
        logbook_nodes[0].resource(), Hint(HintType.LOCATION, None, PickupIndex(0))
    ).assign_pickup_assignment({PickupIndex(1): pickup})

    # Run
    result_patches, remaining_items = runner.run_filler(configuration, echoes_game_description,
                                                        item_pool, patches,
                                                        rng, status_update)

    # Assert
    assert len(result_patches.hints) == len(logbook_nodes)
    assert [hint for hint in patches.hints.values()
            if hint.item_precision is None or hint.location_precision is None] == []
    assert remaining_items == [pickup]
예제 #6
0
def _create_test_layout_description(
        configuration: LayoutConfiguration,
        pickup_mapping: Iterable[int],
) -> LayoutDescription:
    """
    Creates a LayoutDescription for the given configuration, with the patches being for the given pickup_mapping
    :param configuration:
    :param pickup_mapping:
    :return:
    """
    game = data_reader.decode_data(configuration.game_data)
    pickup_database = game.pickup_database

    return LayoutDescription(
        version=VERSION,
        permalink=Permalink(
            seed_number=0,
            spoiler=True,
            patcher_configuration=PatcherConfiguration.default(),
            layout_configuration=configuration,
        ),
        patches=GamePatches.with_game(game).assign_new_pickups([
            (PickupIndex(i), pickup_database.original_pickup_mapping[PickupIndex(new_index)])
            for i, new_index in enumerate(pickup_mapping)
        ]),
        solver_path=())
예제 #7
0
    def __init__(self, layout_configuration: LayoutConfiguration):
        super().__init__()
        self.setupUi(self)
        set_default_window_icon(self)

        self.layout_configuration = layout_configuration
        self.game_description = data_reader.decode_data(
            layout_configuration.game_data, True)

        self.logic, self._initial_state = logic_bootstrap(
            layout_configuration, self.game_description,
            GamePatches.with_game(self.game_description))
        self.resource_filter_check.stateChanged.connect(
            self.update_locations_tree_for_reachable_nodes)
        self.hide_collected_resources_check.stateChanged.connect(
            self.update_locations_tree_for_reachable_nodes)
        self.undo_last_action_button.clicked.connect(self._undo_last_action)

        self.configuration_label.setText(
            "Trick Level: {}; Elevators: Vanilla; Item Loss: {}".format(
                layout_configuration.trick_level.value,
                layout_configuration.starting_resources.configuration.value,
            ))

        self.setup_pickups_box()
        self.setup_possible_locations_tree()

        self._starting_nodes = {
            node
            for node in self.game_description.world_list.all_nodes
            if node.is_resource_node
            and node.resource() in self._initial_state.resources
        }
        self._add_new_action(self._initial_state.node)
예제 #8
0
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

    elevator_fields = []

    for node, connection in patches.all_elevator_connections():
        elevator_fields.append({
            "instance_id":
            node.extra["teleporter_instance_id"],
            "origin_location":
            _area_identifier_to_json(game.world_list,
                                     node.identifier.area_location),
            "target_location":
            _area_identifier_to_json(game.world_list, connection),
            "room_name":
            _pretty_name_for_elevator(game.game, world_list, node, connection)
        })

    num_teleporter_nodes = sum(
        1 for _ in _get_nodes_by_teleporter_id(world_list))
    if len(elevator_fields) != num_teleporter_nodes:
        raise ValueError("Invalid elevator count. Expected {}, got {}.".format(
            num_teleporter_nodes, len(elevator_fields)))

    return elevator_fields
예제 #9
0
    def from_json_dict(cls, json_dict: dict) -> "LayoutDescription":
        version = json_dict["info"]["version"]
        version_as_obj = StrictVersion(version)

        if version_as_obj < StrictVersion("0.21.0"):
            raise RuntimeError("Unsupported log file version '{}'.".format(version))

        # TODO: add try/catch to throw convert potential errors in "seed from future version broke"
        permalink = Permalink.from_json_dict(json_dict["info"]["permalink"])

        if not permalink.spoiler:
            raise ValueError("Unable to read details of seed log with spoiler disabled")

        game = data_reader.decode_data(permalink.layout_configuration.game_data)
        patches = GamePatches(
            _item_locations_to_pickup_assignment(game, json_dict["locations"]),
            _node_mapping_to_elevator_connection(game.world_list, json_dict["elevators"]),
            {},
            {},
            (),
            game.starting_location
        )

        return LayoutDescription(
            version=version,
            permalink=permalink,
            patches=patches,
            solver_path=_playthrough_list_to_solver_path(json_dict["playthrough"]),
        )
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 = GamePatches.with_game(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)

    return patches
예제 #11
0
    def create_game_patches(self) -> GamePatches:
        elevator_connection = {
            node.teleporter_instance_id: node.default_connection
            for node in self.world_list.all_nodes
            if isinstance(node, TeleporterNode) and node.editable
        }

        return GamePatches({}, elevator_connection, {}, {}, {}, {},
                           self.starting_location, {})
예제 #12
0
def find_invalid_strongly_connected_components(game: GameDescription) -> Iterator[str]:
    import networkx
    graph = networkx.DiGraph()

    for node in game.world_list.iterate_nodes():
        if isinstance(node, DockLockNode):
            continue
        graph.add_node(node)

    context = NodeContext(
        patches=GamePatches.create_from_game(game, 0, None),
        current_resources=ResourceCollection.with_database(game.resource_database),
        database=game.resource_database,
        node_provider=game.world_list,
    )

    for node in game.world_list.iterate_nodes():
        if node not in graph:
            continue

        for other, req in game.world_list.potential_nodes_from(node, context):
            if other not in graph:
                continue

            if req != Requirement.impossible():
                graph.add_edge(node, other)

    starting_node = game.world_list.resolve_teleporter_connection(game.starting_location)

    for strong_comp in networkx.strongly_connected_components(graph):
        components: set[Node] = strong_comp

        # The starting location determines the default component
        if starting_node in components:
            continue

        if any(node.extra.get("different_strongly_connected_component", False) for node in components):
            continue

        if len(components) == 1:
            node = next(iter(components))

            # If the component is a single node which is the default node of it's area, allow it
            area = game.world_list.nodes_to_area(node)
            if area.default_node == node.name:
                continue

            # We accept nodes that have no paths out or in.
            if not graph.in_edges(node) and not graph.edges(node):
                continue

        names = sorted(
            game.world_list.node_name(node, with_world=True)
            for node in strong_comp
        )
        yield "Unknown strongly connected component detected containing {} nodes:\n{}".format(len(names), names)
예제 #13
0
def test_cs_item_pool_creator(default_cs_configuration: CSConfiguration,
                              puppies, starting_area):
    game_description = default_database.game_description_for(
        default_cs_configuration.game)
    default_cs_configuration = dataclasses.replace(default_cs_configuration,
                                                   puppies_anywhere=puppies)
    tricks = default_cs_configuration.trick_level.set_level_for_trick(
        game_description.resource_database.get_by_type_and_index(
            ResourceType.TRICK, "SNBubbler"), LayoutTrickLevel.HYPERMODE)
    tricks = tricks.set_level_for_trick(
        game_description.resource_database.get_by_type_and_index(
            ResourceType.TRICK, "SNMissiles"), LayoutTrickLevel.HYPERMODE)
    default_cs_configuration = dataclasses.replace(default_cs_configuration,
                                                   trick_level=tricks)

    base_patches = GamePatches(0, default_cs_configuration, {}, {}, {}, {}, {},
                               {}, starting_area, {})
    rng = Random()

    result = pool_creator.calculate_pool_results(
        default_cs_configuration, game_description.resource_database,
        base_patches, rng)

    # Puppies
    expected_puppies = {"Hajime", "Nene", "Mick", "Shinobu", "Kakeru"}
    names = {pickup.name for pickup in result.assignment.values()}

    assert puppies != names.issuperset(expected_puppies)

    # First Cave Weapon
    first_cave_assignment = [
        pickup for index, pickup in result.assignment.items()
        if index in FIRST_CAVE_INDICES
    ]
    expected_first_cave_len = 1 if starting_area.area_name == "Start Point" else 0

    assert len(first_cave_assignment) == expected_first_cave_len
    assert starting_area.area_name != "Start Point" or first_cave_assignment[
        0].broad_category.name in {"weapon", "missile_related"}

    # Camp weapon/life capsule
    camp_assignment = [
        pickup for index, pickup in result.assignment.items()
        if index in CAMP_INDICES
    ]

    if starting_area.area_name != "Camp":
        assert len(camp_assignment) == 0
    else:
        expected_names = set(STRONG_WEAPONS)
        expected_names.add("5HP Life Capsule")
        for pickup in camp_assignment:
            assert pickup.name in expected_names
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", 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 = GamePatches.with_game(game)
    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}
예제 #15
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
예제 #16
0
def pretty_print_area(area: Area):
    world_list = _gd.world_list

    print(area.name)
    print("Asset id: {}".format(area.area_asset_id))
    for node in area.nodes:
        print(">", node.name, type(node))
        for target_node, requirements in world_list.potential_nodes_from(node, GamePatches.with_game(_gd)):
            if target_node is None:
                print("  > None?")
            else:
                print("  >", n(target_node))
                requirements.pretty_print("      ")
        print()
예제 #17
0
    async def assign_joke_hints(self, patches: GamePatches, identifiers: list[NodeIdentifier],
                                prefill: PreFillParams) -> GamePatches:

        all_hint_identifiers = [identifier for identifier in identifiers if identifier not in patches.hints]
        prefill.rng.shuffle(all_hint_identifiers)

        num_joke = self.num_joke_hints

        while num_joke > 0 and all_hint_identifiers:
            identifier = all_hint_identifiers.pop()
            patches = patches.assign_hint(identifier, Hint(HintType.JOKE, None))
            num_joke -= 1
            identifiers.remove(identifier)

        return patches
예제 #18
0
def _create_base_patches(
    rng: Random,
    game: GameDescription,
    permalink: Permalink,
    available_pickups: List[PickupEntry],
) -> GamePatches:

    patches = GamePatches.with_game(game)
    patches = _add_elevator_connections_to_patches(permalink, patches)
    patches = patches.assign_starting_location(
        _starting_location_for_configuration(permalink.layout_configuration,
                                             game, rng))
    patches = _sky_temple_key_distribution_logic(permalink, patches,
                                                 available_pickups)

    return patches
예제 #19
0
def _add_elevator_connections_to_patches(permalink: Permalink,
                                         patches: GamePatches) -> GamePatches:

    assert patches.elevator_connection == {}
    if permalink.layout_configuration.elevators == LayoutRandomizedFlag.RANDOMIZED:
        return GamePatches(
            patches.pickup_assignment,
            claris_randomizer.elevator_connections_for_seed_number(
                permalink.seed_number),
            patches.dock_connection,
            patches.dock_weakness,
            patches.extra_initial_items,
            patches.starting_location,
        )
    else:
        return patches
예제 #20
0
def test_create_patches(mock_random: MagicMock,
                        mock_calculate_item_pool: MagicMock,
                        mock_sky_temple_key_distribution_logic: MagicMock,
                        mock_retcon_playthrough_filler: MagicMock,
                        mock_indices_for_unassigned_pickups: MagicMock,
                        empty_patches
                        ):
    # Setup
    seed_number: int = 91319
    game = default_prime2_game_description()
    status_update: Union[MagicMock, Callable[[str], None]] = MagicMock()
    configuration = LayoutConfiguration.from_params(trick_level=LayoutTrickLevel.NO_TRICKS,
                                                    sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM,
                                                    elevators=LayoutRandomizedFlag.VANILLA,
                                                    pickup_quantities={},
                                                    starting_location=StartingLocation.default(),
                                                    starting_resources=StartingResources.from_item_loss(False),
                                                    )
    permalink = Permalink(
        seed_number=seed_number,
        spoiler=True,
        patcher_configuration=PatcherConfiguration.default(),
        layout_configuration=configuration,
    )
    mock_calculate_item_pool.return_value = list(sorted(game.pickup_database.original_pickup_mapping.values()))
    mock_sky_temple_key_distribution_logic.return_value.starting_location = game.starting_location
    mock_sky_temple_key_distribution_logic.return_value.custom_initial_items = None

    filler_patches = mock_retcon_playthrough_filler.return_value

    # Run
    result = generator._create_patches(permalink, game, status_update)

    # Assert
    mock_random.assert_called_once_with(permalink.as_str)
    mock_calculate_item_pool.assert_called_once_with(permalink, game)

    mock_sky_temple_key_distribution_logic.assert_called_once_with(permalink, GamePatches.with_game(game), ANY)

    mock_retcon_playthrough_filler.assert_called_once_with(ANY, ANY, ANY,
                                                           mock_random.return_value, status_update)

    mock_indices_for_unassigned_pickups.assert_called_once_with(mock_random.return_value, game,
                                                                filler_patches.pickup_assignment, ANY)
    filler_patches.assign_new_pickups.assert_called_once_with(mock_indices_for_unassigned_pickups.return_value)

    assert result == filler_patches.assign_new_pickups.return_value
def _test_data():
    data = default_data.decode_default_prime2()
    game = data_reader.decode_data(data)
    configuration = LayoutConfiguration.from_params()
    permalink = Permalink(
        seed_number=15000,
        spoiler=True,
        patcher_configuration=PatcherConfiguration.default(),
        layout_configuration=configuration,
    )
    patches = GamePatches.with_game(game)
    patches = patches.assign_gate_assignment(
        base_patches_factory.gate_assignment_for_configuration(
            configuration, game.resource_database, Random(15000)))
    game, state = logic_bootstrap(configuration, game, patches)

    return game, state, permalink
예제 #22
0
def test_retcon_filler_integration():
    layout_configuration = LayoutConfiguration.default()

    rng = Random("fixed-seed!")
    status_update = MagicMock()

    game = data_reader.decode_data(layout_configuration.game_data)
    patches = GamePatches.with_game(game)
    available_pickups = game.pickup_database.all_useful_pickups

    logic, state = logic_bootstrap(layout_configuration, game, patches)
    logic.game.simplify_connections(state.resources)

    filler_patches = retcon.retcon_playthrough_filler(logic, state,
                                                      tuple(available_pickups),
                                                      rng, status_update)
    assert filler_patches == patches
예제 #23
0
def _sky_temple_key_distribution_logic(
    permalink: Permalink,
    previous_patches: GamePatches,
    available_pickups: List[PickupEntry],
) -> GamePatches:

    mode = permalink.layout_configuration.sky_temple_keys
    new_assignments = {}

    if mode == LayoutSkyTempleKeyMode.VANILLA:
        locations_to_place = _FLYING_ING_CACHES[:]

    elif mode == LayoutSkyTempleKeyMode.ALL_BOSSES or mode == LayoutSkyTempleKeyMode.ALL_GUARDIANS:
        locations_to_place = _GUARDIAN_INDICES[:]
        if mode == LayoutSkyTempleKeyMode.ALL_BOSSES:
            locations_to_place += _SUB_GUARDIAN_INDICES

    elif mode == LayoutSkyTempleKeyMode.FULLY_RANDOM:
        locations_to_place = []

    else:
        raise GenerationFailure("Unknown Sky Temple Key mode: {}".format(mode),
                                permalink)

    for pickup in available_pickups[:]:
        if not locations_to_place:
            break

        if pickup.item_category == "sky_temple_key":
            available_pickups.remove(pickup)
            index = locations_to_place.pop(0)
            if index in previous_patches.pickup_assignment:
                raise GenerationFailure(
                    "Attempted to place '{}' in {}, but there's already '{}' there"
                    .format(pickup, index,
                            previous_patches.pickup_assignment[index]),
                    permalink)
            new_assignments[index] = pickup

    if locations_to_place:
        raise GenerationFailure(
            "Missing Sky Temple Keys in available_pickups to place in all requested boss places",
            permalink)

    return previous_patches.assign_pickup_assignment(new_assignments)
예제 #24
0
파일: echoes.py 프로젝트: xisi/randovania
def validate_command_logic(args):
    debug._DEBUG_LEVEL = args.debug
    data = prime_database.decode_data_file(args)
    game = data_reader.decode_data(data)

    if args.layout_file is not None:
        description = LayoutDescription.from_file(Path(args.layout_file))
        configuration = description.permalink.layout_configuration
        patches = description.patches
    else:
        configuration = LayoutConfiguration.default()
        patches = GamePatches.with_game(game).assign_pickup_assignment(
            game.pickup_database.original_pickup_mapping)

    final_state_by_resolve = resolver.resolve(configuration=configuration,
                                              game=game,
                                              patches=patches)
    print(final_state_by_resolve)
예제 #25
0
    async def assign_guaranteed_indices_hints(self, patches: GamePatches, identifiers: list[NodeIdentifier],
                                              prefill: PreFillParams) -> GamePatches:
        # Specific Pickup/any LogbookNode Hints
        indices_with_hint = await self.get_guranteed_hints(patches, prefill)
        prefill.rng.shuffle(indices_with_hint)

        all_hint_identifiers = [identifier for identifier in identifiers if identifier not in patches.hints]
        prefill.rng.shuffle(all_hint_identifiers)

        for index, precision in indices_with_hint:
            if not all_hint_identifiers:
                break

            identifier = all_hint_identifiers.pop()
            patches = patches.assign_hint(identifier, Hint(HintType.LOCATION, precision, index))
            identifiers.remove(identifier)

        return patches
예제 #26
0
def calculate_item_pool(
    layout_configuration: LayoutConfiguration,
    resource_database: ResourceDatabase,
    patches: GamePatches,
) -> Tuple[GamePatches, List[PickupEntry]]:
    """
    Creates a GamePatches with all starting items and pickups in fixed locations, as well as a list of
    pickups we should shuffle.
    :param layout_configuration:
    :param resource_database:
    :param patches:
    :return:
    """

    item_pool, pickup_assignment, initial_items = calculate_pool_results(
        layout_configuration, resource_database)
    new_patches = patches.assign_pickup_assignment(
        pickup_assignment).assign_extra_initial_items(initial_items)
    return new_patches, item_pool
예제 #27
0
    async def assign_specific_location_hints(self, patches: GamePatches, prefill: PreFillParams) -> GamePatches:
        specific_location_precisions = await self.get_specific_pickup_precision_pair_overrides(patches, prefill)

        # TODO: this is an Echoes default. Should not have a default and all nodes have one in the DB.
        default_precision = PrecisionPair(HintLocationPrecision.KEYBEARER, HintItemPrecision.BROAD_CATEGORY,
                                          include_owner=True)

        wl = prefill.game.world_list
        for node in wl.iterate_nodes():
            if isinstance(node, LogbookNode) and node.lore_type == LoreType.SPECIFIC_PICKUP:
                identifier = wl.identifier_for_node(node)
                patches = patches.assign_hint(
                    identifier,
                    Hint(HintType.LOCATION,
                         specific_location_precisions.get(identifier, default_precision),
                         PickupIndex(node.hint_index))
                )

        return patches
예제 #28
0
def test_round_trip_default(permalink: Permalink,
                            item_locations: Dict[str, Dict[str, str]],
                            solver_path: Tuple[SolverPath, ...]):

    game = data_reader.decode_data(permalink.layout_configuration.game_data)
    original = LayoutDescription(
        version=randovania.VERSION,
        permalink=permalink,
        patches=GamePatches(
            _item_locations_to_pickup_assignment(game, item_locations),
            claris_randomizer.elevator_connections_for_seed_number(
                permalink.seed_number), {}, {}, (), game.starting_location),
        solver_path=solver_path,
    )

    # Run
    decoded = LayoutDescription.from_json_dict(original.as_json)

    # Assert
    assert decoded == original
def test_reach_size_from_start(echoes_game_description):
    # Setup
    configuration = LayoutConfiguration.from_params(
        trick_level_configuration=TrickLevelConfiguration(
            LayoutTrickLevel.HYPERMODE), )
    patches = GamePatches.with_game(echoes_game_description)
    patches = patches.assign_gate_assignment(
        base_patches_factory.gate_assignment_for_configuration(
            configuration, echoes_game_description.resource_database,
            Random(15000)))

    game, state = logic_bootstrap(configuration, echoes_game_description,
                                  patches)

    # Run
    reach = GeneratorReach.reach_from_state(game, state)

    # Assert
    assert len(list(reach.nodes)) == 26
    assert len(list(reach.safe_nodes)) == 4
예제 #30
0
def _test_data():
    data = default_data.decode_default_prime2()
    game = data_reader.decode_data(data, False)
    configuration = LayoutConfiguration.from_params(
        trick_level=LayoutTrickLevel.NO_TRICKS,
        sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM,
        elevators=LayoutRandomizedFlag.VANILLA,
        pickup_quantities={},
        starting_location=StartingLocation.default(),
        starting_resources=StartingResources.default(),
    )
    permalink = Permalink(
        seed_number=15000,
        spoiler=True,
        patcher_configuration=PatcherConfiguration.default(),
        layout_configuration=configuration,
    )
    logic, state = logic_bootstrap(configuration, game,
                                   GamePatches.with_game(game))

    return logic, state, permalink