def test_add_resources_into_another(blank_resource_db, a, b, result): a = wrap(blank_resource_db, a) b = wrap(blank_resource_db, b) result = wrap(blank_resource_db, result) ac = ResourceCollection.from_dict(blank_resource_db, a) bc = ResourceCollection.from_dict(blank_resource_db, b) ac.add_resource_gain(bc.as_resource_gain()) assert dict(ac.as_resource_gain()) == result
def test_assign_extra_initial_items_merge(empty_patches, initial, new_items, expected): db = empty_patches.game.resource_database initial = wrap(db, initial) new_items = wrap(db, new_items) expected = wrap(db, expected) # Setup initial_patches = dataclasses.replace(empty_patches, starting_items=ResourceCollection.from_dict(db, initial)) # Run new_patches = initial_patches.assign_extra_initial_items( ResourceCollection.from_dict(db, new_items).as_resource_gain(), ) # Assert assert new_patches.starting_items == ResourceCollection.from_dict(db, expected)
def test_add_pickup_to_state(state_game_data, empty_patches, generic_item_category): # Starting State db = state_game_data.resource_database starting_node = state_game_data.world_list.resolve_teleporter_connection( empty_patches.game.starting_location) s = state.State(ResourceCollection(), (), 99, starting_node, empty_patches, None, state_game_data) resource_a = db.item[0] resource_b = db.item[1] p = PickupEntry("B", 2, generic_item_category, generic_item_category, progression=( (resource_a, 1), (resource_b, 1), )) # Run state.add_pickup_to_state(s, p) state.add_pickup_to_state(s, p) # Assert assert s.resources == ResourceCollection.from_dict(db, { resource_a: 1, resource_b: 1, })
def test_collected_pickup_indices(state_game_data, empty_patches): # Setup db = state_game_data.resource_database starting = state_game_data.world_list.resolve_teleporter_connection( empty_patches.game.starting_location) pickup_nodes = [ node for node in empty_patches.game.world_list.all_nodes if isinstance(node, PickupNode) ] context = NodeContext( empty_patches, ResourceCollection(), empty_patches.game.resource_database, empty_patches.game.world_list, ) resources = ResourceCollection.from_dict( db, { db.item[0]: 5, pickup_nodes[0].resource(context): 1, pickup_nodes[1].resource(context): 1 }) s = state.State(resources, (), 99, starting, empty_patches, None, state_game_data) # Run indices = list(s.collected_pickup_indices) # Assert assert indices == [ pickup_nodes[0].pickup_index, pickup_nodes[1].pickup_index ]
def test_remove_resource_missing(echoes_resource_database): m = echoes_resource_database.get_item("Missile") beam = echoes_resource_database.get_item("Light") col = ResourceCollection.from_dict(echoes_resource_database, { beam: 1, }) col.remove_resource(m) assert dict(col.as_resource_gain()) == {beam: 1}
def test_requirement_damage(damage, items, requirement, echoes_resource_database): req = data_reader.read_requirement(requirement, echoes_resource_database) collection = ResourceCollection.from_dict( echoes_resource_database, {echoes_resource_database.get_item(item): 1 for item in items}) assert req.damage(collection, echoes_resource_database) == damage
def test_sky_temple_key_distribution_logic_all_bosses_valid( echoes_resource_database): # Run results = sky_temple_keys.add_sky_temple_key_distribution_logic( echoes_resource_database, LayoutSkyTempleKeyMode.ALL_BOSSES) # Assert assert results.pickups == [] assert results.initial_resources == ResourceCollection.from_dict( echoes_resource_database, {}) assert list( results.assignment.keys() ) == sky_temple_keys._GUARDIAN_INDICES + sky_temple_keys._SUB_GUARDIAN_INDICES
def test_sky_temple_key_distribution_logic_all_guardians_valid( echoes_resource_database): # Run results = sky_temple_keys.add_sky_temple_key_distribution_logic( echoes_resource_database, LayoutSkyTempleKeyMode.ALL_GUARDIANS) # Assert assert results.pickups == [] assert results.initial_resources == ResourceCollection.from_dict( echoes_resource_database, { echoes_resource_database.get_item(f'TempleKey{i}'): 1 for i in range(4, 10) }) assert list(results.assignment.keys()) == sky_temple_keys._GUARDIAN_INDICES
def test_assign_pickup_to_starting_items(empty_patches, state_game_data, generic_item_category): # Setup db = state_game_data.resource_database starting_node = state_game_data.world_list.resolve_teleporter_connection( empty_patches.game.starting_location) starting = state.State(ResourceCollection(), (), 99, starting_node, empty_patches, None, state_game_data) resource_a = db.get_item("Ammo") resource_b = db.item[0] p = PickupEntry( "A", 2, generic_item_category, generic_item_category, progression=((resource_a, 5), ), extra_resources=(), unlocks_resource=True, resource_lock=ResourceLock(resource_a, resource_a, resource_b), ) # Run final = starting.assign_pickup_to_starting_items(p) # Assert assert final.patches.starting_items == ResourceCollection.from_dict( db, { resource_a: 5, resource_b: 0 }) assert final.resources == ResourceCollection.from_dict( db, { resource_a: 5, resource_b: 0 })
def test_sky_temple_key_distribution_logic_with_quantity( echoes_resource_database, quantity: int): # Run results = sky_temple_keys.add_sky_temple_key_distribution_logic( echoes_resource_database, LayoutSkyTempleKeyMode(quantity)) # ItemResourceInfo(f'Sky Temple Key {i}', , 1, frozendict({"item_id": item_ids[i - 1]})): 1 # Assert assert results.pickups == [ pickup_creator.create_sky_temple_key(i, echoes_resource_database) for i in range(quantity) ] assert results.assignment == {} assert results.initial_resources == ResourceCollection.from_dict( echoes_resource_database, { echoes_resource_database.get_item(f'TempleKey{i}'): 1 for i in range(quantity + 1, 10) })
def test_create_starting_popup_items(default_echoes_configuration, echoes_resource_database): starting_items = ResourceCollection.from_dict( echoes_resource_database, { echoes_resource_database.get_item_by_name("Missile"): 15, echoes_resource_database.energy_tank: 3, echoes_resource_database.get_item_by_name("Dark Beam"): 1, echoes_resource_database.get_item_by_name("Screw Attack"): 1, }) # Run result = patch_data_factory._create_starting_popup( default_echoes_configuration, echoes_resource_database, starting_items) # Assert assert result == [ 'Extra starting items:', 'Dark Beam, 3 Energy Tank, 15 Missiles, Screw Attack' ]
def test_state_with_pickup(state_game_data, empty_patches, generic_item_category): # Setup db = state_game_data.resource_database starting = state.State(ResourceCollection(), (), 99, None, empty_patches, None, state_game_data) resource_a = db.item[0] p = PickupEntry("A", 2, generic_item_category, generic_item_category, progression=((resource_a, 1), )) # Run final = state.state_with_pickup(starting, p) # Assert assert final.previous_state is starting assert final.resources == ResourceCollection.from_dict(db, {resource_a: 1})
def ctx(*args: ResourceInfo): resources = ResourceCollection.from_dict(db, {r: 1 for r in args}) return NodeContext(empty_patches, resources, db, node_provider)
def _col_for(db: ResourceDatabase, *args: ResourceInfo): return ResourceCollection.from_dict(db, {resource: 1 for resource in args})
def test_basic_search_with_translator_gate(has_translator: bool, echoes_resource_database, echoes_game_patches): # Setup scan_visor = echoes_resource_database.get_item("DarkVisor") nc = functools.partial(NodeIdentifier.create, "Test World", "Test Area A") node_a = GenericNode(nc("Node A"), 0, True, None, "", ("default", ), {}) node_b = GenericNode(nc("Node B"), 1, True, None, "", ("default", ), {}) node_c = GenericNode(nc("Node C"), 2, True, None, "", ("default", ), {}) translator_node = ConfigurableNode( translator_identif := nc("Translator Gate"), 3, True, None, "", ("default", ), {}) world_list = WorldList([ World("Test World", [ Area( "Test Area A", None, True, [node_a, node_b, node_c, translator_node], { node_a: { node_b: Requirement.trivial(), translator_node: Requirement.trivial(), }, node_b: { node_a: Requirement.trivial(), }, node_c: { translator_node: Requirement.trivial(), }, translator_node: { node_a: Requirement.trivial(), node_c: Requirement.trivial(), }, }, {}) ], {}) ]) game = GameDescription(RandovaniaGame.METROID_PRIME_ECHOES, DockWeaknessDatabase([], {}, {}, (None, None)), echoes_resource_database, ("default", ), Requirement.impossible(), None, {}, None, world_list) patches = echoes_game_patches.assign_node_configuration([ (translator_identif, ResourceRequirement.simple(scan_visor)), ]) initial_state = State( ResourceCollection.from_dict(echoes_resource_database, {scan_visor: 1 if has_translator else 0}), (), 99, node_a, patches, None, StateGameData(echoes_resource_database, game.world_list, 100, 99), ) # Run reach = reach_lib.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 decode_single(player_index: int, all_pools: dict[int, PoolResults], game: GameDescription, game_modifications: dict, configuration: BaseConfiguration) -> GamePatches: """ Decodes a dict created by `serialize` back into a GamePatches. :param player_index: :param all_pools: :param game: :param game_modifications: :param configuration: :return: """ world_list = game.world_list weakness_db = game.dock_weakness_database if game_modifications["game"] != game.game.value: raise ValueError( f"Expected '{game.game.value}', got '{game_modifications['game']}'" ) initial_pickup_assignment = all_pools[player_index].assignment # Starting Location starting_location = AreaIdentifier.from_string( game_modifications["starting_location"]) # Initial items starting_items = ResourceCollection.from_dict( game.resource_database, { find_resource_info_with_long_name(game.resource_database.item, resource_name): quantity for resource_name, quantity in game_modifications["starting_items"].items() }) # Elevators elevator_connection = [(world_list.get_teleporter_node( NodeIdentifier.from_string(source_name)), AreaIdentifier.from_string(target_name)) for source_name, target_name in game_modifications["teleporters"].items()] # Dock Weakness def get_dock(ni: NodeIdentifier): result = game.world_list.node_by_identifier(ni) assert isinstance(result, DockNode) return result dock_weakness = [(get_dock(NodeIdentifier.from_string(source_name)), weakness_db.get_by_weakness( weakness_data["type"], weakness_data["name"], )) for source_name, weakness_data in game_modifications["dock_weakness"].items()] # Configurable Nodes configurable_nodes = { NodeIdentifier.from_string(identifier): data_reader.read_requirement(requirement, game.resource_database) for identifier, requirement in game_modifications["configurable_nodes"].items() } # Pickups target_name_re = re.compile(r"(.*) for Player (\d+)") pickup_assignment: PickupAssignment = {} for world_name, world_data in game_modifications["locations"].items(): for area_node_name, target_name in typing.cast(dict[str, str], world_data).items(): if target_name == _ETM_NAME: continue pickup_name_match = target_name_re.match(target_name) if pickup_name_match is not None: pickup_name = pickup_name_match.group(1) target_player = int(pickup_name_match.group(2)) - 1 else: pickup_name = target_name target_player = 0 node_identifier = NodeIdentifier.create( world_name, *area_node_name.split("/", 1)) node = world_list.node_by_identifier(node_identifier) assert isinstance(node, PickupNode) if node.pickup_index in initial_pickup_assignment: pickup = initial_pickup_assignment[node.pickup_index] if (pickup_name, target_player) != (pickup.name, player_index): raise ValueError( f"{area_node_name} should be vanilla based on configuration" ) elif pickup_name != _ETM_NAME: configuration_item_pool = all_pools[target_player].pickups pickup = _find_pickup_with_name(configuration_item_pool, pickup_name) configuration_item_pool.remove(pickup) else: pickup = None if pickup is not None: pickup_assignment[node.pickup_index] = PickupTarget( pickup, target_player) # Hints hints = {} for identifier_str, hint in game_modifications["hints"].items(): hints[NodeIdentifier.from_string(identifier_str)] = Hint.from_json( hint) patches = GamePatches.create_from_game(game, player_index, configuration) patches = patches.assign_dock_weakness(dock_weakness) patches = patches.assign_elevators(elevator_connection) return dataclasses.replace( patches, pickup_assignment=pickup_assignment, # PickupAssignment configurable_nodes=configurable_nodes, starting_items=starting_items, # ResourceGainTuple starting_location=starting_location, # AreaIdentifier hints=hints, )