コード例 #1
0
def distances_to_node(
    world_list: WorldList,
    starting_node: Node,
    *,
    ignore_elevators: bool = True,
    cutoff: Optional[int] = None,
    patches: Optional[GamePatches] = None,
) -> Dict[Area, int]:
    """
    Compute the shortest distance from a node to all reachable areas.
    :param world_list:
    :param starting_node:
    :param ignore_elevators:
    :param cutoff: Exclude areas with a length longer that cutoff.
    :param patches:
    :return: Dict keyed by area to shortest distance to starting_node.
    """
    import networkx
    g = networkx.DiGraph()

    dock_connections = patches.dock_connection if patches is not None else {}
    elevator_connections: ElevatorConnection = patches.elevator_connection if patches is not None else {}

    for area in world_list.all_areas:
        g.add_node(area)

    for world in world_list.worlds:
        for area in world.areas:
            new_areas = set()
            for node in area.nodes:
                connection = None
                if isinstance(node, DockNode):
                    connection = dock_connections.get(
                        world_list.identifier_for_node(node),
                        node.default_connection).area_identifier

                elif isinstance(node, TeleporterNode) and not ignore_elevators:
                    connection = elevator_connections.get(
                        world_list.identifier_for_node(node),
                        node.default_connection)

                if connection is not None:
                    new_areas.add(world_list.area_by_area_location(connection))

            for next_area in new_areas:
                g.add_edge(area, next_area)

    return networkx.single_source_shortest_path_length(
        g, world_list.nodes_to_area(starting_node), cutoff)
コード例 #2
0
def _get_nodes_by_teleporter_id(
        world_list: WorldList) -> Dict[NodeIdentifier, TeleporterNode]:
    return {
        world_list.identifier_for_node(node): node
        for node in world_list.all_nodes
        if isinstance(node, TeleporterNode) and node.editable
    }
コード例 #3
0
    def fill_unassigned_hints(self, patches: GamePatches,
                              world_list: WorldList,
                              rng: Random,
                              scan_asset_initial_pickups: dict[NodeIdentifier, frozenset[PickupIndex]],
                              ) -> GamePatches:
        new_hints = copy.copy(patches.hints)

        # Get all LogbookAssets from the WorldList
        potential_hint_locations: set[NodeIdentifier] = {
            world_list.identifier_for_node(node)
            for node in world_list.iterate_nodes()
            if isinstance(node, LogbookNode)
        }
        for logbook in potential_hint_locations:
            if logbook not in scan_asset_initial_pickups:
                scan_asset_initial_pickups[logbook] = frozenset()

        # But remove these that already have hints
        potential_hint_locations -= patches.hints.keys()

        # We try our best to not hint the same thing twice
        hinted_indices: set[PickupIndex] = {hint.target for hint in patches.hints.values() if hint.target is not None}

        # Get interesting items to place hints for
        possible_indices: set[PickupIndex] = {
            index
            for index, target in patches.pickup_assignment.items()
            if self.interesting_pickup_to_hint(target.pickup)
        }
        possible_indices -= hinted_indices

        debug.debug_print("fill_unassigned_hints had {} decent indices for {} hint locations".format(
            len(possible_indices), len(potential_hint_locations)))

        if debug.debug_level() > 1:
            print(f"> Num pickups per asset:")
            for asset, pickups in scan_asset_initial_pickups.items():
                print(f"* {asset}: {len(pickups)} pickups")
            print("> Done.")

        all_pickup_indices = [
            node.pickup_index
            for node in world_list.iterate_nodes()
            if isinstance(node, PickupNode)
        ]
        rng.shuffle(all_pickup_indices)

        # If there isn't enough indices, use unhinted non-majors placed by generator
        if (num_indices_needed := len(potential_hint_locations) - len(possible_indices)) > 0:
            potential_indices = [
                index for index in all_pickup_indices
                if index not in possible_indices and index not in hinted_indices
            ]
            debug.debug_print(
                f"Had only {len(possible_indices)} hintable indices, but needed {len(potential_hint_locations)}."
                f" Found {len(potential_indices)} less desirable locations.")
            possible_indices |= set(potential_indices[:num_indices_needed])
コード例 #4
0
def create_elevator_database(world_list: WorldList,
                             all_teleporters: List[NodeIdentifier],
                             ) -> Tuple[ElevatorHelper, ...]:
    """
    Creates a tuple of Elevator objects, exclude those that belongs to one of the areas provided.
    :param world_list:
    :param all_teleporters: Set of teleporters to use
    :return:
    """
    all_helpers = [
        ElevatorHelper(world_list.identifier_for_node(node), node.default_connection)

        for world, area, node in world_list.all_worlds_areas_nodes
        if isinstance(node, TeleporterNode)
    ]
    return tuple(
        helper
        for helper in all_helpers
        if helper.teleporter in all_teleporters
    )
コード例 #5
0
ファイル: hints.py プロジェクト: randovania/randovania
def create_patches_hints(
    all_patches: Dict[int, GamePatches],
    players_config: PlayersConfiguration,
    world_list: WorldList,
    namer: HintNamer,
    rng: Random,
) -> list:
    exporter = HintExporter(namer, rng, JOKE_HINTS)

    hints_for_asset: dict[NodeIdentifier, str] = {}
    for identifier, hint in all_patches[
            players_config.player_index].hints.items():
        hints_for_asset[identifier] = exporter.create_message_for_hint(
            hint, all_patches, players_config, True)

    return [
        create_simple_logbook_hint(
            logbook_node.string_asset_id,
            hints_for_asset.get(world_list.identifier_for_node(logbook_node),
                                "Someone forgot to leave a message."),
        ) for logbook_node in world_list.iterate_nodes()
        if isinstance(logbook_node, LogbookNode)
    ]
コード例 #6
0
    def fill_unassigned_hints(
        self,
        patches: GamePatches,
        world_list: WorldList,
        rng: Random,
        scan_asset_initial_pickups: dict[NodeIdentifier,
                                         frozenset[PickupIndex]],
    ) -> GamePatches:
        new_hints = copy.copy(patches.hints)

        # Get all LogbookAssets from the WorldList
        potential_hint_locations: set[NodeIdentifier] = {
            world_list.identifier_for_node(node)
            for node in world_list.all_nodes if isinstance(node, LogbookNode)
        }
        for logbook in potential_hint_locations:
            if logbook not in scan_asset_initial_pickups:
                scan_asset_initial_pickups[logbook] = frozenset()

        # But remove these that already have hints
        potential_hint_locations -= patches.hints.keys()

        # Get interesting items to place hints for
        possible_indices = set(patches.pickup_assignment.keys())
        possible_indices -= {
            hint.target
            for hint in patches.hints.values() if hint.target is not None
        }
        possible_indices -= {
            index
            for index in possible_indices
            if not self.interesting_pickup_to_hint(
                patches.pickup_assignment[index].pickup)
        }

        debug.debug_print(
            "fill_unassigned_hints had {} decent indices for {} hint locations"
            .format(len(possible_indices), len(potential_hint_locations)))

        if debug.debug_level() > 1:
            print(f"> Num pickups per asset:")
            for asset, pickups in scan_asset_initial_pickups.items():
                print(f"* {asset}: {len(pickups)} pickups")
            print("> Done.")

        # But if we don't have enough hints, just pick randomly from everything
        if len(possible_indices) < len(potential_hint_locations):
            possible_indices = {
                node.pickup_index
                for node in world_list.all_nodes
                if isinstance(node, PickupNode)
            }

        # Get an stable order
        ordered_possible_indices = list(sorted(possible_indices))
        ordered_potential_hint_locations = list(
            sorted(potential_hint_locations))

        num_logbooks: dict[PickupIndex, int] = {
            index: sum(1 for indices in scan_asset_initial_pickups.values()
                       if index in indices)
            for index in ordered_possible_indices
        }
        max_seen = max(num_logbooks.values()) if num_logbooks else 0
        pickup_indices_weight: dict[PickupIndex, int] = {
            index: max_seen - num_logbook
            for index, num_logbook in num_logbooks.items()
        }
        # Ensure all indices are present with at least weight 0
        for index in ordered_possible_indices:
            if index not in pickup_indices_weight:
                pickup_indices_weight[index] = 0

        for logbook in sorted(ordered_potential_hint_locations,
                              key=lambda r: len(scan_asset_initial_pickups[r]),
                              reverse=True):
            try:
                new_index = random_lib.select_element_with_weight(
                    pickup_indices_weight, rng)
            except StopIteration:
                # If everything has weight 0, then just choose randomly.
                new_index = random_lib.random_key(pickup_indices_weight, rng)

            del pickup_indices_weight[new_index]

            new_hints[logbook] = Hint(HintType.LOCATION, None, new_index)
            debug.debug_print(
                f"Added hint at {logbook} for item at {new_index}")

        return dataclasses.replace(patches, hints=new_hints)