def test_database_collectable(preset_manager, game_enum: RandovaniaGame, ignore_events: set[str], ignore_pickups: set[int]): game, initial_state, permalink = run_bootstrap( preset_manager.default_preset_for_game(game_enum).get_preset()) all_pickups = set( reach_lib.filter_pickup_nodes(game.world_list.iterate_nodes())) pool_results = pool_creator.calculate_pool_results( permalink.get_preset(0).configuration, game.resource_database) initial_state.resources.add_resource_gain( pool_results.initial_resources.as_resource_gain()) for pickup in pool_results.pickups: add_pickup_to_state(initial_state, pickup) for pickup in pool_results.assignment.values(): add_pickup_to_state(initial_state, pickup) for trick in game.resource_database.trick: initial_state.resources.set_resource( trick, LayoutTrickLevel.maximum().as_number) expected_events = sorted([ event for event in game.resource_database.event if event.short_name not in ignore_events ], key=lambda it: it.short_name) expected_pickups = sorted(it.pickup_index for it in all_pickups if it.pickup_index.index not in ignore_pickups) reach = _create_reach_with_unsafe(game, initial_state.heal()) while list(reach_lib.collectable_resource_nodes(reach.nodes, reach)): reach.act_on( next(iter(reach_lib.collectable_resource_nodes(reach.nodes, reach)))) reach = advance_reach_with_possible_unsafe_resources(reach) # print("\nCurrent reach:") # print(game.world_list.node_name(reach.state.node, with_world=True)) # for world in game.world_list.worlds: # print(f"\n>> {world.name}") # for node in world.all_nodes: # print("[{!s:>5}, {!s:>5}, {!s:>5}] {}".format( # reach.is_reachable_node(node), reach.is_safe_node(node), # reach.state.resources.get(node.resource(), 0) > 0 # if isinstance(node, ResourceNode) else "", # game.world_list.node_name(node, with_world=True))) collected_indices = set(reach.state.collected_pickup_indices) collected_events = { resource for resource, quantity in reach.state.resources.as_resource_gain() if quantity > 0 and resource.resource_type == ResourceType.EVENT } assert list(reach_lib.collectable_resource_nodes(reach.nodes, reach)) == [] assert sorted(collected_indices) == expected_pickups assert sorted(collected_events, key=lambda it: it.short_name) == expected_events
def additional_starting_items(layout_configuration: LayoutConfiguration, resource_database: ResourceDatabase, starting_items: CurrentResources) -> List[str]: initial_items = pool_creator.calculate_pool_results(layout_configuration, resource_database)[2] return [ "{}{}".format("{} ".format(quantity) if quantity > 1 else "", _resource_user_friendly_name(item)) for item, quantity in starting_items.items() if 0 < quantity != initial_items.get(item, 0) ]
def test_decode(patches_with_data, default_layout_configuration): encoded, expected = patches_with_data game = data_reader.decode_data(default_layout_configuration.game_data) pool = pool_creator.calculate_pool_results(default_layout_configuration, game.resource_database) # Run decoded = game_patches_serializer.decode_single(0, {0: pool}, game, encoded, default_layout_configuration) # Assert assert decoded == expected
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 decode(game_modifications: List[dict], layout_configurations: Dict[int, EchoesConfiguration], ) -> Dict[int, GamePatches]: all_games = {index: data_reader.decode_data(configuration.game_data) for index, configuration in layout_configurations.items()} all_pools = {index: pool_creator.calculate_pool_results(configuration, all_games[index].resource_database) for index, configuration in layout_configurations.items()} return { index: decode_single(index, all_pools, all_games[index], modifications, layout_configurations[index]) for index, modifications in enumerate(game_modifications) }
async def create_player_pool(rng: Random, configuration: BaseConfiguration, player_index: int, num_players: int, rng_required: bool = True) -> PlayerPool: game = filtered_database.game_description_for_layout( configuration).get_mutable() game_generator = game.game.generator game.resource_database = game_generator.bootstrap.patch_resource_database( game.resource_database, configuration) base_patches = game_generator.base_patches_factory.create_base_patches( configuration, rng, game, num_players > 1, player_index=player_index, rng_required=rng_required) base_patches = dock_weakness_distributor.distribute_pre_fill_weaknesses( base_patches) base_patches = await game_generator.hint_distributor.assign_pre_filler_hints( base_patches, PreFillParams( rng, configuration, game, num_players > 1, ), rng_required=rng_required) pool_results = pool_creator.calculate_pool_results( configuration, game.resource_database, base_patches, rng, rng_required=rng_required) target_assignment = [(index, PickupTarget(pickup, player_index)) for index, pickup in pool_results.assignment.items()] patches = base_patches.assign_new_pickups( target_assignment).assign_extra_initial_items( pool_results.initial_resources.as_resource_gain()) return PlayerPool( game=game, game_generator=game_generator, configuration=configuration, patches=patches, pickups=pool_results.pickups, )
def additional_starting_items(layout_configuration: BaseConfiguration, resource_database: ResourceDatabase, starting_items: CurrentResources) -> List[str]: initial_items = calculate_pool_results(layout_configuration, resource_database)[2] return [ add_quantity_to_resource(resource_user_friendly_name(item), quantity) for item, quantity in sorted( starting_items.items(), key=lambda a: resource_user_friendly_name(a[0])) if 0 < quantity != initial_items.get(item, 0) ]
def additional_starting_items(layout_configuration: BaseConfiguration, resource_database: ResourceDatabase, starting_items: ResourceCollection) -> List[str]: initial_items = calculate_pool_results(layout_configuration, resource_database).initial_resources return [ add_quantity_to_resource(resource_user_friendly_name(item), quantity) for item, quantity in sorted( starting_items.as_resource_gain(), key=lambda a: resource_user_friendly_name(a[0])) if 0 < quantity != initial_items[item] ]
def test_database_collectable(preset_manager, preset_name, ignore_events, ignore_pickups): game, initial_state, permalink = run_bootstrap( preset_manager.preset_for_name(preset_name).get_preset()) all_pickups = set(filter_pickup_nodes(game.world_list.all_nodes)) pool_results = pool_creator.calculate_pool_results( permalink.get_preset(0).configuration, game.resource_database) add_resources_into_another(initial_state.resources, pool_results.initial_resources) for pickup in pool_results.pickups: add_pickup_to_state(initial_state, pickup) for pickup in pool_results.assignment.values(): add_pickup_to_state(initial_state, pickup) for trick in game.resource_database.trick: initial_state.resources[trick] = LayoutTrickLevel.HYPERMODE.as_number expected_events = [ event for event in game.resource_database.event if event.index not in ignore_events ] expected_pickups = sorted(it.pickup_index for it in all_pickups if it.pickup_index.index not in ignore_pickups) reach = _create_reach_with_unsafe(game, initial_state.heal()) while list(collectable_resource_nodes(reach.nodes, reach)): reach.act_on(next(iter(collectable_resource_nodes(reach.nodes, reach)))) reach = advance_reach_with_possible_unsafe_resources(reach) # print("\nCurrent reach:") # for world in game.world_list.worlds: # print(f"\n>> {world.name}") # for node in world.all_nodes: # print("[{!s:>5}, {!s:>5}, {!s:>5}] {}".format( # reach.is_reachable_node(node), reach.is_safe_node(node), # reach.state.resources.get(node.resource(), 0) > 0 if isinstance(node, ResourceNode) else "", # game.world_list.node_name(node))) collected_indices = { resource for resource, quantity in reach.state.resources.items() if quantity > 0 and isinstance(resource, PickupIndex) } collected_events = { resource for resource, quantity in reach.state.resources.items() if quantity > 0 and resource.resource_type == ResourceType.EVENT } assert list(collectable_resource_nodes(reach.nodes, reach)) == [] assert sorted(collected_indices) == expected_pickups assert sorted(collected_events, key=lambda it: it.index) == expected_events
def test_create_pickup_all_from_pool(echoes_resource_database, default_layout_configuration, disable_hud_popup: bool): item_pool = pool_creator.calculate_pool_results( default_layout_configuration, echoes_resource_database) index = PickupIndex(0) if disable_hud_popup: memo_data = patcher_file._SimplifiedMemo() else: memo_data = default_prime2_memo_data() creator = patcher_file.PickupCreatorSolo(MagicMock(), memo_data) for item in item_pool.pickups: creator.create_pickup(index, PickupTarget(item, 0), item, PickupModelStyle.ALL_VISIBLE)
def test_decode(patches_with_data, default_echoes_configuration): encoded, expected = patches_with_data game = expected.game pool = pool_creator.calculate_pool_results(default_echoes_configuration, game.resource_database) # Run decoded = game_patches_serializer.decode_single( 0, {0: pool}, game, encoded, default_echoes_configuration) # Assert assert set(decoded.all_elevator_connections()) == set( expected.all_elevator_connections()) assert decoded == expected
def test_create_pickup_all_from_pool(echoes_resource_database, disable_hud_popup: bool ): layout_configuration = LayoutConfiguration.from_params() item_pool = pool_creator.calculate_pool_results(layout_configuration, echoes_resource_database)[0] index = PickupIndex(0) if disable_hud_popup: memo_data = None else: memo_data = default_prime2_memo_data() for item in item_pool: try: patcher_file._create_pickup(index, item, item, PickupModelStyle.ALL_VISIBLE, memo_data) except Exception as e: assert str(e) == item.name
def test_create_pickup_all_from_pool(echoes_resource_database, default_layout_configuration, disable_hud_popup: bool): item_pool = pool_creator.calculate_pool_results( default_layout_configuration, echoes_resource_database) index = PickupIndex(0) if disable_hud_popup: memo_data = claris_patcher_file._simplified_memo_data() else: memo_data = default_prime2_memo_data() creator = pickup_exporter.PickupExporterSolo(memo_data) for item in item_pool.pickups: data = creator.export(index, PickupTarget(item, 0), item, PickupModelStyle.ALL_VISIBLE) for hud_text in data.hud_text: assert not hud_text.startswith("Locked")
def test_decode(patches_with_data, default_echoes_configuration): encoded, expected = patches_with_data data = default_data.read_json_then_binary( default_echoes_configuration.game)[1] game = data_reader.decode_data(data) pool = pool_creator.calculate_pool_results(default_echoes_configuration, game.resource_database) # Run decoded = game_patches_serializer.decode_single( 0, {0: pool}, game, encoded, default_echoes_configuration) # Assert assert decoded.elevator_connection == expected.elevator_connection assert decoded == expected
def _create_starting_popup(layout_configuration: LayoutConfiguration, resource_database: ResourceDatabase, starting_items: CurrentResources) -> list: initial_items = pool_creator.calculate_pool_results( layout_configuration, resource_database)[2] extra_items = [ "{}{}".format("{} ".format(quantity) if quantity > 1 else "", _resource_user_friendly_name(item)) for item, quantity in starting_items.items() if 0 < quantity != initial_items.get(item, 0) ] if extra_items: return ["Extra starting items:", ", ".join(extra_items)] else: return []
def decode( game_modifications: List[dict], layout_configurations: Dict[int, BaseConfiguration], ) -> Dict[int, GamePatches]: all_games = { index: filtered_database.game_description_for_layout(configuration) for index, configuration in layout_configurations.items() } all_pools = { index: pool_creator.calculate_pool_results(configuration, all_games[index].resource_database) for index, configuration in layout_configurations.items() } return { index: decode_single(index, all_pools, all_games[index], modifications, layout_configurations[index]) for index, modifications in enumerate(game_modifications) }
async def create_player_pool(rng: Random, configuration: BaseConfiguration, player_index: int, num_players: int, rng_required: bool = True) -> PlayerPool: game = filtered_database.game_description_for_layout(configuration).get_mutable() derived_nodes.create_derived_nodes(game) game_generator = game.game.generator game.resource_database = game_generator.bootstrap.patch_resource_database(game.resource_database, configuration) base_patches = game_generator.base_patches_factory.create_base_patches(configuration, rng, game, num_players > 1, player_index=player_index, rng_required=rng_required) base_patches = await game_generator.hint_distributor.assign_pre_filler_hints( base_patches, PreFillParams( rng, configuration, game, num_players > 1, ), rng_required=rng_required ) item_pool, pickup_assignment, initial_items = pool_creator.calculate_pool_results(configuration, game.resource_database, base_patches, rng, rng_required=rng_required) target_assignment = { index: PickupTarget(pickup, player_index) for index, pickup in pickup_assignment.items() } patches = base_patches.assign_pickup_assignment(target_assignment).assign_extra_initial_items(initial_items) return PlayerPool( game=game, game_generator=game_generator, configuration=configuration, patches=patches, pickups=item_pool, )
def create_player_pool(rng: Random, configuration: EchoesConfiguration, player_index: int, num_players: int) -> PlayerPool: game = default_database.game_description_for(configuration.game) base_patches = base_patches_factory.create_base_patches(configuration, rng, game, num_players > 1, player_index=player_index) item_pool, pickup_assignment, initial_items = pool_creator.calculate_pool_results(configuration, game.resource_database) target_assignment = { index: PickupTarget(pickup, player_index) for index, pickup in pickup_assignment.items() } patches = base_patches.assign_pickup_assignment(target_assignment).assign_extra_initial_items(initial_items) return PlayerPool( game=game, configuration=configuration, patches=patches, pickups=item_pool, )
def create_player_pool(rng: Random, configuration: LayoutConfiguration, player_index: int) -> PlayerPool: game = data_reader.decode_data(configuration.game_data) base_patches = dataclasses.replace(base_patches_factory.create_base_patches(configuration, rng, game), player_index=player_index) item_pool, pickup_assignment, initial_items = pool_creator.calculate_pool_results(configuration, game.resource_database) target_assignment = { index: PickupTarget(pickup, player_index) for index, pickup in pickup_assignment.items() } patches = base_patches.assign_pickup_assignment(target_assignment).assign_extra_initial_items(initial_items) return PlayerPool( game=game, configuration=configuration, patches=patches, pickups=item_pool, )
def test_calculate_reach_with_all_pickups(test_data): game, state, permalink = test_data pool_results = pool_creator.calculate_pool_results( permalink.get_preset(0).layout_configuration, game.resource_database) add_resources_into_another(state.resources, pool_results.initial_resources) for pickup in pool_results.pickups: add_pickup_to_state(state, pickup) for pickup in pool_results.assignment.values(): add_pickup_to_state(state, pickup) first_reach, second_reach = _create_reaches_and_compare(game, state) first_actions, second_actions = _compare_actions(first_reach, second_reach) found_pickups = set( filter_pickup_nodes(filter_reachable(second_reach.nodes, first_reach))) all_pickups = set(filter_pickup_nodes(game.world_list.all_nodes)) # assert (len(list(first_reach.nodes)), len(first_actions)) == (898, 9) # assert (len(list(second_reach.nodes)), len(second_actions)) == (898, 9) pprint.pprint(first_actions) assert all_pickups == found_pickups
def on_preset_changed(self, preset: Preset): # Item alternatives layout = preset.configuration major_configuration = layout.major_items_configuration for progressive_widget in self._progressive_widgets: progressive_widget.on_preset_changed( preset, self._boxes_for_category[ progressive_widget.progressive_item.item_category][2], ) for split_ammo in self._split_ammo_widgets: split_ammo.on_preset_changed(preset, self._ammo_pickup_widgets) # Random Starting Items self.minimum_starting_spinbox.setValue( major_configuration.minimum_random_starting_items) self.maximum_starting_spinbox.setValue( major_configuration.maximum_random_starting_items) # Default Items for category, default_item in major_configuration.default_items.items( ): combo = self._default_items[category] combo.setCurrentIndex(combo.findData(default_item)) for item, widget in self._boxes_for_category[category][2].items(): widget.setEnabled(default_item != item) # Major Items for _, _, elements in self._boxes_for_category.values(): for major_item, widget in elements.items(): widget.state = major_configuration.items_state[major_item] # Energy Tank energy_tank_state = major_configuration.items_state[ self._energy_tank_item] self.energy_tank_starting_spinbox.setValue( energy_tank_state.num_included_in_starting_items) self.energy_tank_shuffled_spinbox.setValue( energy_tank_state.num_shuffled_pickups) # Ammo ammo_provided = major_configuration.calculate_provided_ammo() ammo_configuration = layout.ammo_configuration for ammo_item, maximum in ammo_configuration.maximum_ammo.items(): for spinbox in self._ammo_maximum_spinboxes[ammo_item]: spinbox.setValue(maximum) previous_pickup_for_item = {} resource_database = self.game_description.resource_database item_for_index: Dict[int, ItemResourceInfo] = { ammo_index: resource_database.get_item(ammo_index) for ammo_index in ammo_provided.keys() } for ammo, state in ammo_configuration.items_state.items(): widgets = self._ammo_pickup_widgets[ammo] widgets.pickup_spinbox.setValue(state.pickup_count) if widgets.require_major_item_check is not None: widgets.require_major_item_check.setChecked( state.requires_major_item) try: if state.pickup_count == 0: widgets.expected_count.setText( "No expansions will be created.") continue ammo_per_pickup = items_for_ammo( ammo, state, ammo_provided, previous_pickup_for_item, ammo_configuration.maximum_ammo) totals = functools.reduce( lambda a, b: [x + y for x, y in zip(a, b)], ammo_per_pickup, [0 for _ in ammo.items]) if {total % state.pickup_count for total in totals} == {0}: count_text_template = _EXPECTED_COUNT_TEXT_TEMPLATE_EXACT else: count_text_template = _EXPECTED_COUNT_TEXT_TEMPLATE widgets.expected_count.setText( count_text_template.format( per_expansion=" and ".join( "{:.3g} {}".format( total / state.pickup_count, item_for_index[ammo_index].long_name) for ammo_index, total in zip(ammo.items, totals)), total=" and ".join( "{} {}".format( total, item_for_index[ammo_index].long_name) for ammo_index, total in zip(ammo.items, totals)), from_items=" and ".join("{} {}".format( ammo_provided[ammo_index], item_for_index[ammo_index].long_name) for ammo_index in ammo.items), )) except InvalidConfiguration as invalid_config: widgets.expected_count.setText(str(invalid_config)) # Item pool count try: pool_pickup = pool_creator.calculate_pool_results( layout, resource_database).pickups min_starting_items = layout.major_items_configuration.minimum_random_starting_items maximum_size = self.game_description.world_list.num_pickup_nodes + min_starting_items self.item_pool_count_label.setText("Items in pool: {}/{}".format( len(pool_pickup), maximum_size)) common_qt_lib.set_error_border_stylesheet( self.item_pool_count_label, len(pool_pickup) > maximum_size) except InvalidConfiguration as invalid_config: self.item_pool_count_label.setText( "Invalid Configuration: {}".format(invalid_config)) common_qt_lib.set_error_border_stylesheet( self.item_pool_count_label, True)