예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
    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
예제 #4
0
    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
예제 #5
0
 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)
     )
예제 #6
0
 def node_context(self) -> NodeContext:
     return NodeContext(
         self.patches,
         self.resources,
         self.resource_database,
         self.game_data.world_list,
     )
예제 #7
0
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
    ]
예제 #8
0
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),
    ]
예제 #9
0
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)
예제 #10
0
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)
예제 #11
0
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}
예제 #12
0
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),
    ]
예제 #13
0
    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
예제 #14
0
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))
예제 #15
0
 def ctx(resources):
     return NodeContext(empty_patches, resources, db, empty_patches.game.world_list)
예제 #16
0
 def is_collected(self, context: NodeContext) -> bool:
     return context.has_resource(self.resource(context))
예제 #17
0
 def is_collected(self, context: NodeContext) -> bool:
     return context.has_resource(self.pickup_index)
예제 #18
0
 def ctx(*args: ResourceInfo):
     resources = ResourceCollection.from_dict(db, {r: 1 for r in args})
     return NodeContext(empty_patches, resources, db, node_provider)