def fill_unassigned_hints( patches: GamePatches, world_list: WorldList, rng: Random, ) -> GamePatches: new_hints = copy.copy(patches.hints) # Get all LogbookAssets from the WorldList potential_hint_locations: Set[LogbookAsset] = { node.resource() for node in world_list.all_nodes if isinstance(node, LogbookNode) } # But remove these that already have hints potential_hint_locations -= patches.hints.keys() # Get interesting items to place hints for possible_indices = set(patches.pickup_assignment.keys()) possible_indices -= {hint.target for hint in patches.hints.values()} possible_indices -= { index for index in possible_indices if not should_have_hint(patches.pickup_assignment[index].item_category) } debug.debug_print( "fill_unassigned_hints had {} decent indices for {} hint locations". format(len(possible_indices), len(potential_hint_locations))) # But if we don't have enough hints, just pick randomly from everything if len(possible_indices) < len(potential_hint_locations): possible_indices = { node.pickup_index for node in world_list.all_nodes if isinstance(node, PickupNode) } # Get an stable order then shuffle possible_indices = list(sorted(possible_indices)) rng.shuffle(possible_indices) for logbook in sorted(potential_hint_locations): new_hints[logbook] = Hint(HintType.LOCATION, None, possible_indices.pop()) debug.debug_print( f"Added hint at {logbook} for item at {new_hints[logbook].target}") return dataclasses.replace(patches, hints=new_hints)
def _calculate_hint_location_for_action(action: PickupEntry, current_uncollected: UncollectedState, pickup_index: PickupIndex, rng: Random, scan_asset_initial_pickups: Dict[LogbookAsset, FrozenSet[PickupIndex]], ) -> Optional[LogbookAsset]: """ Calculates where a hint for the given action should be placed. :return: A LogbookAsset to use, or None if no hint should be placed. """ if should_have_hint(action.item_category): potential_hint_locations = [ logbook_asset for logbook_asset in current_uncollected.logbooks if pickup_index not in scan_asset_initial_pickups[logbook_asset] ] if potential_hint_locations: return rng.choice(potential_hint_locations) return None
def fill_unassigned_hints(patches: GamePatches, world_list: WorldList, rng: Random, scan_asset_initial_pickups: Dict[LogbookAsset, FrozenSet[PickupIndex]], ) -> GamePatches: new_hints = copy.copy(patches.hints) # Get all LogbookAssets from the WorldList potential_hint_locations: Set[LogbookAsset] = { node.resource() for node in world_list.all_nodes if isinstance(node, LogbookNode) } for logbook in potential_hint_locations: if logbook not in scan_asset_initial_pickups: scan_asset_initial_pickups[logbook] = frozenset() # But remove these that already have hints potential_hint_locations -= patches.hints.keys() # Get interesting items to place hints for possible_indices = set(patches.pickup_assignment.keys()) possible_indices -= {hint.target for hint in patches.hints.values() if hint.target is not None} possible_indices -= {index for index in possible_indices if not should_have_hint(patches.pickup_assignment[index].pickup.item_category)} debug.debug_print("fill_unassigned_hints had {} decent indices for {} hint locations".format( len(possible_indices), len(potential_hint_locations))) if debug.debug_level() > 1: print(f"> Num pickups per asset:") for asset, pickups in scan_asset_initial_pickups.items(): print(f"* {asset}: {len(pickups)} pickups") print("> Done.") # But if we don't have enough hints, just pick randomly from everything if len(possible_indices) < len(potential_hint_locations): possible_indices = {node.pickup_index for node in world_list.all_nodes if isinstance(node, PickupNode)} # Get an stable order ordered_possible_indices = list(sorted(possible_indices)) ordered_potential_hint_locations = list(sorted(potential_hint_locations)) num_logbooks: Dict[PickupIndex, int] = { index: sum(1 for indices in scan_asset_initial_pickups.values() if index in indices) for index in ordered_possible_indices } max_seen = max(num_logbooks.values()) pickup_indices_weight: Dict[PickupIndex, int] = { index: max_seen - num_logbook for index, num_logbook in num_logbooks.items() } # Ensure all indices are present with at least weight 0 for index in ordered_possible_indices: if index not in pickup_indices_weight: pickup_indices_weight[index] = 0 for logbook in sorted(ordered_potential_hint_locations, key=lambda r: len(scan_asset_initial_pickups[r]), reverse=True): try: new_index = random_lib.select_element_with_weight(pickup_indices_weight, rng) except StopIteration: # If everything has weight 0, then just choose randomly. new_index = random_lib.random_key(pickup_indices_weight, rng) del pickup_indices_weight[new_index] new_hints[logbook] = Hint(HintType.LOCATION, None, new_index) debug.debug_print(f"Added hint at {logbook} for item at {new_index}") return dataclasses.replace(patches, hints=new_hints)