def resource_gain_on_collect(self, context: NodeContext) -> ResourceGain: dock = self._get_dock(context) dock_target = dock.get_target_identifier(context) front_weak = dock.get_front_weakness(context) if not context.has_resource(self.dock_identifier) and front_weak.lock is not None: yield self.dock_identifier, 1 if not context.has_resource(dock_target) and front_weak.can_unlock_from_back(dock.get_back_weakness(context)): yield dock_target, 1
def can_collect(self, context: NodeContext) -> bool: dock = self._get_dock(context) front_weak = dock.get_front_weakness(context) if not context.has_resource(self.dock_identifier): if front_weak.lock is not None: return True dock_target = dock.get_target_identifier(context) if not context.has_resource(dock_target): if front_weak.can_unlock_from_back(dock.get_back_weakness(context)): return True return False
def resource_gain_on_collect(self, context: NodeContext) -> ResourceGain: dock = self.dock dock_resource = self.resource(context) target_resource = NodeResourceInfo.from_node( dock.get_target_identifier(context), context) front_weak = dock.get_front_weakness(context) if not context.has_resource( dock_resource) and front_weak.lock is not None: yield dock_resource, 1 if not context.has_resource( target_resource) and front_weak.can_unlock_from_back( dock.get_back_weakness(context)): yield target_resource, 1
def can_collect(self, context: NodeContext) -> bool: dock = self.dock front_weak = dock.get_front_weakness(context) if not context.has_resource(self.resource(context)): if front_weak.lock is not None: return True target = dock.get_target_identifier(context) if not context.has_resource(NodeResourceInfo.from_node( target, context)): if front_weak.can_unlock_from_back( dock.get_back_weakness(context)): return True return False
def is_collected(self, context: NodeContext) -> bool: current_resources = context.current_resources return all( context.has_resource(node.resource(context)) for node in _all_ship_nodes(context) if node.is_unlocked.satisfied(current_resources, 0, context.database) )
def node_context(self) -> NodeContext: return NodeContext( self.patches, self.resources, self.resource_database, self.game_data.world_list, )
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_connections_from_dock_blast_shield(empty_patches): # Setup trivial = Requirement.trivial() req_1 = ResourceRequirement.simple( SimpleResourceInfo(0, "Ev1", "Ev1", ResourceType.EVENT)) req_2 = ResourceRequirement.simple( SimpleResourceInfo(1, "Ev2", "Ev2", ResourceType.EVENT)) dock_type = DockType("Type", "Type", frozendict()) weak_1 = DockWeakness(0, "Weak 1", frozendict(), req_1, None) weak_2 = DockWeakness(1, "Weak 2", frozendict(), trivial, DockLock(DockLockType.FRONT_BLAST_BACK_BLAST, req_2)) node_1_identifier = NodeIdentifier.create("W", "Area 1", "Node 1") node_2_identifier = NodeIdentifier.create("W", "Area 2", "Node 2") node_1 = DockNode(node_1_identifier, 0, False, None, "", ("default", ), {}, dock_type, node_2_identifier, weak_1, None, None) node_1_lock = DockLockNode.create_from_dock(node_1, 1) node_2 = DockNode(node_2_identifier, 2, False, None, "", ("default", ), {}, dock_type, node_1_identifier, weak_2, None, None) node_2_lock = DockLockNode.create_from_dock(node_2, 3) area_1 = Area("Area 1", None, True, [node_1, node_1_lock], {}, {}) area_2 = Area("Area 2", None, True, [node_2, node_2_lock], {}, {}) world = World("W", [area_1, area_2], {}) world_list = WorldList([world]) world_list.ensure_has_node_cache() game_mock = MagicMock() game_mock.world_list = world_list patches = dataclasses.replace(empty_patches, game=game_mock) context = NodeContext( patches=patches, current_resources=ResourceCollection(), database=patches.game.resource_database, node_provider=world_list, ) # Run result_1 = list(node_1.connections_from(context)) result_2 = list(node_2.connections_from(context)) # Assert simple = ResourceRequirement.simple assert result_1 == [ (node_2, RequirementAnd( [req_1, simple(NodeResourceInfo.from_node(node_2, context))])), (node_1_lock, RequirementAnd([trivial, req_2])), ] assert result_2 == [ (node_1, simple(NodeResourceInfo.from_node(node_2, context))), (node_2_lock, req_2), ]
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_node_resource_info_as_requirement(blank_game_description): db = blank_game_description.resource_database node = blank_game_description.world_list.all_nodes[0] context = NodeContext(None, None, db, blank_game_description.world_list) nri = NodeResourceInfo.from_node req = ResourceRequirement.simple(nri(node, context)) assert not req.satisfied(_empty_col(), 0, db) assert req.satisfied(_col_for(db, nri(node, context)), 0, db)
def test_logbook_node_resource_gain_on_collect(logbook_node, empty_patches): # Setup db = empty_patches.game.resource_database node = logbook_node[-1] context = NodeContext(empty_patches, ResourceCollection(), db, MagicMock()) # Run gain = node.resource_gain_on_collect(context) # Assert assert dict(gain) == {node.resource(context): 1}
def test_connections_from_dock_blast_shield(empty_patches): # Setup trivial = Requirement.trivial() req_1 = ResourceRequirement( SimpleResourceInfo("Ev1", "Ev1", ResourceType.EVENT), 1, False) req_2 = ResourceRequirement( SimpleResourceInfo("Ev2", "Ev2", ResourceType.EVENT), 1, False) dock_type = DockType("Type", "Type", frozendict()) weak_1 = DockWeakness("Weak 1", frozendict(), req_1, None) weak_2 = DockWeakness("Weak 2", frozendict(), trivial, DockLock(DockLockType.FRONT_BLAST_BACK_BLAST, req_2)) node_1_identifier = NodeIdentifier.create("W", "Area 1", "Node 1") node_2_identifier = NodeIdentifier.create("W", "Area 2", "Node 2") node_1 = DockNode(node_1_identifier, False, None, "", ("default", ), {}, dock_type, node_2_identifier, weak_1, None, None) node_1_lock = DockLockNode.create_from_dock(node_1) node_2 = DockNode(node_2_identifier, False, None, "", ("default", ), {}, dock_type, node_1_identifier, weak_2, None, None) node_2_lock = DockLockNode.create_from_dock(node_2) area_1 = Area("Area 1", None, True, [node_1, node_1_lock], {}, {}) area_2 = Area("Area 2", None, True, [node_2, node_2_lock], {}, {}) world = World("W", [area_1, area_2], {}) world_list = WorldList([world]) context = NodeContext( patches=empty_patches, current_resources={}, database=None, node_provider=world_list, ) # Run result_1 = list(node_1.connections_from(context)) result_2 = list(node_2.connections_from(context)) # Assert assert result_1 == [ (node_2, RequirementAnd([req_1, ResourceRequirement.simple(node_2_identifier)])), (node_1_lock, RequirementAnd([trivial, req_2])), ] assert result_2 == [ (node_1, ResourceRequirement.simple(node_2_identifier)), (node_2_lock, req_2), ]
def calculate_starting_state(self, game: GameDescription, patches: GamePatches, configuration: BaseConfiguration) -> "State": starting_node = game.world_list.resolve_teleporter_connection( patches.starting_location) initial_resources = copy.copy(patches.starting_items) starting_energy, energy_per_tank = self.energy_config(configuration) if starting_node.is_resource_node: assert isinstance(starting_node, ResourceNode) add_resource_gain_to_current_resources( starting_node.resource_gain_on_collect( NodeContext( patches, initial_resources, game.resource_database, game.world_list, )), initial_resources, ) initial_game_state = game.initial_states.get("Default") if initial_game_state is not None: add_resource_gain_to_current_resources(initial_game_state, initial_resources) starting_state = State( initial_resources, (), None, starting_node, patches, None, StateGameData(game.resource_database, game.world_list, energy_per_tank, starting_energy)) # Being present with value 0 is troublesome since this dict is used for a simplify_requirements later on keys_to_remove = [ resource for resource, quantity in initial_resources.items() if quantity == 0 ] for resource in keys_to_remove: del initial_resources[resource] return starting_state
def test_sort_resource_requirement(blank_game_description): db = blank_game_description.resource_database node = blank_game_description.world_list.all_nodes[0] assert node is not None resources = [ NodeResourceInfo.from_node( node, NodeContext(None, None, db, blank_game_description.world_list)), SimpleResourceInfo(1, "Resource", "Resource", ResourceType.MISC), TrickResourceInfo(2, "Trick", "Trick", "Long Description"), ItemResourceInfo(3, "Item", "Item", 1), ] # Assert resources has an entry for every type of ResourceInfo assert {type(it) for it in resources} == set(ResourceInfo.__args__) assert len(resources) == len(ResourceInfo.__args__) requirements = [ResourceRequirement.simple(it) for it in resources] result = sorted(requirements) assert result == list(reversed(requirements))
def ctx(resources): return NodeContext(empty_patches, resources, db, empty_patches.game.world_list)
def is_collected(self, context: NodeContext) -> bool: return context.has_resource(self.resource(context))
def is_collected(self, context: NodeContext) -> bool: return context.has_resource(self.pickup_index)
def ctx(*args: ResourceInfo): resources = ResourceCollection.from_dict(db, {r: 1 for r in args}) return NodeContext(empty_patches, resources, db, node_provider)