Esempio n. 1
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),
    ]
Esempio n. 2
0
def test_requirement_as_set_2(database):
    def _req(name: str):
        id_req = ResourceRequirement.simple(database.get_item(name))
        return id_req

    req = RequirementAnd([
        Requirement.trivial(),
        _req("A"),
    ])
    assert req.as_set(database) == RequirementSet([
        RequirementList([_req("A")]),
    ])
Esempio n. 3
0
    def simplify(self, keep_comments: bool = False) -> Requirement:
        new_items = expand_items(self.items, RequirementOr,
                                 Requirement.impossible(), keep_comments)
        if Requirement.trivial() in new_items and mergeable_array(
                self, keep_comments):
            return Requirement.trivial()

        num_and_requirements = 0
        common_requirements = None
        for item in new_items:
            if isinstance(item, RequirementAnd) and mergeable_array(
                    item, keep_comments):
                num_and_requirements += 1
                if common_requirements is None:
                    common_requirements = item.items
                else:
                    common_requirements = [
                        common for common in common_requirements
                        if common in item.items
                    ]

        # Only extract the common requirements if there's more than 1 requirement
        if num_and_requirements >= 2 and common_requirements:
            simplified_items = []
            common_new_or = []

            for item in new_items:
                if isinstance(item, RequirementAnd) and mergeable_array(
                        item, keep_comments):
                    assert set(common_requirements) <= set(item.items)
                    simplified_condition = [
                        it for it in item.items
                        if it not in common_requirements
                    ]
                    if simplified_condition:
                        common_new_or.append(
                            RequirementAnd(simplified_condition
                                           ) if len(simplified_condition) > 1
                            else simplified_condition[0])
                else:
                    simplified_items.append(item)

            common_requirements.append(RequirementOr(common_new_or))
            simplified_items.append(RequirementAnd(common_requirements))
            final_items = simplified_items

        else:
            final_items = new_items

        if len(final_items) == 1 and mergeable_array(self, keep_comments):
            return final_items[0]

        return RequirementOr(final_items, comment=self.comment)
Esempio n. 4
0
    def configurable_node_assignment(
            self, configuration: DreadConfiguration, game: GameDescription,
            rng: Random) -> Iterator[NodeConfigurationAssociation]:
        result = []

        rsb = game.resource_database

        requirement_for_type = {
            "POWERBEAM": rsb.requirement_template["Shoot Beam"],
            "BOMB": rsb.requirement_template["Lay Bomb"],
            "MISSILE": rsb.requirement_template["Shoot Missile"],
            "SUPERMISSILE": rsb.requirement_template["Shoot Super Missile"],
            "POWERBOMB": rsb.requirement_template["Lay Power Bomb"],
            "SCREWATTACK": ResourceRequirement.simple(rsb.get_item("Screw")),
            "WEIGHT": Requirement.impossible(),
            "SPEEDBOOST": ResourceRequirement.simple(rsb.get_item("Speed")),
        }

        for node in game.world_list.iterate_nodes():
            if not isinstance(node, ConfigurableNode):
                continue

            result.append((game.world_list.identifier_for_node(node),
                           RequirementAnd([
                               requirement_for_type[block_type]
                               for block_type in node.extra["tile_types"]
                           ]).simplify()))

        return result
Esempio n. 5
0
    def requirement_to_leave(self, context: NodeContext) -> Requirement:
        items = []
        if self.scan_visor is not None:
            items.append(ResourceRequirement.simple(self.scan_visor))
        if self.required_translator is not None:
            items.append(ResourceRequirement.simple(self.required_translator))

        return RequirementAnd(items)
 def make_req(item_id: int):
     return RequirementAnd([
         ResourceRequirement.simple(
             ItemResourceInfo(0, "Scan Visor", "Scan", 1,
                              frozendict({"item_id": 9})), ),
         ResourceRequirement.simple(
             ItemResourceInfo(1, "Other", "Other", 1,
                              frozendict({"item_id": item_id})), ),
     ])
Esempio n. 7
0
def test_simplify_requirement_set_static(blank_game_description):
    db = blank_game_description.resource_database
    res_a, id_req_a = make_req_a(db)
    res_b, id_req_b = make_req_b(db)
    fd = ResourceCollection.from_dict

    the_set = RequirementOr([
        RequirementAnd([id_req_a]),
        RequirementAnd([id_req_b]),
    ])

    simple_1 = the_set.patch_requirements(fd(db, {res_a: 0, res_b: 0}), 1, db)
    simple_2 = the_set.patch_requirements(fd(db, {res_a: 0, res_b: 1}), 1, db)
    simple_3 = the_set.patch_requirements(fd(db, {res_a: 1, res_b: 1}), 1, db)

    assert simple_1.as_set(db).alternatives == frozenset()
    assert simple_2.as_set(db).alternatives == frozenset([RequirementList([])])
    assert simple_3.as_set(db).alternatives == frozenset([RequirementList([])])
Esempio n. 8
0
 def patch_requirements(self, resources: ResourceCollection,
                        database: ResourceDatabase) -> RequirementSet:
     from randovania.game_description.requirements.requirement_and import RequirementAnd
     from randovania.game_description.requirements.requirement_or import RequirementOr
     return RequirementOr(
         RequirementAnd(
             individual.patch_requirements(resources, 1, database)
             for individual in alternative.values())
         for alternative in self.alternatives).as_set(database)
Esempio n. 9
0
def test_requirement_and_str(database):
    def _req(name: str):
        id_req = ResourceRequirement.simple(database.get_item(name))
        return id_req

    req = RequirementAnd([
        _req("B"),
        _req("A"),
        _req("C"),
    ])
    assert str(req) == "(A ≥ 1 and B ≥ 1 and C ≥ 1)"
Esempio n. 10
0
    def _potential_nodes_from(
            self, node: Node) -> Iterator[Tuple[Node, RequirementSet]]:
        extra_requirement = _extra_requirement_for_node(
            self._game, self.node_context(), node)
        requirement_to_leave = node.requirement_to_leave(
            self._state.node_context())

        for target_node, requirement in self._game.world_list.potential_nodes_from(
                node, self.node_context()):
            if target_node is None:
                continue

            if requirement_to_leave != Requirement.trivial():
                requirement = RequirementAnd(
                    [requirement, requirement_to_leave])

            if extra_requirement is not None:
                requirement = RequirementAnd([requirement, extra_requirement])

            yield target_node, requirement.as_set(
                self._state.resource_database)
Esempio n. 11
0
def test_pretty_print_requirement_array_one_item(
        mock_print_requirement: MagicMock):
    mock_print_requirement.return_value = ["a", "b"]

    req = MagicMock()
    array = RequirementAnd([req])

    # Run
    result = list(pretty_print.pretty_print_requirement_array(array, 3))

    # Assert
    assert result == ["a", "b"]
    mock_print_requirement.assert_called_once_with(req, 3)
    def configurable_node_assignment(
            self, configuration: EchoesConfiguration, game: GameDescription,
            rng: Random) -> Iterator[NodeConfigurationAssociation]:
        """
        :param configuration:
        :param game:
        :param rng:
        :return:
        """

        all_choices = list(LayoutTranslatorRequirement)
        all_choices.remove(LayoutTranslatorRequirement.RANDOM)
        all_choices.remove(LayoutTranslatorRequirement.RANDOM_WITH_REMOVED)
        without_removed = copy.copy(all_choices)
        without_removed.remove(LayoutTranslatorRequirement.REMOVED)
        random_requirements = {
            LayoutTranslatorRequirement.RANDOM,
            LayoutTranslatorRequirement.RANDOM_WITH_REMOVED
        }

        result = []

        scan_visor = search.find_resource_info_with_long_name(
            game.resource_database.item, "Scan Visor")
        scan_visor_req = ResourceRequirement.simple(scan_visor)

        for node in game.world_list.iterate_nodes():
            if not isinstance(node, ConfigurableNode):
                continue

            identifier = game.world_list.identifier_for_node(node)
            requirement = configuration.translator_configuration.translator_requirement[
                identifier]
            if requirement in random_requirements:
                if rng is None:
                    raise MissingRng("Translator")
                requirement = rng.choice(
                    all_choices if requirement == LayoutTranslatorRequirement.
                    RANDOM_WITH_REMOVED else without_removed)

            translator = game.resource_database.get_by_type_and_index(
                ResourceType.ITEM, requirement.item_name)
            result.append((identifier,
                           RequirementAnd([
                               scan_visor_req,
                               ResourceRequirement.simple(translator),
                           ])))

        return result
Esempio n. 13
0
def test_pretty_print_requirement_array_combinable(
        mock_print_requirement: MagicMock, echoes_resource_database):
    mock_print_requirement.return_value = ["a", "b"]

    array = RequirementAnd([
        ResourceRequirement.simple(echoes_resource_database.item[0]),
        RequirementTemplate("Shoot Sunburst"),
    ])

    # Run
    result = list(pretty_print.pretty_print_requirement_array(array, 3))

    # Assert
    assert result == [(3, "Power Beam and Shoot Sunburst")]
    mock_print_requirement.assert_not_called()
Esempio n. 14
0
def _extra_requirement_for_node(game: GameDescription, context: NodeContext,
                                node: Node) -> Optional[Requirement]:
    extra_requirement = None

    if node.is_resource_node:
        assert isinstance(node, ResourceNode)
        dangerous_extra = [
            ResourceRequirement.simple(resource)
            for resource, quantity in node.resource_gain_on_collect(context)
            if resource in game.dangerous_resources
        ]
        if dangerous_extra:
            extra_requirement = RequirementAnd(dangerous_extra)

    return extra_requirement
Esempio n. 15
0
    def _lock_connection(self, context: NodeContext) -> typing.Optional[tuple[Node, Requirement]]:
        requirement = Requirement.trivial()

        forward_weakness = self.get_front_weakness(context)
        forward_lock = forward_weakness.lock

        if forward_lock is not None:
            requirement = self._get_lock_requirement(context, forward_weakness)

        back_weakness = self.get_back_weakness(context)
        back_lock = None
        if back_weakness is not None:
            back_lock = back_weakness.lock

            if back_lock is not None and back_lock.lock_type == DockLockType.FRONT_BLAST_BACK_BLAST:
                requirement = RequirementAnd([requirement, back_lock.requirement])

        if forward_lock is None and back_lock is None:
            return None

        return self.lock_node, requirement
def test_build_no_changes(skip_qtbot, echoes_resource_database):
    # Setup
    def mk_req(name: str):
        return ResourceRequirement.with_data(echoes_resource_database,
                                             ResourceType.ITEM, name, 1, False)

    requirement = RequirementOr([
        RequirementAnd([
            mk_req("Dark"),
            mk_req("Light"),
        ]),
        mk_req("Power"),
    ])

    # Run
    editor = connections_editor.ConnectionsEditor(None,
                                                  echoes_resource_database,
                                                  requirement)
    skip_qtbot.addWidget(editor)
    result = editor.final_requirement

    # Assert
    assert result == requirement
Esempio n. 17
0
    def state_for_current_configuration(self) -> Optional[State]:
        state = self._initial_state.copy()
        if self._actions:
            state.node = self._actions[-1]

        state.patches = state.patches.assign_elevators(
            (state.world_list.get_teleporter_node(teleporter),
             combo.currentData())
            for teleporter, combo in self._elevator_id_to_combo.items())

        for gate, item in self._translator_gate_to_combo.items():
            scan_visor = self.game_description.resource_database.get_item(
                "Scan")

            requirement: Optional[
                LayoutTranslatorRequirement] = item.currentData()
            if requirement is None:
                translator_req = Requirement.impossible()
            else:
                translator = self.game_description.resource_database.get_item(
                    requirement.item_name)
                translator_req = ResourceRequirement.simple(translator)

            state.patches.configurable_nodes[gate] = RequirementAnd([
                ResourceRequirement.simple(scan_visor),
                translator_req,
            ])

        for pickup, quantity in self._collected_pickups.items():
            for _ in range(quantity):
                add_pickup_to_state(state, pickup)

        for node in self._collected_nodes:
            state.resources.add_resource_gain(
                node.resource_gain_on_collect(state.node_context()))

        return state
Esempio n. 18
0
    def calculate_reach(cls,
                        logic: Logic,
                        initial_state: State) -> "ResolverReach":

        all_nodes = logic.game.world_list.all_nodes
        checked_nodes: Dict[int, int] = {}
        database = initial_state.resource_database
        context = initial_state.node_context()

        # Keys: nodes to check
        # Value: how much energy was available when visiting that node
        nodes_to_check: Dict[int, int] = {
            initial_state.node.node_index: initial_state.energy
        }

        reach_nodes: Dict[int, int] = {}
        requirements_by_node: Dict[int, Set[RequirementList]] = defaultdict(set)

        path_to_node: dict[int, list[int]] = {
            initial_state.node.node_index: [],
        }

        while nodes_to_check:
            node_index = next(iter(nodes_to_check))
            node = all_nodes[node_index]
            energy = nodes_to_check.pop(node_index)

            if node.heal:
                energy = initial_state.maximum_energy

            checked_nodes[node_index] = energy
            if node_index != initial_state.node.node_index:
                reach_nodes[node_index] = energy

            requirement_to_leave = node.requirement_to_leave(context)

            for target_node, requirement in logic.game.world_list.potential_nodes_from(node, context):
                target_node_index = target_node.node_index

                if checked_nodes.get(target_node_index, math.inf) <= energy or nodes_to_check.get(target_node_index,
                                                                                            math.inf) <= energy:
                    continue

                if requirement_to_leave != Requirement.trivial():
                    requirement = RequirementAnd([requirement, requirement_to_leave])

                # Check if the normal requirements to reach that node is satisfied
                satisfied = requirement.satisfied(initial_state.resources, energy, database)
                if satisfied:
                    # If it is, check if we additional requirements figured out by backtracking is satisfied
                    satisfied = logic.get_additional_requirements(node).satisfied(initial_state.resources,
                                                                                  energy,
                                                                                  initial_state.resource_database)

                if satisfied:
                    nodes_to_check[target_node_index] = energy - requirement.damage(initial_state.resources, database)
                    path_to_node[target_node_index] = list(path_to_node[node_index])
                    path_to_node[target_node_index].append(node_index)

                elif target_node:
                    # If we can't go to this node, store the reason in order to build the satisfiable requirements.
                    # Note we ignore the 'additional requirements' here because it'll be added on the end.
                    requirements_by_node[target_node_index].update(
                        requirement.as_set(initial_state.resource_database).alternatives)

        # Discard satisfiable requirements of nodes reachable by other means
        for node_index in set(reach_nodes.keys()).intersection(requirements_by_node.keys()):
            requirements_by_node.pop(node_index)

        if requirements_by_node:
            satisfiable_requirements = frozenset.union(
                *[RequirementSet(requirements).union(logic.get_additional_requirements(all_nodes[node_index])).alternatives
                  for node_index, requirements in requirements_by_node.items()])
        else:
            satisfiable_requirements = frozenset()

        return ResolverReach(reach_nodes, path_to_node,
                             satisfiable_requirements,
                             logic)
Esempio n. 19
0
 def trivial(cls) -> Requirement:
     # empty RequirementAnd.satisfied is True
     from randovania.game_description.requirements.requirement_and import RequirementAnd
     return RequirementAnd([])
Esempio n. 20
0
 def requirement_to_leave(self, context: NodeContext) -> Requirement:
     return RequirementAnd([
         self.is_unlocked,
         ResourceRequirement.simple(self.resource(context))
     ])
Esempio n. 21
0
class DockNode(Node):
    """
    Represents a connection to another area via something similar to a door and it's always to another DockNode.
    The dock weakness describes the types of door the game might have, which could be randomized separately from where
    the door leads to.

    This is the default way a node connects to another area, expected to be used in every area and it implies the
    areas are "physically" next to each other.

    TeleporterNode is expected to be used exceptionally, where it can be reasonable to list all of them in the
    UI for user selection (elevator rando, for example).
    """
    dock_type: DockType
    default_connection: NodeIdentifier
    default_dock_weakness: DockWeakness
    override_default_open_requirement: typing.Optional[Requirement]
    override_default_lock_requirement: typing.Optional[Requirement]
    lock_node: Node | None = dataclasses.field(init=False, hash=False, compare=False, default=None)
    cache_default_connection: int | None = dataclasses.field(init=False, hash=False, compare=False, default=None)

    def __repr__(self):
        return "DockNode({!r} -> {})".format(self.name, self.default_connection)

    def get_front_weakness(self, context: NodeContext) -> DockWeakness:
        return context.patches.get_dock_weakness_for(self)

    def get_back_weakness(self, context: NodeContext) -> typing.Optional[DockWeakness]:
        target_node = self.get_target_identifier(context)
        if isinstance(target_node, DockNode):
            return context.patches.get_dock_weakness_for(target_node)

        return None

    def _get_open_requirement(self, context: NodeContext, weakness: DockWeakness) -> Requirement:
        if weakness is self.default_dock_weakness and self.override_default_open_requirement is not None:
            return self.override_default_open_requirement
        else:
            return context.node_provider.open_requirement_for(weakness)

    def _get_lock_requirement(self, context: NodeContext, weakness: DockWeakness) -> Requirement:
        if weakness is self.default_dock_weakness and self.override_default_lock_requirement is not None:
            return self.override_default_lock_requirement
        else:
            return context.node_provider.lock_requirement_for(weakness)

    def _open_dock_connection(self, context: NodeContext, target_node: Node,
                              ) -> tuple[Node, Requirement]:

        forward_weakness = self.get_front_weakness(context)

        reqs: list[Requirement] = [self._get_open_requirement(context, forward_weakness)]

        # This dock has a lock, so require it
        if forward_weakness.lock is not None:
            reqs.append(ResourceRequirement.simple(NodeResourceInfo.from_node(self, context)))

        # The other dock has a lock, so require it
        if (other_lock_req := _requirement_from_back(context, target_node)) is not None:
            reqs.append(other_lock_req)

        requirement = RequirementAnd(reqs).simplify() if len(reqs) != 1 else reqs[0]
        return target_node, requirement
Esempio n. 22
0
def test_simplified_requirement(database):
    def _req(name: str):
        id_req = ResourceRequirement.simple(database.get_item(name))
        return id_req

    test_parameters = [
        (
            RequirementOr([
                RequirementAnd([
                    _req("A"),
                ]),
            ]),
            _req("A"),
        ), (
            RequirementAnd([
                RequirementOr([
                    _req("A"),
                ]),
            ]),
            _req("A"),
        ),
        (
            RequirementAnd([
                RequirementOr([_req("B"), Requirement.trivial()]),
                RequirementAnd([
                    Requirement.trivial(),
                    _req("A"),
                ]),
            ]),
            _req("A"),
        ),
        (
            RequirementOr([
                _req("A"),
                RequirementAnd([_req("B"), _req("C")]),
                RequirementAnd([_req("B"), _req("D")]),
            ]),
            RequirementOr([
                _req("A"),
                RequirementAnd([
                    _req("B"),
                    RequirementOr([
                        _req("C"),
                        _req("D"),
                    ]),
                ]),
            ]),
        ),
        (
            RequirementOr([
                RequirementAnd([_req("B"), _req("C")]),
                RequirementAnd([_req("B"), _req("D")]),
            ]),
            RequirementAnd([
                _req("B"),
                RequirementOr([
                    _req("C"),
                    _req("D"),
                ]),
            ]),
        ), (
            RequirementOr([
                _req("A"),
                _req("A"),
            ]),
            _req("A"),
        ), (
            RequirementAnd([
                _req("A"),
                _req("A"),
            ]),
            _req("A"),
        ),
        (
            RequirementOr([
                RequirementAnd([
                    _req("A"),
                    RequirementOr([_req("A"),
                                   RequirementOr([_req("A")])])
                ]),
                RequirementAnd([
                    _req("A"),
                    RequirementOr([
                        _req("A"),
                        RequirementOr([]),
                    ]),
                ]),
            ]),
            _req("A"),
        ),
        (
            RequirementOr([
                RequirementAnd([
                    _req("A"),
                    RequirementOr([_req("A"),
                                   RequirementOr([_req("A")])])
                ]),
                RequirementAnd([
                    _req("A"),
                    RequirementOr([
                        _req("A"),
                        RequirementOr([_req("A")]),
                    ])
                ])
            ]),
            _req("A"),
        )
    ]

    for original, expected in test_parameters:
        simplified = original.simplify()
        assert simplified == expected
        assert simplified.as_set(database) == expected.as_set(database)