Rule Builder: Make region.connect and add_event support rule builder (#5933)

* make region.connect and add_event support rule builder

* fix test

* oops fix

* update tests and typing

* rm unused
This commit is contained in:
Ian Robinson
2026-02-18 14:57:05 -05:00
committed by GitHub
parent fadcfbdfea
commit 8b91f9ff72
2 changed files with 24 additions and 15 deletions

View File

@@ -22,6 +22,7 @@ import Utils
if TYPE_CHECKING: if TYPE_CHECKING:
from entrance_rando import ERPlacementState from entrance_rando import ERPlacementState
from rule_builder.rules import Rule
from worlds import AutoWorld from worlds import AutoWorld
@@ -1368,7 +1369,7 @@ class Region:
self, self,
location_name: str, location_name: str,
item_name: str | None = None, item_name: str | None = None,
rule: CollectionRule | None = None, rule: CollectionRule | Rule[Any] | None = None,
location_type: type[Location] | None = None, location_type: type[Location] | None = None,
item_type: type[Item] | None = None, item_type: type[Item] | None = None,
show_in_spoiler: bool = True, show_in_spoiler: bool = True,
@@ -1396,7 +1397,7 @@ class Region:
event_location = location_type(self.player, location_name, None, self) event_location = location_type(self.player, location_name, None, self)
event_location.show_in_spoiler = show_in_spoiler event_location.show_in_spoiler = show_in_spoiler
if rule is not None: if rule is not None:
event_location.access_rule = rule self.multiworld.worlds[self.player].set_rule(event_location, rule)
event_item = item_type(item_name, ItemClassification.progression, None, self.player) event_item = item_type(item_name, ItemClassification.progression, None, self.player)
@@ -1407,7 +1408,7 @@ class Region:
return event_item return event_item
def connect(self, connecting_region: Region, name: Optional[str] = None, def connect(self, connecting_region: Region, name: Optional[str] = None,
rule: Optional[CollectionRule] = None) -> Entrance: rule: Optional[CollectionRule | Rule[Any]] = None) -> Entrance:
""" """
Connects this Region to another Region, placing the provided rule on the connection. Connects this Region to another Region, placing the provided rule on the connection.
@@ -1415,8 +1416,8 @@ class Region:
:param name: name of the connection being created :param name: name of the connection being created
:param rule: callable to determine access of this connection to go from self to the exiting_region""" :param rule: callable to determine access of this connection to go from self to the exiting_region"""
exit_ = self.create_exit(name if name else f"{self.name} -> {connecting_region.name}") exit_ = self.create_exit(name if name else f"{self.name} -> {connecting_region.name}")
if rule: if rule is not None:
exit_.access_rule = rule self.multiworld.worlds[self.player].set_rule(exit_, rule)
exit_.connect(connecting_region) exit_.connect(connecting_region)
return exit_ return exit_
@@ -1441,7 +1442,7 @@ class Region:
return entrance return entrance
def add_exits(self, exits: Iterable[str] | Mapping[str, str | None], def add_exits(self, exits: Iterable[str] | Mapping[str, str | None],
rules: Mapping[str, CollectionRule] | None = None) -> List[Entrance]: rules: Mapping[str, CollectionRule | Rule[Any]] | None = None) -> List[Entrance]:
""" """
Connects current region to regions in exit dictionary. Passed region names must exist first. Connects current region to regions in exit dictionary. Passed region names must exist first.

View File

@@ -1,9 +1,11 @@
import unittest import unittest
from typing import Callable, Dict, Optional from typing import Any, Dict, Optional
from typing_extensions import override from typing_extensions import override
from BaseClasses import CollectionState, MultiWorld, Region from BaseClasses import CollectionRule, MultiWorld, Region
from rule_builder.rules import Has, Rule
from test.general import TestWorld
class TestHelpers(unittest.TestCase): class TestHelpers(unittest.TestCase):
@@ -16,6 +18,7 @@ class TestHelpers(unittest.TestCase):
self.multiworld.game[self.player] = "helper_test_game" self.multiworld.game[self.player] = "helper_test_game"
self.multiworld.player_name = {1: "Tester"} self.multiworld.player_name = {1: "Tester"}
self.multiworld.set_seed() self.multiworld.set_seed()
self.multiworld.worlds[self.player] = TestWorld(self.multiworld, self.player)
def test_region_helpers(self) -> None: def test_region_helpers(self) -> None:
"""Tests `Region.add_locations()` and `Region.add_exits()` have correct behavior""" """Tests `Region.add_locations()` and `Region.add_exits()` have correct behavior"""
@@ -46,8 +49,9 @@ class TestHelpers(unittest.TestCase):
"TestRegion1": {"TestRegion3"} "TestRegion1": {"TestRegion3"}
} }
exit_rules: Dict[str, Callable[[CollectionState], bool]] = { exit_rules: Dict[str, CollectionRule | Rule[Any]] = {
"TestRegion1": lambda state: state.has("test_item", self.player) "TestRegion1": lambda state: state.has("test_item", self.player),
"TestRegion2": Has("test_item2"),
} }
self.multiworld.regions += [Region(region, self.player, self.multiworld, regions[region]) for region in regions] self.multiworld.regions += [Region(region, self.player, self.multiworld, regions[region]) for region in regions]
@@ -74,13 +78,17 @@ class TestHelpers(unittest.TestCase):
self.assertTrue(f"{parent} -> {exit_reg}" in created_exit_names) self.assertTrue(f"{parent} -> {exit_reg}" in created_exit_names)
if exit_reg in exit_rules: if exit_reg in exit_rules:
entrance_name = exit_name if exit_name else f"{parent} -> {exit_reg}" entrance_name = exit_name if exit_name else f"{parent} -> {exit_reg}"
self.assertEqual(exit_rules[exit_reg], rule = exit_rules[exit_reg]
self.multiworld.get_entrance(entrance_name, self.player).access_rule) if isinstance(rule, Rule):
self.assertEqual(rule.resolve(self.multiworld.worlds[self.player]),
self.multiworld.get_entrance(entrance_name, self.player).access_rule)
else:
self.assertEqual(rule, self.multiworld.get_entrance(entrance_name, self.player).access_rule)
for region in reg_exit_set: for region, exit_set in reg_exit_set.items():
current_region = self.multiworld.get_region(region, self.player) current_region = self.multiworld.get_region(region, self.player)
current_region.add_exits(reg_exit_set[region]) current_region.add_exits(exit_set)
exit_names = {_exit.name for _exit in current_region.exits} exit_names = {_exit.name for _exit in current_region.exits}
for reg_exit in reg_exit_set[region]: for reg_exit in exit_set:
self.assertTrue(f"{region} -> {reg_exit}" in exit_names, self.assertTrue(f"{region} -> {reg_exit}" in exit_names,
f"{region} -> {reg_exit} not in {exit_names}") f"{region} -> {reg_exit} not in {exit_names}")