Exemple #1
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)
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.create_from_game(
        game_description, 0, default_cs_configuration,
    ).assign_starting_location(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
Exemple #3
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.create_from_game(game, player_index, configuration)

        # 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
Exemple #4
0
def empty_patches(default_blank_configuration,
                  blank_game_description) -> GamePatches:
    configuration = default_blank_configuration
    return GamePatches.create_from_game(blank_game_description, 0,
                                        configuration)
Exemple #5
0
def prime_game_patches(default_prime_configuration,
                       prime_game_description) -> GamePatches:
    return GamePatches.create_from_game(prime_game_description, 0,
                                        default_prime_configuration)
Exemple #6
0
def echoes_game_patches(default_echoes_configuration,
                        echoes_game_description) -> GamePatches:
    return GamePatches.create_from_game(echoes_game_description, 0,
                                        default_echoes_configuration)
def decode_single(player_index: int, all_pools: dict[int, PoolResults],
                  game: GameDescription, game_modifications: dict,
                  configuration: BaseConfiguration) -> GamePatches:
    """
    Decodes a dict created by `serialize` back into a GamePatches.
    :param player_index:
    :param all_pools:
    :param game:
    :param game_modifications:
    :param configuration:
    :return:
    """
    world_list = game.world_list
    weakness_db = game.dock_weakness_database

    if game_modifications["game"] != game.game.value:
        raise ValueError(
            f"Expected '{game.game.value}', got '{game_modifications['game']}'"
        )

    initial_pickup_assignment = all_pools[player_index].assignment

    # Starting Location
    starting_location = AreaIdentifier.from_string(
        game_modifications["starting_location"])

    # Initial items
    starting_items = ResourceCollection.from_dict(
        game.resource_database, {
            find_resource_info_with_long_name(game.resource_database.item,
                                              resource_name): quantity
            for resource_name, quantity in
            game_modifications["starting_items"].items()
        })

    # Elevators
    elevator_connection = [(world_list.get_teleporter_node(
        NodeIdentifier.from_string(source_name)),
                            AreaIdentifier.from_string(target_name))
                           for source_name, target_name in
                           game_modifications["teleporters"].items()]

    # Dock Weakness
    def get_dock(ni: NodeIdentifier):
        result = game.world_list.node_by_identifier(ni)
        assert isinstance(result, DockNode)
        return result

    dock_weakness = [(get_dock(NodeIdentifier.from_string(source_name)),
                      weakness_db.get_by_weakness(
                          weakness_data["type"],
                          weakness_data["name"],
                      )) for source_name, weakness_data in
                     game_modifications["dock_weakness"].items()]

    # Configurable Nodes
    configurable_nodes = {
        NodeIdentifier.from_string(identifier):
        data_reader.read_requirement(requirement, game.resource_database)
        for identifier, requirement in
        game_modifications["configurable_nodes"].items()
    }

    # Pickups
    target_name_re = re.compile(r"(.*) for Player (\d+)")

    pickup_assignment: PickupAssignment = {}
    for world_name, world_data in game_modifications["locations"].items():
        for area_node_name, target_name in typing.cast(dict[str, str],
                                                       world_data).items():
            if target_name == _ETM_NAME:
                continue

            pickup_name_match = target_name_re.match(target_name)
            if pickup_name_match is not None:
                pickup_name = pickup_name_match.group(1)
                target_player = int(pickup_name_match.group(2)) - 1
            else:
                pickup_name = target_name
                target_player = 0

            node_identifier = NodeIdentifier.create(
                world_name, *area_node_name.split("/", 1))
            node = world_list.node_by_identifier(node_identifier)
            assert isinstance(node, PickupNode)
            if node.pickup_index in initial_pickup_assignment:
                pickup = initial_pickup_assignment[node.pickup_index]
                if (pickup_name, target_player) != (pickup.name, player_index):
                    raise ValueError(
                        f"{area_node_name} should be vanilla based on configuration"
                    )

            elif pickup_name != _ETM_NAME:
                configuration_item_pool = all_pools[target_player].pickups
                pickup = _find_pickup_with_name(configuration_item_pool,
                                                pickup_name)
                configuration_item_pool.remove(pickup)
            else:
                pickup = None

            if pickup is not None:
                pickup_assignment[node.pickup_index] = PickupTarget(
                    pickup, target_player)

    # Hints
    hints = {}
    for identifier_str, hint in game_modifications["hints"].items():
        hints[NodeIdentifier.from_string(identifier_str)] = Hint.from_json(
            hint)

    patches = GamePatches.create_from_game(game, player_index, configuration)
    patches = patches.assign_dock_weakness(dock_weakness)
    patches = patches.assign_elevators(elevator_connection)
    return dataclasses.replace(
        patches,
        pickup_assignment=pickup_assignment,  # PickupAssignment
        configurable_nodes=configurable_nodes,
        starting_items=starting_items,  # ResourceGainTuple
        starting_location=starting_location,  # AreaIdentifier
        hints=hints,
    )