def __init__( self, index: int, game: GameDescription, initial_state: State, pickups_left: list[PickupEntry], configuration: FillerConfiguration, ): self.index = index self.game = game self.reach = reach_lib.advance_reach_with_possible_unsafe_resources( reach_lib.reach_with_all_safe_resources(game, initial_state)) self.pickups_left = pickups_left self.configuration = configuration self.pickup_index_considered_count = collections.defaultdict(int) self.hint_seen_count = collections.defaultdict(int) self.event_seen_count = collections.defaultdict(int) self.hint_initial_pickups = {} self.num_random_starting_items_placed = 0 self.num_assigned_pickups = 0 self.num_actions = 0 self.indices_groups, self.all_indices = build_available_indices( game.world_list, configuration)
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 retcon_playthrough_filler( rng: Random, player_states: list[PlayerState], status_update: Callable[[str], None], ) -> tuple[dict[PlayerState, GamePatches], tuple[str, ...]]: """ Runs the retcon logic. :param rng: :param player_states: :param status_update: :return: A GamePatches for each player and a sequence of placed items. """ debug.debug_print("{}\nRetcon filler started with major items:\n{}".format( "*" * 100, "\n".join("Player {}: {}".format( player_state.index, pprint.pformat({ item.name: player_state.pickups_left.count(item) for item in sorted(set(player_state.pickups_left), key=lambda item: item.name) })) for player_state in player_states))) last_message = "Starting." def action_report(message: str): status_update("{} {}".format(last_message, message)) for player_state in player_states: player_state.update_for_new_state() actions_log = [] while True: all_locations_weighted = _calculate_all_pickup_indices_weight( player_states) current_player = _get_next_player(rng, player_states, all_locations_weighted) if current_player is None: break weighted_actions = weighted_potential_actions(current_player, action_report, all_locations_weighted) action = select_weighted_action(rng, weighted_actions) if isinstance(action, tuple): new_pickups: list[PickupEntry] = sorted(action) rng.shuffle(new_pickups) debug.debug_print(f"\n>>> Will place {len(new_pickups)} pickups") for new_pickup in new_pickups: log_entry = _assign_pickup_somewhere(new_pickup, current_player, player_states, rng, all_locations_weighted) actions_log.append(log_entry) debug.debug_print(f"* {log_entry}") # TODO: this item is potentially dangerous and we should remove the invalidated paths current_player.pickups_left.remove(new_pickup) current_player.num_actions += 1 else: debug_print_collect_event(action, current_player.game) # This action is potentially dangerous. Use `act_on` to remove invalid paths current_player.reach.act_on(action) last_message = "{} actions performed.".format( sum(player.num_actions for player in player_states)) status_update(last_message) current_player.reach = reach_lib.advance_reach_with_possible_unsafe_resources( current_player.reach) current_player.update_for_new_state() all_patches = { player_state: player_state.reach.state.patches for player_state in player_states } return all_patches, tuple(actions_log)
def _create_reach_with_unsafe(game: GameDescription, state: State) -> GeneratorReach: return reach_lib.advance_reach_with_possible_unsafe_resources( reach_lib.reach_with_all_safe_resources(game, state))