def test_decode(mock_dictionary_byte_hash: MagicMock):
    mock_dictionary_byte_hash.return_value = 120
    # We're mocking the database hash to avoid breaking tests every single time we change the database

    # This test should break whenever we change how permalinks are created
    # When this happens, we must bump the permalink version and change the tests
    encoded = "gAAAfReLCAAC4wAAAOaANg=="

    expected = Permalink(
        seed_number=1000,
        spoiler=True,
        patcher_configuration=PatcherConfiguration(
            menu_mod=True,
            warp_to_start=False,
        ),
        layout_configuration=LayoutConfiguration.from_params(
            trick_level_configuration=TrickLevelConfiguration(
                LayoutTrickLevel.HARD),
            elevators=LayoutElevators.RANDOMIZED,
        ),
    )

    # Uncomment this line to quickly get the new encoded permalink
    # assert expected.as_str == ""
    # print(expected.as_str)

    # Run
    link = Permalink.from_str(encoded)

    # Assert
    assert link == expected
def test_round_trip_generated_patches(echoes_game_data):
    # Setup
    configuration = LayoutConfiguration.from_params(
        trick_level_configuration=TrickLevelConfiguration(
            global_level=LayoutTrickLevel.MINIMAL_RESTRICTIONS,
            specific_levels={},
        ))

    patches = generator._create_randomized_patches(
        permalink=Permalink(
            seed_number=1000,
            spoiler=True,
            patcher_configuration=PatcherConfiguration.default(),
            layout_configuration=configuration,
        ),
        game=data_reader.decode_data(echoes_game_data),
        status_update=lambda x: None,
    )

    # Run
    encoded = game_patches_serializer.serialize(patches, echoes_game_data)
    decoded = game_patches_serializer.decode(encoded, configuration)

    # Assert
    assert patches == decoded
Beispiel #3
0
def test_distribute_command_logic(mock_generate_list: MagicMock, ):
    # Setup
    args = MagicMock()
    args.trick_level = LayoutTrickLevel.HARD.value
    args.major_items_mode = False
    args.sky_temple_keys = LayoutSkyTempleKeyMode.ALL_BOSSES.value
    args.skip_item_loss = True
    args.seed = 15000
    args.output_file = "asdfasdf/qwerqwerqwer/zxcvzxcv.json"

    # Run
    echoes.distribute_command_logic(args)

    # Assert
    mock_generate_list.assert_called_once_with(permalink=Permalink(
        seed_number=args.seed,
        spoiler=True,
        patcher_configuration=PatcherConfiguration.default(),
        layout_configuration=LayoutConfiguration.from_params(
            trick_level=LayoutTrickLevel.HARD,
            sky_temple_keys=LayoutSkyTempleKeyMode.ALL_BOSSES,
            elevators=LayoutRandomizedFlag.VANILLA,
            pickup_quantities={},
            starting_location=StartingLocation.default(),
            starting_resources=StartingResources.from_non_custom_configuration(
                StartingResourcesConfiguration.VANILLA_ITEM_LOSS_DISABLED),
        ),
    ),
                                               status_update=ANY)

    save_file_mock: MagicMock = mock_generate_list.return_value.save_to_file
    save_file_mock.assert_called_once_with(Path(args.output_file))
Beispiel #4
0
def distribute_command_logic(args):
    debug._DEBUG_LEVEL = args.debug

    def status_update(s):
        pass

    seed_number = args.seed
    if seed_number is None:
        seed_number = random.randint(0, 2**31)

    print("Using seed: {}".format(seed_number))
    permalink = Permalink(
        seed_number=seed_number,
        spoiler=True,
        patcher_configuration=PatcherConfiguration.default(),
        layout_configuration=get_layout_configuration_from_args(args),
    )

    before = time.perf_counter()
    layout_description = generator.generate_list(permalink=permalink,
                                                 status_update=status_update)
    after = time.perf_counter()
    print("Took {} seconds. Hash: {}".format(
        after - before,
        hash(tuple(layout_description.pickup_assignment.items()))))
    layout_description.save_to_file(Path(args.output_file))
Beispiel #5
0
 def default(cls) -> "Permalink":
     return Permalink(
         seed_number=0,
         spoiler=True,
         patcher_configuration=PatcherConfiguration.default(),
         layout_configuration=LayoutConfiguration.default(),
     )
Beispiel #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=())
Beispiel #7
0
def _decode_preset(decoder: BitPackDecoder, manager: PresetManager) -> Preset:
    included_presets = [versioned.get_preset() for versioned in manager.included_presets]

    is_custom_preset = bitpacking.decode_bool(decoder)
    reference_preset = decoder.decode_element(included_presets)
    if is_custom_preset:
        patcher_configuration = PatcherConfiguration.bit_pack_unpack(
            decoder, {"reference": reference_preset.patcher_configuration})
        layout_configuration = LayoutConfiguration.bit_pack_unpack(
            decoder, {"reference": reference_preset.layout_configuration})
        preset = Preset(
            name="{} Custom".format(reference_preset.name),
            description="A customized preset.",
            base_preset_name=reference_preset.name,
            patcher_configuration=patcher_configuration,
            layout_configuration=layout_configuration,
        )

    else:
        preset = reference_preset
    included_data_hash = decoder.decode_single(256)
    expected_data_hash = _dictionary_byte_hash(preset.layout_configuration.game_data)
    if included_data_hash != expected_data_hash:
        raise ValueError("Given permalink is for a Randovania database with hash '{}', "
                         "but current database has hash '{}'.".format(included_data_hash, expected_data_hash))
    return preset
def test_create_permalink_logic(mock_print: MagicMock, ):
    # Setup
    args = MagicMock()
    args.trick_level = LayoutTrickLevel.HARD.value
    args.major_items_mode = False
    args.sky_temple_keys = LayoutSkyTempleKeyMode.ALL_BOSSES.value
    args.skip_item_loss = True
    args.seed = 15000
    args.menu_mod = False
    args.warp_to_start = False

    # Run
    randovania.cli.commands.create_permalink.create_permalink_logic(args)

    # Assert
    permalink = Permalink(
        seed_number=args.seed,
        spoiler=True,
        patcher_configuration=PatcherConfiguration(
            menu_mod=args.menu_mod,
            warp_to_start=args.warp_to_start,
        ),
        layout_configuration=LayoutConfiguration.from_params(
            trick_level_configuration=TrickLevelConfiguration(
                LayoutTrickLevel.HARD),
            sky_temple_keys=LayoutSkyTempleKeyMode.ALL_BOSSES,
            elevators=LayoutElevators.VANILLA,
            starting_location=StartingLocation.default(),
        ),
    )

    # Assert
    mock_print.assert_called_once_with(permalink)
def test_apply_layout(
    mock_ensure_no_menu_mod: MagicMock,
    mock_create_pak_backups: MagicMock,
    mock_add_menu_mod_to_files: MagicMock,
    mock_modern_api: MagicMock,
    mock_create_progress_update_from_successive_messages: MagicMock,
    mock_save_to_file: MagicMock,
    include_menu_mod: bool,
    has_backup_path: bool,
):
    # Setup
    cosmetic_patches = MagicMock()
    description = LayoutDescription(
        version=randovania.VERSION,
        permalink=Permalink(seed_number=1,
                            spoiler=False,
                            patcher_configuration=PatcherConfiguration(
                                menu_mod=include_menu_mod,
                                warp_to_start=MagicMock(),
                            ),
                            layout_configuration=MagicMock()),
        patches=MagicMock(),
        solver_path=(),
    )

    game_root = MagicMock(spec=Path())
    backup_files_path = MagicMock() if has_backup_path else None
    progress_update = MagicMock()
    status_update = mock_create_progress_update_from_successive_messages.return_value

    # Run
    claris_randomizer.apply_layout(description, cosmetic_patches,
                                   backup_files_path, progress_update,
                                   game_root)

    # Assert
    mock_create_progress_update_from_successive_messages.assert_called_once_with(
        progress_update, 400 if include_menu_mod else 100)
    mock_ensure_no_menu_mod.assert_called_once_with(game_root,
                                                    backup_files_path,
                                                    status_update)
    if has_backup_path:
        mock_create_pak_backups.assert_called_once_with(
            game_root, backup_files_path, status_update)
    else:
        mock_create_pak_backups.assert_not_called()
    game_root.joinpath.assert_called_once_with("files", "randovania.json")
    mock_save_to_file.assert_called_once_with(description,
                                              game_root.joinpath.return_value)

    mock_modern_api.assert_called_once_with(game_root, status_update,
                                            description, cosmetic_patches)

    if include_menu_mod:
        mock_add_menu_mod_to_files.assert_called_once_with(
            game_root, status_update)
    else:
        mock_add_menu_mod_to_files.assert_not_called()
Beispiel #10
0
 def from_json_dict(cls, param: dict) -> "Permalink":
     return Permalink(
         seed_number=param["seed"],
         spoiler=param["spoiler"],
         patcher_configuration=PatcherConfiguration.from_json_dict(
             param["patcher_configuration"]),
         layout_configuration=LayoutConfiguration.from_json_dict(
             param["layout_configuration"]),
     )
def test_decode(patcher_with_data):
    # Setup
    data, expected = patcher_with_data

    # Run
    decoder = BitPackDecoder(data)
    result = PatcherConfiguration.bit_pack_unpack(decoder, {})

    # Assert
    assert result == expected
Beispiel #12
0
 def from_json_dict(cls, value) -> "Preset":
     return Preset(
         name=value["name"],
         description=value["description"],
         base_preset_name=value["base_preset_name"],
         patcher_configuration=PatcherConfiguration.from_json_dict(
             value["patcher_configuration"]),
         layout_configuration=LayoutConfiguration.from_json_dict(
             value["layout_configuration"]),
     )
Beispiel #13
0
def test_create_patches(
    mock_random: MagicMock,
    mock_calculate_item_pool: MagicMock,
    mock_create_base_patches: MagicMock,
    mock_retcon_playthrough_filler: MagicMock,
    mock_indices_for_unassigned_pickups: MagicMock,
):
    # 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_create_base_patches.return_value.starting_location = game.starting_location
    mock_create_base_patches.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_create_base_patches.assert_called_once_with(mock_random.return_value,
                                                     game, permalink, 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 create_permalink_logic(args):
    seed_number = args.seed
    if seed_number is None:
        seed_number = random.randint(0, 2 ** 31)

    permalink = Permalink(
        seed_number=seed_number,
        spoiler=True,
        patcher_configuration=PatcherConfiguration(
            menu_mod=args.menu_mod,
            warp_to_start=args.warp_to_start,
        ),
        layout_configuration=get_layout_configuration_from_args(args),
    )
    _print_permalink(permalink)
Beispiel #15
0
def batch_distribute_helper(args, seed_number) -> float:
    data = prime_database.decode_data_file(args)
    configuration = get_layout_configuration_from_args(args)
    permalink = Permalink(
        seed_number=seed_number,
        spoiler=True,
        patcher_configuration=PatcherConfiguration.default(),
        layout_configuration=configuration,
    )

    start_time = time.perf_counter()
    description = generator.generate_list(permalink, None)
    delta_time = time.perf_counter() - start_time

    description.save_to_file(
        Path(args.output_dir, "{}.json".format(seed_number)))
    return delta_time
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
Beispiel #17
0
def test_encode(mock_dictionary_byte_hash: MagicMock):
    # Setup
    mock_dictionary_byte_hash.return_value = 120
    link = Permalink(
        seed_number=1000,
        spoiler=True,
        patcher_configuration=PatcherConfiguration.default(),
        layout_configuration=LayoutConfiguration.default(),
    )

    # Run
    encoded = link.as_str

    # Assert
    mock_dictionary_byte_hash.assert_called_once_with(
        link.layout_configuration.game_data)
    assert encoded == "MAAAfReMYADv"
def test_output_name_for(mock_shareable_hash: PropertyMock, empty_patches):
    # Setup
    permalink_mock = MagicMock(spec=Permalink(
        seed_number=15000,
        spoiler=True,
        patcher_configuration=PatcherConfiguration.default(),
        layout_configuration=LayoutConfiguration.default(),
    ))
    layout = LayoutDescription(version="0.15.0",
                               permalink=permalink_mock,
                               patches=empty_patches,
                               solver_path=())
    mock_shareable_hash.return_value = "PermalinkStr"

    # Run
    result = simplified_patcher._output_name_for(layout)

    # Assert
    assert result == "Echoes Randomizer - PermalinkStr"
Beispiel #19
0
    def bit_pack_unpack(cls, decoder: BitPackDecoder) -> "Permalink":
        version, seed, spoiler = decoder.decode(_PERMALINK_MAX_VERSION,
                                                _PERMALINK_MAX_SEED, 2)
        cls._raise_if_different_version(version)

        included_data_hash = decoder.decode(256)[0]

        patcher_configuration = PatcherConfiguration.bit_pack_unpack(decoder)
        layout_configuration = LayoutConfiguration.bit_pack_unpack(decoder)

        expected_data_hash = _dictionary_byte_hash(
            layout_configuration.game_data)
        if included_data_hash != expected_data_hash:
            raise ValueError(
                "Given permalink is for a Randovania database with hash '{}', "
                "but current database has hash '{}'.".format(
                    included_data_hash, expected_data_hash))

        return Permalink(seed, bool(spoiler), patcher_configuration,
                         layout_configuration)
Beispiel #20
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
Beispiel #21
0
    def bit_pack_unpack(cls, decoder: BitPackDecoder, metadata) -> "Permalink":
        version, seed, spoiler = decoder.decode(_PERMALINK_MAX_VERSION,
                                                _PERMALINK_MAX_SEED, 2)
        cls._raise_if_different_version(version)

        included_data_hash = decoder.decode_single(256)

        manager = PresetManager(None)
        is_custom_preset = bitpacking.decode_bool(decoder)
        reference_preset = decoder.decode_element(manager.included_presets)

        if is_custom_preset:
            patcher_configuration = PatcherConfiguration.bit_pack_unpack(
                decoder, {"reference": reference_preset.patcher_configuration})
            layout_configuration = LayoutConfiguration.bit_pack_unpack(
                decoder, {"reference": reference_preset.layout_configuration})
            preset = Preset(
                name="{} Custom".format(reference_preset.name),
                description="A customized preset.",
                base_preset_name=reference_preset.name,
                patcher_configuration=patcher_configuration,
                layout_configuration=layout_configuration,
            )

        else:
            preset = reference_preset

        expected_data_hash = _dictionary_byte_hash(
            preset.layout_configuration.game_data)
        if included_data_hash != expected_data_hash:
            raise ValueError(
                "Given permalink is for a Randovania database with hash '{}', "
                "but current database has hash '{}'.".format(
                    included_data_hash, expected_data_hash))

        return Permalink(
            seed,
            bool(spoiler),
            preset,
        )
Beispiel #22
0
def test_generate_seed_with_invalid_quantity_configuration():
    # Setup
    status_update = MagicMock()

    configuration = LayoutConfiguration.from_params(
        trick_level=LayoutTrickLevel.NO_TRICKS,
        sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM,
        elevators=LayoutRandomizedFlag.VANILLA,
        pickup_quantities={"Light Suit": 5},
        starting_location=StartingLocation.default(),
        starting_resources=StartingResources.default(),
    )

    permalink = Permalink(
        seed_number=50,
        spoiler=True,
        patcher_configuration=PatcherConfiguration.default(),
        layout_configuration=configuration,
    )

    # Run
    with pytest.raises(randovania.resolver.exceptions.GenerationFailure):
        generator.generate_list(permalink, status_update=status_update)
Beispiel #23
0
def test_decode_v1(mock_dictionary_byte_hash: MagicMock):
    mock_dictionary_byte_hash.return_value = 120
    # We're mocking the database hash to avoid breaking tests every single time we change the database

    # This test should break whenever we change how permalinks are created
    # When this happens, we must bump the permalink version and change the tests
    encoded = "IAAAfReNPArRHMClxLYgIID3"

    expected = Permalink(
        seed_number=1000,
        spoiler=True,
        patcher_configuration=PatcherConfiguration(
            disable_hud_popup=True,
            menu_mod=True,
            speed_up_credits=False,
        ),
        layout_configuration=LayoutConfiguration.from_params(
            trick_level=LayoutTrickLevel.HARD,
            sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM,
            elevators=LayoutRandomizedFlag.RANDOMIZED,
            pickup_quantities={
                "Missile Expansion": 10,
                "Light Suit": 9,
            },
            starting_location=StartingLocation.default(),
            starting_resources=StartingResources.default(),
        ),
    )

    # Uncomment this line to quickly get the new encoded permalink
    # assert expected.as_str == ""

    # Run
    link = Permalink.from_str(encoded)

    # Assert
    assert link == expected
def test_apply_layout(mock_run_with_args: MagicMock,
                      mock_base_args: MagicMock,
                      mock_ensure_no_menu_mod: MagicMock,
                      mock_create_pak_backups: MagicMock,
                      mock_calculate_indices: MagicMock,
                      mock_add_menu_mod_to_files: MagicMock,
                      mock_create_progress_update_from_successive_messages: MagicMock,
                      mock_save_to_file: MagicMock,
                      seed_number: int,
                      item_loss: bool,
                      elevators: bool,
                      warp_to_start: bool,
                      include_menu_mod: bool,
                      speed_up_credits: bool,
                      ):
    # Setup
    hud_memo_popup_removal: bool = MagicMock()
    cosmetic_patches = CosmeticPatches(disable_hud_popup=hud_memo_popup_removal,
                                       speed_up_credits=speed_up_credits,
                                       )
    description = LayoutDescription(
        version=randovania.VERSION,
        permalink=Permalink(
            seed_number=seed_number,
            spoiler=False,
            patcher_configuration=PatcherConfiguration(
                menu_mod=include_menu_mod,
                warp_to_start=warp_to_start,
            ),
            layout_configuration=LayoutConfiguration.from_params(
                trick_level=MagicMock(),
                sky_temple_keys=MagicMock(),
                elevators=LayoutRandomizedFlag.RANDOMIZED if elevators else LayoutRandomizedFlag.VANILLA,
                pickup_quantities={},
                starting_location=StartingLocation.default(),
                starting_resources=StartingResources.from_item_loss(item_loss),
            )
        ),
        patches=None,
        solver_path=(),
    )

    game_root = MagicMock(spec=Path())
    backup_files_path = MagicMock()
    progress_update = MagicMock()
    status_update = mock_create_progress_update_from_successive_messages.return_value

    mock_calculate_indices.return_value = [10, 25, 1, 2, 5, 1]
    mock_base_args.return_value = []
    expected_args = [
        "-s", str(seed_number),
        "-p", "10,25,1,2,5,1"
    ]
    if not item_loss:
        expected_args.append("-i")
    if elevators:
        expected_args.append("-v")
    if speed_up_credits:
        expected_args.append("-c")
    if warp_to_start:
        expected_args.append("-t")

    # Run
    claris_randomizer.apply_layout(description, cosmetic_patches, backup_files_path, progress_update, game_root)

    # Assert
    mock_base_args.assert_called_once_with(game_root, hud_memo_popup_removal=hud_memo_popup_removal)
    mock_create_progress_update_from_successive_messages.assert_called_once_with(
        progress_update,
        400 if include_menu_mod else 100
    )
    mock_ensure_no_menu_mod.assert_called_once_with(game_root, backup_files_path, status_update)
    mock_create_pak_backups.assert_called_once_with(game_root, backup_files_path, status_update)
    mock_calculate_indices.assert_called_once_with(description)
    game_root.joinpath.assert_called_once_with("files", "randovania.json")
    mock_save_to_file.assert_called_once_with(description, game_root.joinpath.return_value)
    mock_run_with_args.assert_called_once_with(expected_args, "Randomized!", status_update)
    if include_menu_mod:
        mock_add_menu_mod_to_files.assert_called_once_with(game_root, status_update)
    else:
        mock_add_menu_mod_to_files.assert_not_called()
from randovania.layout.layout_configuration import LayoutTrickLevel, LayoutConfiguration
from randovania.layout.layout_description import LayoutDescription, SolverPath, _item_locations_to_pickup_assignment
from randovania.layout.patcher_configuration import PatcherConfiguration
from randovania.layout.permalink import Permalink


@pytest.mark.parametrize("value", LayoutTrickLevel)
def test_pickle_trick_level(value: LayoutTrickLevel):
    assert pickle.loads(pickle.dumps(value)) == value


@pytest.mark.parametrize("permalink", [
    Permalink(
        seed_number=1000,
        spoiler=True,
        patcher_configuration=PatcherConfiguration.default(),
        layout_configuration=LayoutConfiguration.default(),
    )
])
@pytest.mark.parametrize("item_locations", [{}])
@pytest.mark.parametrize("solver_path", [tuple()])
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),
Beispiel #26
0
    mock_dictionary_byte_hash.assert_called_once_with(
        link.layout_configuration.game_data)
    assert encoded == "MAAAfReMYADv"


@pytest.mark.parametrize(
    "invalid",
    ["", "a", "x", "zz", "AAAAfRxALWmCI50gIQD", "AAAAfRxALxmCI50gIQDy"])
def test_decode_invalid(invalid: str):
    with pytest.raises(ValueError):
        Permalink.from_str(invalid)


@pytest.mark.parametrize("spoiler", [False, True])
@pytest.mark.parametrize("patcher", [
    PatcherConfiguration.default(),
    PatcherConfiguration(
        menu_mod=True,
        warp_to_start=False,
    ),
])
@pytest.mark.parametrize("layout", [
    LayoutConfiguration.default(),
    LayoutConfiguration.from_params(
        trick_level=LayoutTrickLevel.HARD,
        sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM,
        elevators=LayoutRandomizedFlag.RANDOMIZED,
        pickup_quantities={
            "Missile Expansion": 10,
            "Light Suit": 9,
        },
Beispiel #27
0
def describe(preset: Preset) -> Iterable[PresetDescription]:
    patcher = preset.patcher_configuration
    configuration = preset.layout_configuration
    major_items = configuration.major_items_configuration

    game_description = data_reader.decode_data(
        preset.layout_configuration.game_data)

    format_params = {}

    def _bool_to_str(b: bool) -> str:
        if b:
            return "Yes"
        else:
            return "No"

    # Item Placement

    random_starting_items = "{} to {}".format(
        major_items.minimum_random_starting_items,
        major_items.maximum_random_starting_items,
    )
    if random_starting_items == "0 to 0":
        random_starting_items = "None"

    format_params[
        "trick_level"] = configuration.trick_level_configuration.pretty_description
    format_params[
        "randomization_mode"] = configuration.randomization_mode.value
    format_params["random_starting_items"] = random_starting_items

    # Items
    format_params["progressive_suit"] = _bool_to_str(
        major_items.progressive_suit)
    format_params["progressive_grapple"] = _bool_to_str(
        major_items.progressive_grapple)
    format_params["split_beam_ammo"] = _bool_to_str(
        configuration.split_beam_ammo)
    format_params["starting_items"] = _calculate_starting_items(
        configuration.major_items_configuration.items_state)
    format_params["item_pool"] = _calculate_item_pool(
        configuration.major_items_configuration)

    # Difficulty
    default_patcher = PatcherConfiguration()

    if patcher.varia_suit_damage == default_patcher.varia_suit_damage and (
            patcher.dark_suit_damage == default_patcher.dark_suit_damage):
        dark_aether_suit_damage = "Normal"
    else:
        dark_aether_suit_damage = "Custom"

    format_params["dark_aether_suit_damage"] = dark_aether_suit_damage
    format_params[
        "dark_aether_damage_strictness"] = configuration.damage_strictness.long_name
    format_params["pickup_model"] = patcher.pickup_model_style.value

    # Gameplay
    translator_gates = "Custom"
    translator_configurations = [
        (configuration.translator_configuration.with_vanilla_actual(),
         "Vanilla (Actual)"),
        (configuration.translator_configuration.with_vanilla_colors(),
         "Vanilla (Colors)"),
        (configuration.translator_configuration.with_full_random(), "Random"),
    ]
    for translator_config, name in translator_configurations:
        if translator_config == configuration.translator_configuration:
            translator_gates = name
            break

    starting_locations = configuration.starting_location.locations

    if len(starting_locations) == 1:
        area = game_description.world_list.area_by_area_location(
            next(iter(starting_locations)))
        format_params[
            "starting_location"] = game_description.world_list.area_name(
                area, distinguish_dark_aether=True)
    else:
        format_params["starting_location"] = "{} locations".format(
            len(starting_locations))

    format_params["translator_gates"] = translator_gates
    format_params["elevators"] = configuration.elevators.value
    format_params["hints"] = "Yes"

    # Game Changes
    missile_launcher_required = True
    main_pb_required = True
    for ammo, state in configuration.ammo_configuration.items_state.items():
        if ammo.name == "Missile Expansion":
            missile_launcher_required = state.requires_major_item
        elif ammo.name == "Power Bomb Expansion":
            main_pb_required = state.requires_major_item

    format_params["missile_launcher_required"] = _bool_to_str(
        missile_launcher_required)
    format_params["main_pb_required"] = _bool_to_str(main_pb_required)
    format_params["warp_to_start"] = _bool_to_str(patcher.warp_to_start)
    format_params["generic_patches"] = "Some"
    format_params["menu_mod"] = _bool_to_str(patcher.menu_mod)
    format_params["include_final_bosses"] = _bool_to_str(
        not configuration.skip_final_bosses)

    # Sky Temple Keys
    if configuration.sky_temple_keys.num_keys == LayoutSkyTempleKeyMode.ALL_BOSSES:
        stk_location = "Bosses"
    elif configuration.sky_temple_keys.num_keys == LayoutSkyTempleKeyMode.ALL_GUARDIANS:
        stk_location = "Guardians"
    else:
        stk_location = "Random"

    format_params["target"] = "{0} of {0}".format(
        configuration.sky_temple_keys.num_keys)
    format_params["location"] = stk_location

    for category, templates in _TEMPLATE_STRINGS.items():
        yield category, [item.format(**format_params) for item in templates]
Beispiel #28
0
    mock_dictionary_byte_hash.assert_called_once_with(
        link.layout_configuration.game_data)
    assert encoded == "IAAAfReOMACm"


@pytest.mark.parametrize(
    "invalid",
    ["", "a", "x", "zz", "AAAAfRxALWmCI50gIQD", "AAAAfRxALxmCI50gIQDy"])
def test_decode_invalid(invalid: str):
    with pytest.raises(ValueError):
        Permalink.from_str(invalid)


@pytest.mark.parametrize("spoiler", [False, True])
@pytest.mark.parametrize("patcher", [
    PatcherConfiguration.default(),
    PatcherConfiguration(
        disable_hud_popup=False, menu_mod=True, speed_up_credits=False),
])
@pytest.mark.parametrize("layout", [
    LayoutConfiguration.default(),
    LayoutConfiguration.from_params(
        trick_level=LayoutTrickLevel.HARD,
        sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM,
        elevators=LayoutRandomizedFlag.RANDOMIZED,
        pickup_quantities={
            "Missile Expansion": 10,
            "Light Suit": 9,
        },
        starting_location=StartingLocation.default(),
        starting_resources=StartingResources.default(),
def _patcher_with_data(request):
    return request.param["encoded"], PatcherConfiguration.from_json_dict(
        request.param["json"])
Beispiel #30
0
def test_apply_layout(
    mock_ensure_no_menu_mod: MagicMock,
    mock_create_pak_backups: MagicMock,
    mock_add_menu_mod_to_files: MagicMock,
    mock_modern_api: MagicMock,
    mock_apply_patches: MagicMock,
    mock_create_progress_update_from_successive_messages: MagicMock,
    mock_save_to_file: MagicMock,
    include_menu_mod: bool,
    has_backup_path: bool,
):
    # Setup
    cosmetic_patches = MagicMock()
    description = LayoutDescription(
        version=randovania.VERSION,
        permalink=Permalink(
            seed_number=1,
            spoiler=False,
            presets={
                0:
                Preset(name="Name",
                       description="Desc",
                       base_preset_name=None,
                       patcher_configuration=PatcherConfiguration(
                           menu_mod=include_menu_mod,
                           warp_to_start=MagicMock(),
                       ),
                       layout_configuration=MagicMock())
            },
        ),
        all_patches={0: MagicMock()},
        item_order=(),
    )

    game_root = MagicMock(spec=Path())
    backup_files_path = MagicMock() if has_backup_path else None
    progress_update = MagicMock()
    player_config = PlayersConfiguration(0, {0: "you"})
    status_update = mock_create_progress_update_from_successive_messages.return_value

    # Run
    claris_randomizer.apply_layout(description, player_config,
                                   cosmetic_patches, backup_files_path,
                                   progress_update, game_root)

    # Assert
    mock_create_progress_update_from_successive_messages.assert_called_once_with(
        progress_update, 400 if include_menu_mod else 100)
    mock_ensure_no_menu_mod.assert_called_once_with(game_root,
                                                    backup_files_path,
                                                    status_update)
    if has_backup_path:
        mock_create_pak_backups.assert_called_once_with(
            game_root, backup_files_path, status_update)
    else:
        mock_create_pak_backups.assert_not_called()
    game_root.joinpath.assert_called_once_with(
        "files", "randovania.{}".format(LayoutDescription.file_extension()))
    mock_save_to_file.assert_called_once_with(description,
                                              game_root.joinpath.return_value)

    mock_modern_api.assert_called_once_with(game_root, status_update,
                                            description, player_config,
                                            cosmetic_patches)
    mock_apply_patches.assert_called_once_with(game_root,
                                               description.all_patches[0],
                                               cosmetic_patches)

    if include_menu_mod:
        mock_add_menu_mod_to_files.assert_called_once_with(
            game_root, status_update)
    else:
        mock_add_menu_mod_to_files.assert_not_called()