Example #1
0
def pickups_to_solve_list(pickup_pool: List[PickupEntry],
                          requirement_list: RequirementList, state: State):
    pickups = []

    resources = copy.copy(state.resources)
    pickups_for_this = list(pickup_pool)

    for individual in sorted(requirement_list.values()):
        if individual.satisfied(resources, state.energy):
            continue

        # FIXME: this picks Dark Beam to provide Dark Ammo

        # Create another copy of the list so we can remove elements while iterating
        for pickup in list(pickups_for_this):
            new_resources = resource_info.convert_resource_gain_to_current_resources(
                pickup.resource_gain(resources))
            pickup_progression = resource_info.convert_resource_gain_to_current_resources(
                pickup.progression)
            if new_resources.get(individual.resource,
                                 0) + pickup_progression.get(
                                     individual.resource, 0) > 0:
                pickups.append(pickup)
                pickups_for_this.remove(pickup)
                resource_info.add_resources_into_another(
                    resources, new_resources)

            if individual.satisfied(resources, state.energy):
                break

        if not individual.satisfied(resources, state.energy):
            return None

    return pickups
Example #2
0
def find_locations_that_gives_items(
    target_items: List[ItemResourceInfo],
    all_patches: Dict[int, GamePatches],
    player: int,
) -> dict[ItemResourceInfo, list[tuple[int, PickupLocation]]]:
    result: dict[ItemResourceInfo, list[tuple[int, PickupLocation]]] = {
        item: []
        for item in target_items
    }

    for other_player, patches in all_patches.items():
        for pickup_index, target in patches.pickup_assignment.items():
            if target.player != player:
                continue

            # TODO: iterate over all tiers of progression
            resources = resource_info.convert_resource_gain_to_current_resources(
                target.pickup.resource_gain({}))

            for resource, quantity in resources.items():
                if quantity > 0 and resource in result:
                    result[resource].append(
                        (other_player,
                         PickupLocation(patches.configuration.game,
                                        pickup_index)))

    return result
Example #3
0
def test_convert_resource_gain_to_current_resources(resource_gain, expected):
    # Setup
    # Run
    result = convert_resource_gain_to_current_resources(resource_gain)

    # Assert
    assert result == expected
Example #4
0
    def assign_pickup_to_starting_items(self, pickup: PickupEntry) -> "State":
        pickup_resources = convert_resource_gain_to_current_resources(
            pickup.resource_gain(self.resources))

        # Make sure there's no item percentage on starting items
        pickup_resources.pop(self.resource_database.item_percentage, None)

        new_resources = copy.copy(self.resources)
        add_resources_into_another(new_resources, pickup_resources)
        new_patches = self.patches.assign_extra_initial_items(pickup_resources)

        if self.patches.game_specific is not None:
            energy_per_tank = self.patches.game_specific.energy_per_tank
        else:
            energy_per_tank = 100

        return State(
            new_resources,
            self.collected_resource_nodes,
            self.energy +
            _energy_tank_difference(new_resources, self.resources,
                                    self.resource_database) * energy_per_tank,
            self.node,
            new_patches,
            self,
            self.resource_database,
            self.world_list,
        )
Example #5
0
def find_area_errors(game: GameDescription, area: Area) -> Iterator[str]:
    nodes_with_paths_in = set()
    for node in area.nodes:
        nodes_with_paths_in.update(area.connections[node].keys())

        for error in find_node_errors(game, node):
            yield f"{area.name} - {error}"

        if node in area.connections.get(node, {}):
            yield f"{area.name} - Node '{node.name}' has a connection to itself"

    if area.default_node is not None and area.node_with_name(
            area.default_node) is None:
        yield f"{area.name} has default node {area.default_node}, but no node with that name exists"

    # elif area.default_node is not None:
    #     nodes_with_paths_in.add(area.node_with_name(area.default_node))

    for node in area.nodes:
        if isinstance(node,
                      (TeleporterNode, DockNode)) or area.connections[node]:
            continue

        # FIXME: cannot implement this for PickupNodes because their resource gain depends on GamePatches
        if isinstance(node, EventNode):
            # if this node would satisfy the victory condition, it does not need outgoing connections
            current = convert_resource_gain_to_current_resources(
                node.resource_gain_on_collect(None))
            if game.victory_condition.satisfied(current, 0,
                                                game.resource_database):
                continue

        if node in nodes_with_paths_in:
            yield f"{area.name} - '{node.name}': Node has paths in, but no connections out."
Example #6
0
def create_temple_key_hint(
    all_patches: Dict[int, GamePatches],
    player_index: int,
    temple: HintDarkTemple,
    world_list: WorldList,
) -> str:
    """
    Creates the text for .
    :param all_patches:
    :param player_index:
    :param temple:
    :param world_list:
    :return:
    """
    all_world_names = set()

    _TEMPLE_NAMES = ["Dark Agon Temple", "Dark Torvus Temple", "Hive Temple"]
    temple_index = [
        HintDarkTemple.AGON_WASTES, HintDarkTemple.TORVUS_BOG,
        HintDarkTemple.SANCTUARY_FORTRESS
    ].index(temple)
    keys = echoes_items.DARK_TEMPLE_KEY_ITEMS[temple_index]

    index_to_node = {
        node.pickup_index: node
        for node in world_list.all_nodes if isinstance(node, PickupNode)
    }

    for patches in all_patches.values():
        for pickup_index, target in patches.pickup_assignment.items():
            if target.player != player_index:
                continue

            resources = resource_info.convert_resource_gain_to_current_resources(
                target.pickup.resource_gain({}))
            for resource, quantity in resources.items():
                if quantity < 1 or resource.index not in keys:
                    continue

                pickup_node = index_to_node[pickup_index]
                all_world_names.add(
                    world_list.world_name_from_node(pickup_node, True))

    temple_name = hint_lib.color_text(hint_lib.TextColor.ITEM,
                                      _TEMPLE_NAMES[temple_index])
    names_sorted = [
        hint_lib.color_text(hint_lib.TextColor.LOCATION, world)
        for world in sorted(all_world_names)
    ]
    if len(names_sorted) == 0:
        return f"The keys to {temple_name} are nowhere to be found."
    elif len(names_sorted) == 1:
        return f"The keys to {temple_name} can all be found in {names_sorted[0]}."
    else:
        last = names_sorted.pop()
        front = ", ".join(names_sorted)
        return f"The keys to {temple_name} can be found in {front} and {last}."
Example #7
0
def test_logbook_node_resource_gain_on_collect(logbook_node,
                                               empty_patches):
    # Setup
    node = logbook_node[-1]

    # Run
    gain = node.resource_gain_on_collect(empty_patches, {})

    # Assert
    assert convert_resource_gain_to_current_resources(gain) == {node.resource(): 1}
Example #8
0
    def assign_pickup_to_starting_items(self, pickup: PickupEntry) -> "State":
        pickup_resources = convert_resource_gain_to_current_resources(
            pickup.resource_gain(self.resources))

        # Make sure there's no item percentage on starting items
        pickup_resources.pop(self.resource_database.item_percentage, None)

        new_resources = copy.copy(self.resources)
        add_resources_into_another(new_resources, pickup_resources)
        new_patches = self.patches.assign_extra_initial_items(pickup_resources)

        return State(
            new_resources, self.collected_resource_nodes, self.energy +
            _energy_tank_difference(new_resources, self.resources,
                                    self.resource_database) * ENERGY_PER_TANK,
            self.node, new_patches, self, self.resource_database)
Example #9
0
    def assign_pickup_to_starting_items(self, pickup: PickupEntry) -> "State":
        pickup_resources = convert_resource_gain_to_current_resources(
            pickup.resource_gain(self.resources, force_lock=True))

        # Make sure there's no item percentage on starting items
        if self.resource_database.item_percentage is not None:
            pickup_resources.pop(self.resource_database.item_percentage, None)

        new_resources = copy.copy(self.resources)
        add_resources_into_another(new_resources, pickup_resources)
        new_patches = self.patches.assign_extra_initial_items(pickup_resources)

        tank_delta = _energy_tank_difference(new_resources, self.resources, self.resource_database)

        return State(
            new_resources,
            self.collected_resource_nodes,
            self.energy + tank_delta * self.game_data.energy_per_tank,
            self.node,
            new_patches,
            self,
            self.game_data,
        )
Example #10
0
def create_hints(
    patches: GamePatches,
    world_list: WorldList,
    hide_area: bool,
) -> list:
    """
    Creates the string patches entries that changes the Sky Temple Gateway hint scans with hints for where
    the STK actually are.
    :param patches:
    :param world_list:
    :param hide_area: Should the hint include only the world?
    :return:
    """
    location_hint_creator = LocationHintCreator(world_list)
    sky_temple_key_hints = {}

    for pickup_index, pickup in patches.pickup_assignment.items():
        resources = resource_info.convert_resource_gain_to_current_resources(
            pickup.resource_gain({}))

        for resource, quantity in resources.items():
            if quantity < 1:
                continue

            try:
                key_number = echoes_items.SKY_TEMPLE_KEY_ITEMS.index(
                    resource.index) + 1
            except ValueError:
                continue

            assert resource.index not in sky_temple_key_hints

            sky_temple_key_hints[
                resource.index] = "{} is located in {}.".format(
                    _sky_temple_key_name(key_number),
                    color_text(
                        TextColor.LOCATION,
                        location_hint_creator.index_node_name(
                            pickup_index, hide_area),
                    ),
                )

    for starting_resource, quantity in patches.starting_items.items():
        if quantity < 1:
            continue
        try:
            key_number = echoes_items.SKY_TEMPLE_KEY_ITEMS.index(
                starting_resource.index) + 1
        except ValueError:
            continue

        assert starting_resource.index not in sky_temple_key_hints
        sky_temple_key_hints[
            starting_resource.index] = "{} has no need to be located.".format(
                _sky_temple_key_name(key_number), )

    if len(sky_temple_key_hints) != len(echoes_items.SKY_TEMPLE_KEY_ITEMS):
        raise ValueError(
            "Expected to find {} Sky Temple Keys between pickup placement and starting items, found {}"
            .format(len(echoes_items.SKY_TEMPLE_KEY_ITEMS),
                    len(sky_temple_key_hints)))

    return [
        create_simple_logbook_hint(_SKY_TEMPLE_KEY_SCAN_ASSETS[key_number],
                                   sky_temple_key_hints[key_index]) for
        key_number, key_index in enumerate(echoes_items.SKY_TEMPLE_KEY_ITEMS)
    ]