def test_add_elevator_connections_to_patches_random(echoes_game_data): # Setup game = data_reader.decode_data(echoes_game_data) permalink = dataclasses.replace(Permalink.default(), layout_configuration=dataclasses.replace( LayoutConfiguration.default(), elevators=LayoutElevators.RANDOMIZED)) expected = dataclasses.replace(GamePatches.with_game(game), elevator_connection={ 589851: AreaLocation(1039999561, 1868895730), 1572998: AreaLocation(1039999561, 3479543630), 1966093: AreaLocation(2252328306, 408633584), 2097251: AreaLocation(1119434212, 3331021649), 136970379: AreaLocation(2252328306, 2068511343), 3342446: AreaLocation(1039999561, 3205424168), 3538975: AreaLocation(1119434212, 2806956034), 152: AreaLocation(1006255871, 2889020216), 393260: AreaLocation(464164546, 3145160350), 524321: AreaLocation(464164546, 900285955), 589949: AreaLocation(1006255871, 2278776548), 122: AreaLocation(464164546, 3528156989), 1245307: AreaLocation(1006255871, 1345979968), 2949235: AreaLocation(1006255871, 1287880522), 129: AreaLocation(1006255871, 2918020398), 2162826: AreaLocation(1006255871, 1660916974), 4522032: AreaLocation(1006255871, 3455543403), 38: AreaLocation(1119434212, 1473133138), 1245332: AreaLocation(2252328306, 2399252740), 1638535: AreaLocation(2252328306, 2556480432), }) # Run result = base_patches_factory.add_elevator_connections_to_patches( permalink.layout_configuration, Random(permalink.seed_number), GamePatches.with_game(game), ) # Assert assert result == expected
def test_add_elevator_connections_to_patches_vanilla(echoes_game_data): # Setup game = data_reader.decode_data(echoes_game_data) permalink = Permalink.default() # Run result = base_patches_factory.add_elevator_connections_to_patches( permalink.layout_configuration, Random(permalink.seed_number), GamePatches.with_game(game)) # Assert assert result == GamePatches.with_game(game)
def serialize_single(player_index: int, num_players: int, patches: GamePatches) -> dict: """ Encodes a given GamePatches into a JSON-serializable dict. :param player_index: :param num_players: :param patches: :return: """ game = filtered_database.game_description_for_layout(patches.configuration) world_list = game.world_list dock_weakness_to_type = {} for dock_type, weaknesses in game.dock_weakness_database.weaknesses.items( ): for weakness in weaknesses.values(): dock_weakness_to_type[weakness] = dock_type result = { # This field helps schema migrations, if nothing else "game": patches.configuration.game.value, "starting_location": patches.starting_location.as_string, "starting_items": { resource_info.long_name: quantity for resource_info, quantity in patches.starting_items.as_resource_gain() }, "teleporters": { source.identifier.as_string: connection.as_string for source, connection in patches.all_elevator_connections() }, "dock_weakness": { dock.identifier.as_string: { "type": dock_weakness_to_type[weakness].short_name, "name": weakness.name, } for dock, weakness in patches.all_dock_weaknesses() }, "configurable_nodes": { identifier.as_string: data_writer.write_requirement(requirement) for identifier, requirement in patches.configurable_nodes.items() }, "locations": { key: value for key, value in _pickup_assignment_to_item_locations( world_list, patches.pickup_assignment, num_players).items() }, "hints": { identifier.as_string: hint.as_json for identifier, hint in patches.hints.items() } } return result
def add_default_hints_to_patches( rng: Random, patches: GamePatches, world_list: WorldList, ) -> GamePatches: """ Adds hints for the locations :param rng: :param patches: :param world_list: :return: """ for node in world_list.all_nodes: if isinstance( node, LogbookNode) and node.lore_type == LoreType.LUMINOTH_WARRIOR: patches = patches.assign_hint( node.resource(), Hint( HintType.KEYBEARER, PrecisionPair(HintLocationPrecision.DETAILED, HintItemPrecision.PRECISE_CATEGORY), PickupIndex(node.hint_index))) # TODO: this should be a flag in PickupNode indices_with_hint = [ (PickupIndex(24), HintType.LIGHT_SUIT_LOCATION), # Light Suit (PickupIndex(43), HintType.GUARDIAN), # Dark Suit (Amorbis) (PickupIndex(79), HintType.GUARDIAN), # Dark Visor (Chykka) (PickupIndex(115), HintType.GUARDIAN), # Annihilator Beam (Quadraxis) ] all_logbook_assets = [ node.resource() for node in world_list.all_nodes if isinstance(node, LogbookNode) and node.resource() not in patches.hints and node.lore_type.holds_generic_hint ] rng.shuffle(indices_with_hint) rng.shuffle(all_logbook_assets) for index, hint_type in indices_with_hint: if not all_logbook_assets: break logbook_asset = all_logbook_assets.pop() patches = patches.assign_hint( logbook_asset, Hint(hint_type, PrecisionPair.detailed(), index)) return patches
def test_run_filler(mock_retcon_playthrough_filler: MagicMock, echoes_game_description, pickup ): # Setup configuration = LayoutConfiguration.default() rng = Random(5000) status_update = MagicMock() item_pool = [pickup] patches = GamePatches.with_game(echoes_game_description) logbook_nodes = [node for node in echoes_game_description.world_list.all_nodes if isinstance(node, LogbookNode)] mock_retcon_playthrough_filler.return_value = patches.assign_hint( logbook_nodes[0].resource(), Hint(HintType.LOCATION, None, PickupIndex(0)) ).assign_pickup_assignment({PickupIndex(1): pickup}) # Run result_patches, remaining_items = runner.run_filler(configuration, echoes_game_description, item_pool, patches, rng, status_update) # Assert assert len(result_patches.hints) == len(logbook_nodes) assert [hint for hint in patches.hints.values() if hint.item_precision is None or hint.location_precision is None] == [] assert remaining_items == [pickup]
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=())
def __init__(self, layout_configuration: LayoutConfiguration): super().__init__() self.setupUi(self) set_default_window_icon(self) self.layout_configuration = layout_configuration self.game_description = data_reader.decode_data( layout_configuration.game_data, True) self.logic, self._initial_state = logic_bootstrap( layout_configuration, self.game_description, GamePatches.with_game(self.game_description)) self.resource_filter_check.stateChanged.connect( self.update_locations_tree_for_reachable_nodes) self.hide_collected_resources_check.stateChanged.connect( self.update_locations_tree_for_reachable_nodes) self.undo_last_action_button.clicked.connect(self._undo_last_action) self.configuration_label.setText( "Trick Level: {}; Elevators: Vanilla; Item Loss: {}".format( layout_configuration.trick_level.value, layout_configuration.starting_resources.configuration.value, )) self.setup_pickups_box() self.setup_possible_locations_tree() self._starting_nodes = { node for node in self.game_description.world_list.all_nodes if node.is_resource_node and node.resource() in self._initial_state.resources } self._add_new_action(self._initial_state.node)
def _create_elevators_field(patches: GamePatches, game: GameDescription) -> list: """ Creates the elevator entries in the patcher file :param patches: :param game: :return: """ world_list = game.world_list elevator_fields = [] for node, connection in patches.all_elevator_connections(): elevator_fields.append({ "instance_id": node.extra["teleporter_instance_id"], "origin_location": _area_identifier_to_json(game.world_list, node.identifier.area_location), "target_location": _area_identifier_to_json(game.world_list, connection), "room_name": _pretty_name_for_elevator(game.game, world_list, node, connection) }) num_teleporter_nodes = sum( 1 for _ in _get_nodes_by_teleporter_id(world_list)) if len(elevator_fields) != num_teleporter_nodes: raise ValueError("Invalid elevator count. Expected {}, got {}.".format( num_teleporter_nodes, len(elevator_fields))) return elevator_fields
def from_json_dict(cls, json_dict: dict) -> "LayoutDescription": version = json_dict["info"]["version"] version_as_obj = StrictVersion(version) if version_as_obj < StrictVersion("0.21.0"): raise RuntimeError("Unsupported log file version '{}'.".format(version)) # TODO: add try/catch to throw convert potential errors in "seed from future version broke" permalink = Permalink.from_json_dict(json_dict["info"]["permalink"]) if not permalink.spoiler: raise ValueError("Unable to read details of seed log with spoiler disabled") game = data_reader.decode_data(permalink.layout_configuration.game_data) patches = GamePatches( _item_locations_to_pickup_assignment(game, json_dict["locations"]), _node_mapping_to_elevator_connection(game.world_list, json_dict["elevators"]), {}, {}, (), game.starting_location ) return LayoutDescription( version=version, permalink=permalink, patches=patches, solver_path=_playthrough_list_to_solver_path(json_dict["playthrough"]), )
def create_base_patches( configuration: LayoutConfiguration, rng: Random, game: GameDescription, ) -> GamePatches: """ :param configuration: :param rng: :param game: :return: """ # TODO: we shouldn't need the seed_number! patches = GamePatches.with_game(game) patches = add_elevator_connections_to_patches(configuration, rng, patches) # Gates patches = patches.assign_gate_assignment( gate_assignment_for_configuration(configuration, game.resource_database, rng)) # Starting Location patches = patches.assign_starting_location( starting_location_for_configuration(configuration, game, rng)) # Hints if rng is not None: patches = add_default_hints_to_patches(rng, patches, game.world_list) return patches
def create_game_patches(self) -> GamePatches: elevator_connection = { node.teleporter_instance_id: node.default_connection for node in self.world_list.all_nodes if isinstance(node, TeleporterNode) and node.editable } return GamePatches({}, elevator_connection, {}, {}, {}, {}, self.starting_location, {})
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(0, default_cs_configuration, {}, {}, {}, {}, {}, {}, 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
def test_basic_search_with_translator_gate(has_translator: bool, echoes_resource_database): # Setup scan_visor = echoes_resource_database.get_by_type_and_index( ResourceType.ITEM, 10) node_a = GenericNode("Node A", True, 0) node_b = GenericNode("Node B", True, 1) node_c = GenericNode("Node C", True, 2) translator_node = TranslatorGateNode("Translator Gate", True, 3, TranslatorGate(1), scan_visor) world_list = WorldList([ World("Test World", 1, [ Area( "Test Area A", False, 10, 0, [node_a, node_b, node_c, translator_node], { node_a: { node_b: RequirementSet.trivial(), translator_node: RequirementSet.trivial(), }, node_b: { node_a: RequirementSet.trivial(), }, node_c: { translator_node: RequirementSet.trivial(), }, translator_node: { node_a: RequirementSet.trivial(), node_c: RequirementSet.trivial(), }, }) ]) ]) game = GameDescription(0, "", DockWeaknessDatabase([], [], [], []), echoes_resource_database, RequirementSet.impossible(), None, {}, world_list) patches = GamePatches.with_game(game) patches = patches.assign_gate_assignment({TranslatorGate(1): scan_visor}) initial_state = State({scan_visor: 1 if has_translator else 0}, (), 99, node_a, patches, None, echoes_resource_database) # Run reach = reach_with_all_safe_resources(game, initial_state) # Assert if has_translator: assert set( reach.safe_nodes) == {node_a, node_b, translator_node, node_c} else: assert set(reach.safe_nodes) == {node_a, node_b}
def create_base_patches(self, configuration: BaseConfiguration, rng: Random, game: GameDescription, is_multiworld: bool, player_index: int, rng_required: bool = True ) -> GamePatches: """ """ patches = GamePatches(player_index, configuration, {}, game.get_default_elevator_connection(), {}, {}, {}, {}, game.starting_location, {}) # 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
def pretty_print_area(area: Area): world_list = _gd.world_list print(area.name) print("Asset id: {}".format(area.area_asset_id)) for node in area.nodes: print(">", node.name, type(node)) for target_node, requirements in world_list.potential_nodes_from(node, GamePatches.with_game(_gd)): if target_node is None: print(" > None?") else: print(" >", n(target_node)) requirements.pretty_print(" ") print()
async def assign_joke_hints(self, patches: GamePatches, identifiers: list[NodeIdentifier], prefill: PreFillParams) -> GamePatches: all_hint_identifiers = [identifier for identifier in identifiers if identifier not in patches.hints] prefill.rng.shuffle(all_hint_identifiers) num_joke = self.num_joke_hints while num_joke > 0 and all_hint_identifiers: identifier = all_hint_identifiers.pop() patches = patches.assign_hint(identifier, Hint(HintType.JOKE, None)) num_joke -= 1 identifiers.remove(identifier) return patches
def _create_base_patches( rng: Random, game: GameDescription, permalink: Permalink, available_pickups: List[PickupEntry], ) -> GamePatches: patches = GamePatches.with_game(game) patches = _add_elevator_connections_to_patches(permalink, patches) patches = patches.assign_starting_location( _starting_location_for_configuration(permalink.layout_configuration, game, rng)) patches = _sky_temple_key_distribution_logic(permalink, patches, available_pickups) return patches
def _add_elevator_connections_to_patches(permalink: Permalink, patches: GamePatches) -> GamePatches: assert patches.elevator_connection == {} if permalink.layout_configuration.elevators == LayoutRandomizedFlag.RANDOMIZED: return GamePatches( patches.pickup_assignment, claris_randomizer.elevator_connections_for_seed_number( permalink.seed_number), patches.dock_connection, patches.dock_weakness, patches.extra_initial_items, patches.starting_location, ) else: return patches
def test_create_patches(mock_random: MagicMock, mock_calculate_item_pool: MagicMock, mock_sky_temple_key_distribution_logic: MagicMock, mock_retcon_playthrough_filler: MagicMock, mock_indices_for_unassigned_pickups: MagicMock, empty_patches ): # 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_sky_temple_key_distribution_logic.return_value.starting_location = game.starting_location mock_sky_temple_key_distribution_logic.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_sky_temple_key_distribution_logic.assert_called_once_with(permalink, GamePatches.with_game(game), 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 _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
def test_retcon_filler_integration(): layout_configuration = LayoutConfiguration.default() rng = Random("fixed-seed!") status_update = MagicMock() game = data_reader.decode_data(layout_configuration.game_data) patches = GamePatches.with_game(game) available_pickups = game.pickup_database.all_useful_pickups logic, state = logic_bootstrap(layout_configuration, game, patches) logic.game.simplify_connections(state.resources) filler_patches = retcon.retcon_playthrough_filler(logic, state, tuple(available_pickups), rng, status_update) assert filler_patches == patches
def _sky_temple_key_distribution_logic( permalink: Permalink, previous_patches: GamePatches, available_pickups: List[PickupEntry], ) -> GamePatches: mode = permalink.layout_configuration.sky_temple_keys new_assignments = {} if mode == LayoutSkyTempleKeyMode.VANILLA: locations_to_place = _FLYING_ING_CACHES[:] elif mode == LayoutSkyTempleKeyMode.ALL_BOSSES or mode == LayoutSkyTempleKeyMode.ALL_GUARDIANS: locations_to_place = _GUARDIAN_INDICES[:] if mode == LayoutSkyTempleKeyMode.ALL_BOSSES: locations_to_place += _SUB_GUARDIAN_INDICES elif mode == LayoutSkyTempleKeyMode.FULLY_RANDOM: locations_to_place = [] else: raise GenerationFailure("Unknown Sky Temple Key mode: {}".format(mode), permalink) for pickup in available_pickups[:]: if not locations_to_place: break if pickup.item_category == "sky_temple_key": available_pickups.remove(pickup) index = locations_to_place.pop(0) if index in previous_patches.pickup_assignment: raise GenerationFailure( "Attempted to place '{}' in {}, but there's already '{}' there" .format(pickup, index, previous_patches.pickup_assignment[index]), permalink) new_assignments[index] = pickup if locations_to_place: raise GenerationFailure( "Missing Sky Temple Keys in available_pickups to place in all requested boss places", permalink) return previous_patches.assign_pickup_assignment(new_assignments)
def validate_command_logic(args): debug._DEBUG_LEVEL = args.debug data = prime_database.decode_data_file(args) game = data_reader.decode_data(data) if args.layout_file is not None: description = LayoutDescription.from_file(Path(args.layout_file)) configuration = description.permalink.layout_configuration patches = description.patches else: configuration = LayoutConfiguration.default() patches = GamePatches.with_game(game).assign_pickup_assignment( game.pickup_database.original_pickup_mapping) final_state_by_resolve = resolver.resolve(configuration=configuration, game=game, patches=patches) print(final_state_by_resolve)
async def assign_guaranteed_indices_hints(self, patches: GamePatches, identifiers: list[NodeIdentifier], prefill: PreFillParams) -> GamePatches: # Specific Pickup/any LogbookNode Hints indices_with_hint = await self.get_guranteed_hints(patches, prefill) prefill.rng.shuffle(indices_with_hint) all_hint_identifiers = [identifier for identifier in identifiers if identifier not in patches.hints] prefill.rng.shuffle(all_hint_identifiers) for index, precision in indices_with_hint: if not all_hint_identifiers: break identifier = all_hint_identifiers.pop() patches = patches.assign_hint(identifier, Hint(HintType.LOCATION, precision, index)) identifiers.remove(identifier) return patches
def calculate_item_pool( layout_configuration: LayoutConfiguration, resource_database: ResourceDatabase, patches: GamePatches, ) -> Tuple[GamePatches, List[PickupEntry]]: """ Creates a GamePatches with all starting items and pickups in fixed locations, as well as a list of pickups we should shuffle. :param layout_configuration: :param resource_database: :param patches: :return: """ item_pool, pickup_assignment, initial_items = calculate_pool_results( layout_configuration, resource_database) new_patches = patches.assign_pickup_assignment( pickup_assignment).assign_extra_initial_items(initial_items) return new_patches, item_pool
async def assign_specific_location_hints(self, patches: GamePatches, prefill: PreFillParams) -> GamePatches: specific_location_precisions = await self.get_specific_pickup_precision_pair_overrides(patches, prefill) # TODO: this is an Echoes default. Should not have a default and all nodes have one in the DB. default_precision = PrecisionPair(HintLocationPrecision.KEYBEARER, HintItemPrecision.BROAD_CATEGORY, include_owner=True) wl = prefill.game.world_list for node in wl.iterate_nodes(): if isinstance(node, LogbookNode) and node.lore_type == LoreType.SPECIFIC_PICKUP: identifier = wl.identifier_for_node(node) patches = patches.assign_hint( identifier, Hint(HintType.LOCATION, specific_location_precisions.get(identifier, default_precision), PickupIndex(node.hint_index)) ) return patches
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), claris_randomizer.elevator_connections_for_seed_number( permalink.seed_number), {}, {}, (), game.starting_location), solver_path=solver_path, ) # Run decoded = LayoutDescription.from_json_dict(original.as_json) # Assert assert decoded == original
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_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