コード例 #1
0
def test_gate_assignment_for_configuration_all_random(echoes_game_description, default_echoes_configuration):
    # Setup
    patches_factory = echoes_game_description.game.generator.base_patches_factory
    scan_visor = find_resource_info_with_long_name(echoes_game_description.resource_database.item, "Scan Visor")
    violet = find_resource_info_with_long_name(echoes_game_description.resource_database.item, "Violet Translator")
    emerald = find_resource_info_with_long_name(echoes_game_description.resource_database.item, "Emerald Translator")

    translator_configuration = default_echoes_configuration.translator_configuration
    configuration = dataclasses.replace(
        default_echoes_configuration,
        translator_configuration=translator_configuration.with_full_random(),
    )

    requirements = [
        RequirementAnd([
            ResourceRequirement(scan_visor, 1, False),
            ResourceRequirement(emerald, 1, False),
        ]),
        RequirementAnd([
            ResourceRequirement(scan_visor, 1, False),
            ResourceRequirement(violet, 1, False),
        ])
    ]
    requirements = requirements * len(translator_configuration.translator_requirement)

    choices = [LayoutTranslatorRequirement.EMERALD, LayoutTranslatorRequirement.VIOLET]
    rng = MagicMock()
    rng.choice.side_effect = choices * len(translator_configuration.translator_requirement)

    # Run
    results = patches_factory.configurable_node_assignment(
        configuration, echoes_game_description, rng)

    # Assert
    assert list(results.values()) == requirements[:len(translator_configuration.translator_requirement)]
コード例 #2
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),
    ]
コード例 #3
0
    def configurable_node_assignment(self, configuration: DreadConfiguration, game: GameDescription,
                                     rng: Random) -> NodeConfigurationAssignment:
        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(rsb.get_item("Screw"), 1, False),
            "WEIGHT": Requirement.impossible(),
            "SPEEDBOOST": ResourceRequirement(rsb.get_item("Speed"), 1, False),
        }

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

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

        return result
コード例 #4
0
ファイル: data_reader.py プロジェクト: 00mjk/randovania
def read_requirement_and(data: Dict,
                         resource_database: ResourceDatabase,
                         ) -> RequirementAnd:
    return RequirementAnd([
        read_requirement(item, resource_database)
        for item in data["data"]
    ])
コード例 #5
0
def test_gate_assignment_for_configuration_all_emerald(echoes_game_description, default_echoes_configuration):
    # Setup
    patches_factory = echoes_game_description.game.generator.base_patches_factory
    scan_visor = find_resource_info_with_long_name(echoes_game_description.resource_database.item, "Scan Visor")
    emerald = find_resource_info_with_long_name(echoes_game_description.resource_database.item, "Emerald Translator")

    translator_configuration = default_echoes_configuration.translator_configuration
    configuration = dataclasses.replace(
        default_echoes_configuration,
        translator_configuration=dataclasses.replace(
            translator_configuration,
            translator_requirement={
                key: LayoutTranslatorRequirement.EMERALD
                for key in translator_configuration.translator_requirement.keys()
            }
        )
    )

    rng = MagicMock()

    # Run
    results = patches_factory.configurable_node_assignment(
        configuration, echoes_game_description, rng)

    # Assert
    assert list(results.values()) == [
        RequirementAnd([
            ResourceRequirement(scan_visor, 1, False),
            ResourceRequirement(emerald, 1, False),
        ])
    ] * len(translator_configuration.translator_requirement)
コード例 #6
0
ファイル: node.py プロジェクト: gollop/randovania
 def requirement_to_leave(
         self, patches: GamePatches,
         current_resources: CurrentResources) -> Requirement:
     return RequirementAnd([
         ResourceRequirement(patches.translator_gates[self.gate], 1, False),
         ResourceRequirement(self.scan_visor, 1, False),
     ])
コード例 #7
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(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

        lock_node = context.node_provider.node_by_identifier(
            self.get_lock_node_identifier(context))
        return lock_node, requirement
コード例 #8
0
def test_requirement_and_str():
    req = RequirementAnd([
        _req("B"),
        _req("A"),
        _req("C"),
    ])
    assert str(req) == "(A ≥ 1 and B ≥ 1 and C ≥ 1)"
コード例 #9
0
    def _potential_nodes_from(self, node: Node) -> Iterator[Tuple[Node, Requirement, bool]]:
        extra_requirement = _extra_requirement_for_node(self._game, node)
        requirement_to_leave = node.requirement_to_leave(self._state.patches, self._state.resources)

        for target_node, requirement in self._game.world_list.potential_nodes_from(node, self.state.patches):
            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])

            satisfied = requirement.satisfied(self._state.resources, self._state.energy)
            yield target_node, requirement, satisfied
コード例 #10
0
def test_simplify_requirement_set_static():
    res_a, id_req_a = make_req_a()
    res_b, id_req_b = make_req_b()

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

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

    assert simple_1.as_set.alternatives == frozenset()
    assert simple_2.as_set.alternatives == frozenset([RequirementList([])])
    assert simple_3.as_set.alternatives == frozenset([RequirementList([])])
コード例 #11
0
def test_requirement_as_set_2():
    req = RequirementAnd([
        Requirement.trivial(),
        _req("A"),
    ])
    assert req.as_set(None) == RequirementSet([
        RequirementList([_req("A")]),
    ])
コード例 #12
0
    def requirement_to_leave(self, context: NodeContext) -> Requirement:
        items = []
        if self.scan_visor is not None:
            items.append(ResourceRequirement(self.scan_visor, 1, False))
        if self.required_translator is not None:
            items.append(
                ResourceRequirement(self.required_translator, 1, False))

        return RequirementAnd(items)
コード例 #13
0
ファイル: node.py プロジェクト: gollop/randovania
    def requirement_to_leave(
            self, patches: GamePatches,
            current_resources: CurrentResources) -> Requirement:
        items = [ResourceRequirement(self.scan_visor, 1, False)]
        if self.required_translator is not None:
            items.append(
                ResourceRequirement(self.required_translator, 1, False))

        return RequirementAnd(items)
コード例 #14
0
 def make_req(item_id: int):
     return RequirementAnd([
         ResourceRequirement(
             ItemResourceInfo("Scan Visor", "Scan", 1, frozendict({"item_id": 9})), 1, False,
         ),
         ResourceRequirement(
             ItemResourceInfo("Other", "Other", 1, frozendict({"item_id": item_id})), 1, False,
         ),
     ])
コード例 #15
0
def test_requirement_as_set_5():
    req = RequirementAnd([
        _req("A"),
        _req("B"),
        _req("C"),
    ])
    assert req.as_set(None) == RequirementSet([
        RequirementList([_req("A"), _req("B"), _req("C")]),
    ])
コード例 #16
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)
コード例 #17
0
def test_requirement_as_set_1():
    req = RequirementAnd([
        _req("A"),
        RequirementOr([_req("B"), _req("C")]),
        RequirementOr([_req("D"), _req("E")]),
    ])
    assert req.as_set == RequirementSet([
        RequirementList([_req("A"), _req("B"), _req("D")]),
        RequirementList([_req("A"), _req("B"), _req("E")]),
        RequirementList([_req("A"), _req("C"), _req("D")]),
        RequirementList([_req("A"), _req("C"), _req("E")]),
    ])
コード例 #18
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)
コード例 #19
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(echoes_resource_database.item[0], 1, False),
        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()
コード例 #20
0
    def configurable_node_assignment(
            self, configuration: EchoesConfiguration, game: GameDescription,
            rng: Random) -> NodeConfigurationAssignment:
        """
        :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(scan_visor, 1, False)

        for node in game.world_list.all_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[identifier] = RequirementAnd([
                scan_visor_req,
                ResourceRequirement(translator, 1, False),
            ])

        return result
コード例 #21
0
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
コード例 #22
0
def test_build_no_changes(skip_qtbot, echoes_resource_database):
    # Setup
    def mk_req(index: int):
        return ResourceRequirement.with_data(echoes_resource_database,
                                             ResourceType.ITEM, index, 1,
                                             False)

    requirement = RequirementOr([
        RequirementAnd([
            mk_req(1),
            mk_req(2),
        ]),
        mk_req(0),
    ])

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

    # Assert
    assert result == requirement
コード例 #23
0
    def state_for_current_configuration(self) -> Optional[State]:
        state = self._initial_state.copy()
        if self._actions:
            state.node = self._actions[-1]

        for teleporter, combo in self._elevator_id_to_combo.items():
            state.patches.elevator_connection[teleporter] = combo.currentData()

        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(translator, 1, False)

            state.patches.configurable_nodes[gate] = RequirementAnd([
                ResourceRequirement(scan_visor, 1, False),
                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:
            add_resource_gain_to_current_resources(
                node.resource_gain_on_collect(state.node_context()),
                state.resources)

        return state
コード例 #24
0
    def calculate_reach(cls,
                        logic: Logic,
                        initial_state: State) -> "ResolverReach":

        checked_nodes: Dict[Node, int] = {}

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

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

        path_to_node: Dict[Node, Tuple[Node, ...]] = {}
        path_to_node[initial_state.node] = tuple()

        while nodes_to_check:
            node = next(iter(nodes_to_check))
            energy = nodes_to_check.pop(node)

            if node.heal:
                energy = initial_state.maximum_energy

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

            requirement_to_leave = node.requirement_to_leave(initial_state.patches, initial_state.resources)

            for target_node, requirement in logic.game.world_list.potential_nodes_from(node, initial_state.patches):
                if target_node is None:
                    continue

                if checked_nodes.get(target_node, math.inf) <= energy or nodes_to_check.get(target_node,
                                                                                            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)
                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)

                if satisfied:
                    nodes_to_check[target_node] = energy - requirement.damage(initial_state.resources)
                    path_to_node[target_node] = path_to_node[node] + (node,)

                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].update(requirement.as_set.alternatives)

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

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

        return ResolverReach(reach_nodes, path_to_node,
                             satisfiable_requirements,
                             logic)
コード例 #25
0
 def requirement_to_leave(self, context: NodeContext) -> Requirement:
     return RequirementAnd([self.is_unlocked, ResourceRequirement(self.resource(context), 1, False)])
コード例 #26
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]

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

    def get_lock_node_identifier_from_identifier(
            self, self_identifier: NodeIdentifier):
        return dataclasses.replace(
            self_identifier,
            node_name=f"Lock - {self.name}",
        )

    def get_lock_node_identifier(self, context: NodeContext) -> NodeIdentifier:
        return self.get_lock_node_identifier_from_identifier(
            context.node_provider.identifier_for_node(self), )

    def get_front_weakness(self, context: NodeContext) -> DockWeakness:
        self_identifier = context.node_provider.identifier_for_node(self)
        return context.patches.dock_weakness.get(self_identifier,
                                                 self.default_dock_weakness)

    def get_back_weakness(
            self, context: NodeContext) -> typing.Optional[DockWeakness]:
        target_identifier = self.get_target_identifier(context)
        if target_identifier is None:
            return None

        target_node = context.node_provider.node_by_identifier(
            target_identifier)
        if isinstance(target_node, DockNode):
            return context.patches.dock_weakness.get(
                target_identifier, target_node.default_dock_weakness)

        return None

    def _get_open_requirement(self, 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 weakness.requirement

    def _get_lock_requirement(self, 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 weakness.lock.requirement

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

        forward_weakness = self.get_front_weakness(context)

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

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

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

        target_node = context.node_provider.node_by_identifier(
            target_identifier)
        requirement = RequirementAnd(
            reqs).simplify() if len(reqs) != 1 else reqs[0]
        return target_node, requirement
コード例 #27
0

def test_trivial_requirement_damage():
    assert Requirement.trivial().damage({}, None) == 0


def test_trivial_requirement_str():
    assert str(Requirement.trivial()) == "Trivial"


@pytest.mark.parametrize(
    ["original", "expected"],
    [(
        RequirementOr([
            RequirementAnd([
                _req("A"),
            ]),
        ]),
        _req("A"),
    ), (
        RequirementAnd([
            RequirementOr([
                _req("A"),
            ]),
        ]),
        _req("A"),
    ),
     (
         RequirementAnd([
             RequirementOr([_req("B"), Requirement.trivial()]),
             RequirementAnd([
コード例 #28
0
ファイル: node.py プロジェクト: gollop/randovania
 def requirement_to_leave(
         self, patches: GamePatches,
         current_resources: CurrentResources) -> Requirement:
     return RequirementAnd(
         [self.is_unlocked,
          ResourceRequirement(self.resource(), 1, False)])