def test_decode(configuration_with_data): # Setup data, expected = configuration_with_data # Run decoder = BitPackDecoder(data) result = TrickLevelConfiguration.bit_pack_unpack(decoder, { "reference": TrickLevelConfiguration(False, {}, RandovaniaGame.PRIME2), }) # Assert assert result == expected
def test_round_trip_generated_patches(echoes_game_data, default_preset): # Setup preset = dataclasses.replace( default_preset, base_preset_name=default_preset.name, configuration=dataclasses.replace( default_preset.configuration, trick_level=TrickLevelConfiguration( minimal_logic=True, specific_levels={}, game=RandovaniaGame.PRIME2, ) ) ) all_patches = generator._async_create_description( permalink=Permalink( seed_number=1000, spoiler=True, presets={0: preset}, ), status_update=lambda x: None, attempts=0, ).all_patches # Run encoded = game_patches_serializer.serialize(all_patches, {0: echoes_game_data}) decoded = game_patches_serializer.decode(encoded, {0: preset.configuration}) # Assert assert all_patches == decoded
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_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_reach_size_from_start(echoes_game_description, default_layout_configuration, minimal_logic, nodes, safe_nodes): # Setup specific_levels = { trick.short_name: LayoutTrickLevel.HYPERMODE for trick in echoes_game_description.resource_database.trick } layout_configuration = dataclasses.replace( default_layout_configuration, trick_level_configuration=TrickLevelConfiguration( minimal_logic=minimal_logic, specific_levels=specific_levels if not minimal_logic else {}), ) player_pool = generator.create_player_pool(Random(15000), layout_configuration, 0) game, state = logic_bootstrap(layout_configuration, player_pool.game, player_pool.patches) # Run reach = GeneratorReach.reach_from_state(game, state) # Assert assert len(list(reach.nodes)) >= nodes assert len(list(reach.safe_nodes)) >= safe_nodes
def from_json_dict(cls, json_dict: dict) -> "LayoutConfiguration": return LayoutConfiguration( trick_level_configuration=TrickLevelConfiguration.from_json( json_dict["trick_level"]), damage_strictness=LayoutDamageStrictness( json_dict["damage_strictness"]), sky_temple_keys=LayoutSkyTempleKeyMode( json_dict["sky_temple_keys"]), elevators=LayoutElevators(json_dict["elevators"]), starting_location=StartingLocation.from_json( json_dict["starting_location"]), available_locations=AvailableLocationsConfiguration.from_json( json_dict["available_locations"]), major_items_configuration=MajorItemsConfiguration.from_json( json_dict["major_items_configuration"], default_prime2_item_database(), ), ammo_configuration=AmmoConfiguration.from_json( json_dict["ammo_configuration"], default_prime2_item_database(), ), translator_configuration=TranslatorConfiguration.from_json( json_dict["translator_configuration"]), hints=HintConfiguration.from_json(json_dict["hints"]), beam_configuration=BeamConfiguration.from_json( json_dict["beam_configuration"]), skip_final_bosses=json_dict["skip_final_bosses"], energy_per_tank=json_dict["energy_per_tank"], split_beam_ammo=json_dict["split_beam_ammo"], )
def test_edit_layout_trick_level(editor: PresetEditor, initial_layout_configuration_params: dict, default_layout_configuration, new_trick_level: LayoutTrickLevel): # Setup editor._layout_configuration = dataclasses.replace(default_layout_configuration, **initial_layout_configuration_params) editor._nested_autosave_level = 1 # Run initial_layout_configuration_params["trick_level_configuration"] = TrickLevelConfiguration(new_trick_level) editor.set_layout_configuration_field("trick_level_configuration", TrickLevelConfiguration(new_trick_level)) # Assert assert editor.layout_configuration == dataclasses.replace(default_layout_configuration, **initial_layout_configuration_params)
def test_round_trip_generated_patches(echoes_game_data, preset_manager): # Setup preset = dataclasses.replace( preset_manager.default_preset, layout_configuration=dataclasses.replace( preset_manager.default_preset.layout_configuration, trick_level_configuration=TrickLevelConfiguration( global_level=LayoutTrickLevel.MINIMAL_RESTRICTIONS, specific_levels={}, ))) all_patches = generator._async_create_description( permalink=Permalink( seed_number=1000, spoiler=True, presets={0: preset}, ), status_update=lambda x: None, ).all_patches # Run encoded = game_patches_serializer.serialize(all_patches, {0: echoes_game_data}) decoded = game_patches_serializer.decode(encoded, {0: preset.layout_configuration}) # Assert assert all_patches == decoded
def _setup_difficulties_menu(self): game = default_database.default_prime2_game_description() for i, trick_level in enumerate(LayoutTrickLevel): if trick_level not in { LayoutTrickLevel.NO_TRICKS, LayoutTrickLevel.MINIMAL_RESTRICTIONS }: difficulty_action = QAction(self) difficulty_action.setText(trick_level.long_name) self.menu_difficulties.addAction(difficulty_action) difficulty_action.triggered.connect( functools.partial(self._open_difficulty_details_popup, trick_level)) configurable_tricks = TrickLevelConfiguration.all_possible_tricks() tricks_in_use = used_tricks(game.world_list) for trick in sorted(game.resource_database.trick, key=lambda _trick: _trick.long_name): if trick.index not in configurable_tricks or trick not in tricks_in_use: continue trick_menu = QMenu(self) trick_menu.setTitle(trick.long_name) self.menu_trick_details.addAction(trick_menu.menuAction()) used_difficulties = difficulties_for_trick(game.world_list, trick) for i, trick_level in enumerate(LayoutTrickLevel): if trick_level in used_difficulties: difficulty_action = QAction(self) difficulty_action.setText(trick_level.long_name) trick_menu.addAction(difficulty_action) difficulty_action.triggered.connect( functools.partial(self._open_trick_details_popup, trick, trick_level))
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
def test_round_trip_generated_patches(echoes_game_data, preset_manager): # Setup preset = dataclasses.replace( preset_manager.default_preset, layout_configuration=dataclasses.replace( preset_manager.default_preset.layout_configuration, trick_level_configuration=TrickLevelConfiguration( global_level=LayoutTrickLevel.MINIMAL_RESTRICTIONS, specific_levels={}, ) ) ) patches = generator._create_randomized_patches( permalink=Permalink( seed_number=1000, spoiler=True, preset=preset, ), 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, preset.layout_configuration) # Assert assert patches == decoded
def test_edit_layout_trick_level(option: Options, initial_layout_configuration_params: dict, new_trick_level: LayoutTrickLevel): # Setup option._layout_configuration = LayoutConfiguration.from_params( **initial_layout_configuration_params) option._nested_autosave_level = 1 # Run initial_layout_configuration_params[ "trick_level_configuration"] = TrickLevelConfiguration(new_trick_level) option.set_layout_configuration_field( "trick_level_configuration", TrickLevelConfiguration(new_trick_level)) # Assert assert option.layout_configuration == LayoutConfiguration.from_params( **initial_layout_configuration_params)
def test_decode(mock_possible_tricks, configuration_with_data): # Setup data, expected = configuration_with_data # Run decoder = BitPackDecoder(data) result = TrickLevelConfiguration.bit_pack_unpack(decoder, {}) # Assert assert result == expected
def test_encode_no_tricks_are_removed(): from_json = TrickLevelConfiguration.from_json({ "minimal_logic": False, "specific_levels": { "Dash": "no-tricks" } }) encoded = bitpacking._pack_encode_results([ (value_argument, value_format) for value_argument, value_format in from_json.bit_pack_encode({}) ]) assert encoded == b'\x00\x00\x00\x00' decoder = BitPackDecoder(encoded) decoded = TrickLevelConfiguration.bit_pack_unpack(decoder, {}) assert decoded.specific_levels == {}
def test_set_level_for_trick_remove(echoes_resource_database): trick = echoes_resource_database.trick[0] config = TrickLevelConfiguration(False, {}, RandovaniaGame.PRIME2) assert config.level_for_trick(trick) == LayoutTrickLevel.DISABLED config = config.set_level_for_trick(trick, LayoutTrickLevel.ADVANCED) assert config.level_for_trick(trick) == LayoutTrickLevel.ADVANCED config = config.set_level_for_trick(trick, LayoutTrickLevel.DISABLED) assert config.level_for_trick(trick) == LayoutTrickLevel.DISABLED
def get_layout_configuration_from_args(args) -> LayoutConfiguration: try: sky_temple_keys = int(args.sky_temple_keys) except ValueError: sky_temple_keys = args.sky_temple_keys # TODO: support for item loss return LayoutConfiguration.from_params( trick_level_configuration=TrickLevelConfiguration( LayoutTrickLevel(args.trick_level)), sky_temple_keys=LayoutSkyTempleKeyMode(sky_temple_keys), elevators=LayoutElevators.VANILLA, starting_location=StartingLocation.default(), )
def static_resources_for_layout_logic( configuration: TrickLevelConfiguration, resource_database: ResourceDatabase, ) -> Tuple[int, CurrentResources]: """ :param configuration: :param resource_database: :return: """ all_used_tricks = TrickLevelConfiguration.all_possible_tricks() static_resources = {} for trick in resource_database.trick: if trick.index in all_used_tricks: static_resources[trick] = configuration.level_for_trick( trick).as_number # Exclude from Room Rando static_resources[resource_database.get_by_type_and_index( ResourceType.TRICK, 18)] = 5 return configuration.global_level.as_number, static_resources
def _create_open_map_tracker_actions(self): base_layout = self.preset_manager.default_preset.layout_configuration for trick_level in LayoutTrickLevel: if trick_level != LayoutTrickLevel.MINIMAL_RESTRICTIONS: action = QtWidgets.QAction(self) action.setText(trick_level.long_name) self.menu_map_tracker.addAction(action) configuration = dataclasses.replace( base_layout, trick_level_configuration=TrickLevelConfiguration(trick_level, {}) ) action.triggered.connect(partial(self.open_map_tracker, configuration))
def test_encode_no_tricks_are_removed(): from_json = TrickLevelConfiguration.from_json( { "minimal_logic": False, "specific_levels": { "Dash": "disabled" } }, game=RandovaniaGame.PRIME2) encoded = bitpacking._pack_encode_results([ (value_argument, value_format) for value_argument, value_format in from_json.bit_pack_encode({}) ]) assert encoded == b'\x00\x00\x00\x00' decoder = BitPackDecoder(encoded) decoded = TrickLevelConfiguration.bit_pack_unpack(decoder, { "reference": TrickLevelConfiguration(False, {}, RandovaniaGame.PRIME2), }) assert decoded.specific_levels == {}
def from_json_dict(cls, json_dict: dict) -> "LayoutConfiguration": return cls.from_params( trick_level_configuration=TrickLevelConfiguration.from_json(json_dict["trick_level"]), sky_temple_keys=LayoutSkyTempleKeyMode(json_dict["sky_temple_keys"]), elevators=LayoutElevators(json_dict["elevators"]), starting_location=StartingLocation.from_json(json_dict["starting_location"]), randomization_mode=RandomizationMode(json_dict["randomization_mode"]), major_items_configuration=MajorItemsConfiguration.from_json( json_dict["major_items_configuration"], default_prime2_item_database(), ), ammo_configuration=AmmoConfiguration.from_json( json_dict["ammo_configuration"], default_prime2_item_database(), ), translator_configuration=TranslatorConfiguration.from_json(json_dict["translator_configuration"]), hints=HintConfiguration.from_json(json_dict["hints"]), split_beam_ammo=json_dict["split_beam_ammo"], )
def trick_resources_for_configuration(configuration: TrickLevelConfiguration, resource_database: ResourceDatabase, ) -> CurrentResources: """ :param configuration: :param resource_database: :return: """ static_resources = {} for trick in resource_database.trick: if configuration.minimal_logic: level = LayoutTrickLevel.MINIMAL_LOGIC else: level = configuration.level_for_trick(trick) static_resources[trick] = level.as_number return static_resources
def test_reach_size_from_start(echoes_game_description): # Setup configuration = LayoutConfiguration.from_params( trick_level_configuration=TrickLevelConfiguration( LayoutTrickLevel.HYPERMODE), ) patches = GamePatches.with_game(echoes_game_description) patches = patches.assign_gate_assignment( base_patches_factory.gate_assignment_for_configuration( configuration, echoes_game_description.resource_database, Random(15000))) game, state = logic_bootstrap(configuration, echoes_game_description, patches) # Run reach = GeneratorReach.reach_from_state(game, state) # Assert assert len(list(reach.nodes)) == 26 assert len(list(reach.safe_nodes)) == 4
def test_reach_size_from_start(echoes_game_description, default_layout_configuration): # Setup layout_configuration = dataclasses.replace( default_layout_configuration, trick_level_configuration=TrickLevelConfiguration( LayoutTrickLevel.HYPERMODE), ) player_pool = generator.create_player_pool(Random(15000), layout_configuration, 0) game, state = logic_bootstrap(layout_configuration, player_pool.game, player_pool.patches) # Run reach = GeneratorReach.reach_from_state(game, state) # Assert assert len(list(reach.nodes)) == 44 assert len(list(reach.safe_nodes)) == 5
def static_resources_for_layout_logic( configuration: TrickLevelConfiguration, resource_database: ResourceDatabase, ) -> CurrentResources: """ :param configuration: :param resource_database: :return: """ static_resources = {} for trick in resource_database.trick: static_resources[trick] = configuration.level_for_trick( trick).as_number # Room Rando room_rando = find_resource_info_with_long_name(resource_database.misc, "Room Randomizer") static_resources[room_rando] = 0 return static_resources
def test_reach_size_from_start(echoes_game_description, default_layout_configuration): # Setup configuration = dataclasses.replace( default_layout_configuration, trick_level_configuration=TrickLevelConfiguration( LayoutTrickLevel.HYPERMODE), ) patches = echoes_game_description.create_game_patches() patches = patches.assign_gate_assignment( base_patches_factory.gate_assignment_for_configuration( configuration, echoes_game_description.resource_database, Random(15000))) game, state = logic_bootstrap(configuration, echoes_game_description, patches) # Run reach = GeneratorReach.reach_from_state(game, state) # Assert assert len(list(reach.nodes)) == 25 assert len(list(reach.safe_nodes)) == 4
def _configuration_with_data(request, mocker, echoes_game_description): tricks = echoes_game_description.resource_database.trick[:14] mocker.patch("randovania.layout.trick_level._all_tricks", return_value=tricks) return request.param["encoded"], TrickLevelConfiguration.from_json( request.param["json"])
def test_pretty_description_tricks_echoes(levels, expected): config = TrickLevelConfiguration(False, levels, RandovaniaGame.PRIME2) assert config.pretty_description == expected
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_configuration=TrickLevelConfiguration( LayoutTrickLevel.HARD), sky_temple_keys=LayoutSkyTempleKeyMode.ALL_GUARDIANS, elevators=LayoutElevators.RANDOMIZED, ), ]) def test_round_trip(spoiler: bool, patcher: PatcherConfiguration, layout: LayoutConfiguration): # Setup link = Permalink( seed_number=1000, spoiler=spoiler, patcher_configuration=patcher, layout_configuration=layout, ) # Run
def _configuration_with_data(request): return request.param["encoded"], TrickLevelConfiguration.from_json(request.param["json"])
def setup_trick_level_elements(self): # logic_combo_box for i, trick_level in enumerate(LayoutTrickLevel): self.logic_combo_box.setItemData(i, trick_level) self.logic_combo_box.currentIndexChanged.connect(self._on_trick_level_changed) self.trick_difficulties_layout = QtWidgets.QGridLayout() self._checkbox_for_trick = {} self._slider_for_trick = {} configurable_tricks = TrickLevelConfiguration.all_possible_tricks() used_tricks = _used_tricks(self.world_list) size_policy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) self._create_difficulty_details_row() row = 2 for trick in sorted(self.resource_database.trick, key=lambda _trick: _trick.long_name): if trick.index not in configurable_tricks or trick not in used_tricks: continue if row > 1: self.trick_difficulties_layout.addItem(QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)) trick_configurable = QtWidgets.QCheckBox(self.trick_level_scroll_contents) trick_configurable.setFixedWidth(16) trick_configurable.stateChanged.connect(functools.partial(self._on_check_trick_configurable, trick)) self._checkbox_for_trick[trick] = trick_configurable self.trick_difficulties_layout.addWidget(trick_configurable, row, 0, 1, 1) trick_label = QtWidgets.QLabel(self.trick_level_scroll_contents) trick_label.setSizePolicy(size_policy) trick_label.setWordWrap(True) trick_label.setFixedWidth(80) trick_label.setText(trick.long_name) self.trick_difficulties_layout.addWidget(trick_label, row, 1, 1, 1) slider_layout = QtWidgets.QGridLayout() slider_layout.setHorizontalSpacing(0) for i in range(12): slider_layout.setColumnStretch(i, 1) horizontal_slider = QtWidgets.QSlider(self.trick_level_scroll_contents) horizontal_slider.setMaximum(5) horizontal_slider.setPageStep(2) horizontal_slider.setOrientation(QtCore.Qt.Horizontal) horizontal_slider.setTickPosition(QtWidgets.QSlider.TicksBelow) horizontal_slider.setEnabled(False) horizontal_slider.valueChanged.connect(functools.partial(self._on_slide_trick_slider, trick)) self._slider_for_trick[trick] = horizontal_slider slider_layout.addWidget(horizontal_slider, 0, 1, 1, 10) difficulties_for_trick = _difficulties_for_trick(self.world_list, trick) for i, trick_level in enumerate(LayoutTrickLevel): if trick_level == LayoutTrickLevel.NO_TRICKS or trick_level in difficulties_for_trick: difficulty_label = QtWidgets.QLabel(self.trick_level_scroll_contents) difficulty_label.setAlignment(QtCore.Qt.AlignHCenter) difficulty_label.setText(trick_level.long_name) slider_layout.addWidget(difficulty_label, 1, 2 * i, 1, 2) self.trick_difficulties_layout.addLayout(slider_layout, row, 2, 1, 1) tool_button = QtWidgets.QToolButton(self.trick_level_scroll_contents) tool_button.setText("?") tool_button.clicked.connect(functools.partial(self._open_trick_details_popup, trick)) self.trick_difficulties_layout.addWidget(tool_button, row, 3, 1, 1) row += 1 self.trick_level_layout.addLayout(self.trick_difficulties_layout)