Example #1
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
Example #2
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)
Example #3
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),
    ]
 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})), ),
     ])
def test_set_as_str_things(echoes_resource_database):
    item = echoes_resource_database.get_item_by_name

    req_set = RequirementSet([
        RequirementList([
            ResourceRequirement.simple(item("Screw Attack")),
            ResourceRequirement.simple(item("Space Jump Boots")),
        ]),
        RequirementList([
            ResourceRequirement.simple(item("Power Bomb")),
        ]),
    ])

    assert req_set.as_str == "(Power Bomb ≥ 1) or (Screw Attack ≥ 1, Space Jump Boots ≥ 1)"
    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
Example #7
0
def _requirement_from_back(context: NodeContext, target_node: Node) -> typing.Optional[ResourceRequirement]:
    if isinstance(target_node, DockNode):
        weak = context.patches.get_dock_weakness_for(target_node)
        if weak.lock is not None:
            return ResourceRequirement.simple(NodeResourceInfo.from_node(target_node, context))

    return None
Example #8
0
def read_resource_requirement(
        data: Dict,
        resource_database: ResourceDatabase) -> ResourceRequirement:
    data = data["data"]
    return ResourceRequirement.with_data(resource_database,
                                         ResourceType.from_str(data["type"]),
                                         data["name"], data["amount"],
                                         data["negate"])
def test_requirement_list_constructor(echoes_resource_database):
    def item(name):
        return search.find_resource_info_with_long_name(
            echoes_resource_database.item, name)

    req_list = RequirementList([
        ResourceRequirement.simple(item("Dark Visor")),
        ResourceRequirement.create(item("Missile"), 5, False),
        ResourceRequirement.simple(item("Seeker Launcher")),
    ])
    extract = [(req.resource.long_name, req.amount)
               for req in req_list.values()]

    assert sorted(extract) == [
        ("Dark Visor", 1),
        ("Missile", 5),
        ("Seeker Launcher", 1),
    ]
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_set_hash(echoes_resource_database):
    req_set_a = RequirementSet([
        RequirementList([
            ResourceRequirement.simple(
                echoes_resource_database.get_item_by_name("Power Bomb")),
        ]),
    ])
    req_set_b = RequirementSet([
        RequirementList([
            ResourceRequirement.simple(
                echoes_resource_database.get_item_by_name("Power Bomb")),
        ]),
    ])

    assert req_set_a == req_set_b
    assert req_set_a is not req_set_b

    hash_a = hash(req_set_a)
    hash_b = hash(req_set_b)
    assert hash_a == hash_b

    assert hash_a == req_set_a._cached_hash
def test_simple_echoes_damage(echoes_resource_database):
    db = echoes_resource_database
    req = ResourceRequirement.create(
        db.get_by_type_and_index(ResourceType.DAMAGE, "DarkWorld1"),
        50,
        False,
    )
    d_suit = db.get_item_by_name("Dark Suit")
    l_suit = db.get_item_by_name("Light Suit")

    assert req.damage(_empty_col(), db) == 50
    assert req.damage(_col_for(db, d_suit), db) == 11
    assert req.damage(_col_for(db, l_suit), db) == 0
Example #13
0
    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)
Example #14
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
Example #15
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
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()
def test_list_dangerous_resources(database, input_data, output_data):
    # setup
    req_list = RequirementList(
        (ResourceRequirement.create(database.resource_by_index[item[0]], 1,
                                    item[1]) for item in input_data))

    expected_result = {
        database.resource_by_index[item]
        for item in output_data
    }

    # run
    result = set(req_list.dangerous_resources)

    # assert
    assert result == expected_result
Example #18
0
def _unsatisfied_item_requirements_in_list(
        alternative: RequirementList, state: State,
        uncollected_resources: set[ResourceInfo]):
    possible = True
    items = []
    damage = []

    for individual in alternative.values():
        if individual.negate:
            possible = False
            break

        if individual.resource.resource_type == ResourceType.DAMAGE:
            damage.append(individual)
            continue

        if individual.satisfied(state.resources, state.energy,
                                state.resource_database):
            continue

        if individual.resource.resource_type != ResourceType.ITEM:
            if individual.resource not in uncollected_resources:
                possible = False
                break

        items.append(individual)

    if not possible:
        return

    sum_damage = sum(
        req.damage(state.resources, state.resource_database) for req in damage)
    if state.energy < sum_damage:
        tank_count = (sum_damage -
                      state.energy) // state.game_data.energy_per_tank
        yield items + [
            ResourceRequirement.create(state.resource_database.energy_tank,
                                       tank_count + 1, False)
        ]
        # FIXME: get the required items for reductions (aka suits)
    else:
        yield items
Example #19
0
    def current_requirement(self) -> ResourceRequirement:
        resource_type = self.resource_type

        # Quantity
        if resource_type == ResourceType.TRICK:
            quantity: int = self.amount_combo.currentData()
        elif resource_type == ResourceType.EVENT:
            quantity = 1
        else:
            quantity = int(self.amount_edit.text())

        # Negate flag
        if resource_type == ResourceType.ITEM:
            negate: bool = self.negate_combo.currentData()
        elif resource_type == ResourceType.EVENT:
            negate = self.negate_check.isChecked()
        else:
            negate = False

        return ResourceRequirement.create(
            self.resource_name_combo.currentData(), quantity, negate)
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))
Example #21
0
    def patch_resource_database(self, db: ResourceDatabase, configuration: BaseConfiguration) -> ResourceDatabase:
        base_damage_reduction = db.base_damage_reduction
        damage_reductions = copy.copy(db.damage_reductions)
        requirement_template = copy.copy(db.requirement_template)

        suits = [db.get_item_by_name("Varia Suit")]
        if configuration.heat_protection_only_varia:
            requirement_template["Heat-Resisting Suit"] = ResourceRequirement.simple(db.get_item_by_name("Varia Suit"))
        else:
            suits.extend([db.get_item_by_name("Gravity Suit"), db.get_item_by_name("Phazon Suit")])

        reductions = [DamageReduction(None, configuration.heat_damage / 10.0)]
        reductions.extend([DamageReduction(suit, 0) for suit in suits])
        damage_reductions[db.get_by_type_and_index(ResourceType.DAMAGE, "HeatDamage1")] = reductions

        if configuration.progressive_damage_reduction:
            base_damage_reduction = self.prime1_progressive_damage_reduction
        else:
            base_damage_reduction = self.prime1_absolute_damage_reduction

        return dataclasses.replace(db, damage_reductions=damage_reductions, base_damage_reduction=base_damage_reduction,
                                   requirement_template=requirement_template)
def test_requirement_set_constructor(echoes_resource_database):
    item = echoes_resource_database.get_item_by_name

    req_set = RequirementSet([
        RequirementList([
            ResourceRequirement.simple(item("Dark Visor")),
            ResourceRequirement.create(item("Missile"), 5, False),
            ResourceRequirement.simple(item("Seeker Launcher")),
        ]),
        RequirementList([
            ResourceRequirement.simple(item("Screw Attack")),
            ResourceRequirement.simple(item("Space Jump Boots")),
        ]),
        RequirementList([
            ResourceRequirement.simple(item("Power Bomb")),
            ResourceRequirement.simple(item("Boost Ball")),
        ]),
    ])
    extract = [
        sorted((req.resource.long_name, req.amount)
               for req in req_list.values())
        for req_list in req_set.alternatives
    ]

    assert sorted(extract) == [
        [
            ("Boost Ball", 1),
            ("Power Bomb", 1),
        ],
        [
            ("Dark Visor", 1),
            ("Missile", 5),
            ("Seeker Launcher", 1),
        ],
        [
            ("Screw Attack", 1),
            ("Space Jump Boots", 1),
        ],
    ]
 def _req(name: str):
     id_req = ResourceRequirement.simple(database.get_item(name))
     return id_req
def make_req_c(db: ResourceDatabase):
    res = db.item[2]
    return res, ResourceRequirement.simple(res)
 def mk_req(name: str):
     return ResourceRequirement.with_data(echoes_resource_database,
                                          ResourceType.ITEM, name, 1, False)
Example #26
0
def _patches_with_data(request, echoes_game_description, echoes_game_patches,
                       echoes_item_database):
    game = echoes_game_description
    db = game.resource_database

    data = {
        "game": echoes_game_description.game.value,
        "starting_location": "Temple Grounds/Landing Site",
        "starting_items": {},
        "teleporters": {
            "Temple Grounds/Temple Transport C/Elevator to Great Temple - Temple Transport C":
            "Great Temple/Temple Transport C",
            "Temple Grounds/Transport to Agon Wastes/Elevator to Agon Wastes - Transport to Temple Grounds":
            "Agon Wastes/Transport to Temple Grounds",
            "Temple Grounds/Transport to Torvus Bog/Elevator to Torvus Bog - Transport to Temple Grounds":
            "Torvus Bog/Transport to Temple Grounds",
            "Temple Grounds/Temple Transport B/Elevator to Great Temple - Temple Transport B":
            "Great Temple/Temple Transport B",
            "Temple Grounds/Transport to Sanctuary Fortress/Elevator to Sanctuary Fortress - Transport to Temple Grounds":
            "Sanctuary Fortress/Transport to Temple Grounds",
            "Temple Grounds/Temple Transport A/Elevator to Great Temple - Temple Transport A":
            "Great Temple/Temple Transport A",
            "Great Temple/Temple Transport A/Elevator to Temple Grounds - Temple Transport A":
            "Temple Grounds/Temple Transport A",
            "Great Temple/Temple Transport C/Elevator to Temple Grounds - Temple Transport C":
            "Temple Grounds/Temple Transport C",
            "Great Temple/Temple Transport B/Elevator to Temple Grounds - Temple Transport B":
            "Temple Grounds/Temple Transport B",
            "Temple Grounds/Sky Temple Gateway/Teleport to Great Temple - Sky Temple Energy Controller":
            "Great Temple/Sky Temple Energy Controller",
            "Great Temple/Sky Temple Energy Controller/Teleport to Temple Grounds - Sky Temple Gateway":
            "Temple Grounds/Sky Temple Gateway",
            "Agon Wastes/Transport to Temple Grounds/Elevator to Temple Grounds - Transport to Agon Wastes":
            "Temple Grounds/Transport to Agon Wastes",
            "Agon Wastes/Transport to Torvus Bog/Elevator to Torvus Bog - Transport to Agon Wastes":
            "Torvus Bog/Transport to Agon Wastes",
            "Agon Wastes/Transport to Sanctuary Fortress/Elevator to Sanctuary Fortress - Transport to Agon Wastes":
            "Sanctuary Fortress/Transport to Agon Wastes",
            "Torvus Bog/Transport to Temple Grounds/Elevator to Temple Grounds - Transport to Torvus Bog":
            "Temple Grounds/Transport to Torvus Bog",
            "Torvus Bog/Transport to Agon Wastes/Elevator to Agon Wastes - Transport to Torvus Bog":
            "Agon Wastes/Transport to Torvus Bog",
            "Torvus Bog/Transport to Sanctuary Fortress/Elevator to Sanctuary Fortress - Transport to Torvus Bog":
            "Sanctuary Fortress/Transport to Torvus Bog",
            "Sanctuary Fortress/Transport to Temple Grounds/Elevator to Temple Grounds - Transport to Sanctuary Fortress":
            "Temple Grounds/Transport to Sanctuary Fortress",
            "Sanctuary Fortress/Transport to Agon Wastes/Elevator to Agon Wastes - Transport to Sanctuary Fortress":
            "Agon Wastes/Transport to Sanctuary Fortress",
            "Sanctuary Fortress/Transport to Torvus Bog/Elevator to Torvus Bog - Transport to Sanctuary Fortress":
            "Torvus Bog/Transport to Sanctuary Fortress",
            "Sanctuary Fortress/Aerie/Elevator to Sanctuary Fortress - Aerie Transport Station":
            "Sanctuary Fortress/Aerie Transport Station",
            "Sanctuary Fortress/Aerie Transport Station/Elevator to Sanctuary Fortress - Aerie":
            "Sanctuary Fortress/Aerie",
        },
        "dock_weakness": {},
        "configurable_nodes": {},
        "locations": {},
        "hints": {}
    }
    patches = dataclasses.replace(echoes_game_patches, player_index=0)

    locations = collections.defaultdict(dict)
    for world, area, node in game.world_list.all_worlds_areas_nodes:
        if node.is_resource_node and isinstance(node, PickupNode):
            world_name = world.dark_name if area.in_dark_aether else world.name
            locations[world_name][game.world_list.node_name(
                node)] = game_patches_serializer._ETM_NAME

    data["locations"] = {
        world: {area: item
                for area, item in sorted(locations[world].items())}
        for world in sorted(locations.keys())
    }

    if request.param.get("starting_item"):
        item_name = request.param.get("starting_item")
        patches = patches.assign_extra_initial_items([
            (db.get_item_by_name(item_name), 1),
        ])
        data["starting_items"][item_name] = 1

    if request.param.get("elevator"):
        teleporter: NodeIdentifier = request.param.get("elevator")
        patches = patches.assign_elevators([
            (game.world_list.get_teleporter_node(teleporter),
             game.starting_location),
        ])
        data["teleporters"][
            teleporter.as_string] = "Temple Grounds/Landing Site"

    if request.param.get("configurable_nodes"):
        gates = []
        for identifier, translator in request.param.get("configurable_nodes"):
            requirement = ResourceRequirement.simple(db.get_item(translator))
            gates.append((NodeIdentifier.from_string(identifier), requirement))
            data["configurable_nodes"][
                identifier] = data_writer.write_requirement(requirement)

        patches = patches.assign_node_configuration(gates)

    if request.param.get("pickup"):
        pickup_name = request.param.get("pickup")
        pickup = pickup_creator.create_major_item(
            echoes_item_database.major_items[pickup_name], MajorItemState(),
            True, game.resource_database, None, False)

        patches = patches.assign_new_pickups([(PickupIndex(5),
                                               PickupTarget(pickup, 0))])
        data["locations"]["Temple Grounds"][
            'Transport to Agon Wastes/Pickup (Missile)'] = pickup_name

    if request.param.get("hint"):
        identifier, hint = request.param.get("hint")
        patches = patches.assign_hint(NodeIdentifier.from_string(identifier),
                                      Hint.from_json(hint))
        data["hints"][identifier] = hint

    return data, patches
Example #27
0
 def requirement_to_leave(self, context: NodeContext) -> Requirement:
     if context.current_resources.add_self_as_requirement_to_resources:
         return ResourceRequirement.simple(self.event)
     else:
         return Requirement.trivial()
Example #28
0
def _create_default_resource_requirement(
        resource_database: ResourceDatabase) -> ResourceRequirement:
    return ResourceRequirement.simple(
        resource_database.get_by_type(ResourceType.ITEM)[0], )
 def _make_req(name: str):
     req = database.get_item(name)
     id_req = ResourceRequirement.simple(req)
     return req, id_req
 def current_individual(self) -> ResourceRequirement:
     return ResourceRequirement.create(
         self.resource_name_combo.currentData(),
         int(self.amount_edit.text()), self.negate_combo.currentData())