Example #1
0
def _default_filler_config() -> FillerConfiguration:
    return FillerConfiguration(
        randomization_mode=RandomizationMode.FULL,
        minimum_random_starting_items=0,
        maximum_random_starting_items=0,
        indices_to_exclude=frozenset(),
        multi_pickup_placement=False,
        logical_resource_action=LayoutLogicalResourceAction.RANDOMLY,
        first_progression_must_be_local=False,
        minimum_available_locations_for_hint_placement=0,
        minimum_location_weight_for_hint_placement=0,
    )
def test_build_available_indices(major_mode: RandomizationMode, has_exclusion: bool):
    # Setup
    world_a = MagicMock()
    world_a.pickup_indices = [PickupIndex(1), PickupIndex(2)]
    world_a.major_pickup_indices = [PickupIndex(1)]

    world_b = MagicMock()
    world_b.pickup_indices = [PickupIndex(3), PickupIndex(4)]
    world_b.major_pickup_indices = [PickupIndex(3)]

    world_list = MagicMock()
    world_list.worlds = [world_a, world_b]

    if has_exclusion:
        exclusion = frozenset([PickupIndex(3)])
    else:
        exclusion = frozenset()
    configuration = FillerConfiguration(
        randomization_mode=major_mode,
        minimum_random_starting_items=0,
        maximum_random_starting_items=0,
        indices_to_exclude=exclusion,
        multi_pickup_placement=False,
        multi_pickup_new_weighting=False,
        logical_resource_action=LayoutLogicalResourceAction.RANDOMLY,
        first_progression_must_be_local=False,
        minimum_available_locations_for_hint_placement=0,
        minimum_location_weight_for_hint_placement=0,
    )

    # Run
    indices_per_world, all_indices = randovania.generator.filler.player_state.build_available_indices(world_list,
                                                                                                      configuration)

    # Assert
    if major_mode == RandomizationMode.FULL:
        a_pickups = {PickupIndex(1), PickupIndex(2)}
        b_pickups = {PickupIndex(3), PickupIndex(4)}
    else:
        a_pickups = {PickupIndex(1)}
        b_pickups = {PickupIndex(3)}

    if has_exclusion:
        b_pickups.remove(PickupIndex(3))

    assert indices_per_world == [a_pickups, b_pickups]
    assert all_indices == a_pickups | b_pickups
Example #3
0
def test_build_available_indices(major_mode: RandomizationMode,
                                 has_exclusion: bool):
    # Setup
    world_a = MagicMock()
    world_a.pickup_indices = [PickupIndex(1), PickupIndex(2)]
    world_a.major_pickup_indices = [PickupIndex(1)]

    world_b = MagicMock()
    world_b.pickup_indices = [PickupIndex(3), PickupIndex(4)]
    world_b.major_pickup_indices = [PickupIndex(3)]

    world_list = MagicMock()
    world_list.worlds = [world_a, world_b]

    if has_exclusion:
        exclusion = frozenset([PickupIndex(3)])
    else:
        exclusion = frozenset()
    configuration = FillerConfiguration(major_mode, 0, 0, exclusion, False,
                                        LayoutLogicalResourceAction.RANDOMLY,
                                        False, 0, 0.0)

    # Run
    indices_per_world, all_indices = randovania.generator.filler.player_state.build_available_indices(
        world_list, configuration)

    # Assert
    if major_mode == RandomizationMode.FULL:
        a_pickups = {PickupIndex(1), PickupIndex(2)}
        b_pickups = {PickupIndex(3), PickupIndex(4)}
    else:
        a_pickups = {PickupIndex(1)}
        b_pickups = {PickupIndex(3)}

    if has_exclusion:
        b_pickups.remove(PickupIndex(3))

    assert indices_per_world == [a_pickups, b_pickups]
    assert all_indices == a_pickups | b_pickups
def test_retcon_filler_integration(default_layout_configuration):
    layout_configuration = default_layout_configuration

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

    game = default_database.game_description_for(layout_configuration.game)
    patches = game.create_game_patches()
    available_pickups = game.pickup_database.all_useful_pickups

    new_game, state = logic_bootstrap(layout_configuration, game, patches)
    new_game.patch_requirements(state.resources,
                                layout_configuration.damage_strictness.value)

    filler_patches = retcon.retcon_playthrough_filler(
        new_game, state, tuple(available_pickups), rng,
        FillerConfiguration(
            randomization_mode=RandomizationMode.FULL,
            minimum_random_starting_items=0,
            maximum_random_starting_items=0,
            indices_to_exclude=frozenset(),
        ), status_update)
    assert filler_patches == patches
Example #5
0
async def run_filler(
    rng: Random,
    player_pools: list[PlayerPool],
    status_update: Callable[[str], None],
) -> FillerResults:
    """
    Runs the filler logic for the given configuration and item pool.
    Returns a GamePatches with progression items and hints assigned, along with all items in the pool
    that weren't assigned.

    :param player_pools:
    :param rng:
    :param status_update:
    :return:
    """

    player_states = []
    player_expansions: dict[int, list[PickupEntry]] = {}

    for index, pool in enumerate(player_pools):
        config = pool.configuration

        status_update(f"Creating state for player {index + 1}")
        if config.multi_pickup_placement:
            major_items, player_expansions[index] = list(pool.pickups), []
        else:
            major_items, player_expansions[index] = _split_expansions(
                pool.pickups)
        rng.shuffle(major_items)
        rng.shuffle(player_expansions[index])

        new_game, state = pool.game_generator.bootstrap.logic_bootstrap(
            config, pool.game, pool.patches)
        major_configuration = config.major_items_configuration
        player_states.append(
            PlayerState(
                index=index,
                game=new_game,
                initial_state=state,
                pickups_left=major_items,
                configuration=FillerConfiguration(
                    randomization_mode=config.available_locations.
                    randomization_mode,
                    minimum_random_starting_items=major_configuration.
                    minimum_random_starting_items,
                    maximum_random_starting_items=major_configuration.
                    maximum_random_starting_items,
                    indices_to_exclude=config.available_locations.
                    excluded_indices,
                    multi_pickup_placement=config.multi_pickup_placement,
                    multi_pickup_new_weighting=config.
                    multi_pickup_new_weighting,
                    logical_resource_action=config.logical_resource_action,
                    first_progression_must_be_local=config.
                    first_progression_must_be_local,
                    minimum_available_locations_for_hint_placement=config.
                    minimum_available_locations_for_hint_placement,
                    minimum_location_weight_for_hint_placement=config.
                    minimum_location_weight_for_hint_placement,
                ),
            ))

    try:
        filler_result, actions_log = retcon_playthrough_filler(
            rng, player_states, status_update=status_update)
    except UnableToGenerate as e:
        message = "{}\n\n{}".format(
            str(e),
            "\n\n".join(
                "#### Player {}\n{}".format(player.index +
                                            1, player.current_state_report())
                for player in player_states),
        )
        debug.debug_print(message)
        raise UnableToGenerate(message) from e

    results = {}
    for player_state, patches in filler_result.items():
        player_pool = player_pools[player_state.index]

        hint_distributor = player_pool.game_generator.hint_distributor
        results[player_state.index] = FillerPlayerResult(
            game=player_state.game,
            patches=await
            hint_distributor.assign_post_filler_hints(patches, rng,
                                                      player_pool,
                                                      player_state),
            unassigned_pickups=player_state.pickups_left +
            player_expansions[player_state.index],
        )

    return FillerResults(results, actions_log)
Example #6
0
async def run_filler(
    rng: Random,
    player_pools: Dict[int, PlayerPool],
    status_update: Callable[[str], None],
) -> FillerResults:
    """
    Runs the filler logic for the given configuration and item pool.
    Returns a GamePatches with progression items and hints assigned, along with all items in the pool
    that weren't assigned.

    :param player_pools:
    :param rng:
    :param status_update:
    :return:
    """

    player_states = []
    player_expansions: Dict[int, List[PickupEntry]] = {}

    for index, pool in player_pools.items():
        status_update(f"Creating state for player {index + 1}")
        if pool.configuration.multi_pickup_placement and False:
            major_items, player_expansions[index] = list(pool.pickups), []
        else:
            major_items, player_expansions[index] = _split_expansions(
                pool.pickups)
        rng.shuffle(major_items)
        rng.shuffle(player_expansions[index])

        new_game, state = bootstrap.logic_bootstrap(pool.configuration,
                                                    pool.game, pool.patches)

        major_configuration = pool.configuration.major_items_configuration
        player_states.append(
            PlayerState(
                index=index,
                game=new_game,
                initial_state=state,
                pickups_left=major_items,
                configuration=FillerConfiguration(
                    randomization_mode=pool.configuration.available_locations.
                    randomization_mode,
                    minimum_random_starting_items=major_configuration.
                    minimum_random_starting_items,
                    maximum_random_starting_items=major_configuration.
                    maximum_random_starting_items,
                    indices_to_exclude=pool.configuration.available_locations.
                    excluded_indices,
                    multi_pickup_placement=pool.configuration.
                    multi_pickup_placement,
                ),
            ))

    try:
        filler_result, actions_log = retcon_playthrough_filler(
            rng, player_states, status_update=status_update)
    except UnableToGenerate as e:
        message = "{}\n\n{}".format(
            str(e),
            "\n\n".join(
                "#### Player {}\n{}".format(player.index +
                                            1, player.current_state_report())
                for player in player_states),
        )
        debug.debug_print(message)
        raise UnableToGenerate(message) from e

    results = {}

    for player_state, patches in filler_result.items():
        game = player_state.game

        if game.game == RandovaniaGame.PRIME2:
            # Since we haven't added expansions yet, these hints will always be for items added by the filler.
            full_hints_patches = fill_unassigned_hints(
                patches, game.world_list, rng,
                player_state.scan_asset_initial_pickups)

            if player_pools[player_state.index].configuration.hints.item_hints:
                result = add_hints_precision(player_state, full_hints_patches,
                                             rng)
            else:
                result = replace_hints_without_precision_with_jokes(
                    full_hints_patches)
        else:
            result = patches

        results[player_state.index] = FillerPlayerResult(
            game=game,
            patches=result,
            unassigned_pickups=player_state.pickups_left +
            player_expansions[player_state.index],
        )

    return FillerResults(results, actions_log)