From 94a02510c058226e1ad01f454c47dbdf528af9fe Mon Sep 17 00:00:00 2001 From: alwaysintreble Date: Mon, 10 Apr 2023 14:07:37 -0500 Subject: [PATCH] core: Region management helpers (#761) --- BaseClasses.py | 23 ++++++++++++ test/general/TestHelpers.py | 71 +++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 test/general/TestHelpers.py diff --git a/BaseClasses.py b/BaseClasses.py index 53fe407e..9c2c6d6a 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -836,6 +836,29 @@ class Region: for entrance in self.entrances: # BFS might be better here, trying DFS for now. return entrance.parent_region.get_connecting_entrance(is_main_entrance) + def add_locations(self, locations: Dict[str, Optional[int]], location_type: Optional[typing.Type[Location]] = None) -> None: + """Adds locations to the Region object, where location_type is your Location class and locations is a dict of + location names to address.""" + if location_type is None: + location_type = Location + for location, address in locations.items(): + self.locations.append(location_type(self.player, location, address, self)) + + def add_exits(self, exits: Dict[str, Optional[str]], rules: Dict[str, Callable[[CollectionState], bool]] = None) -> None: + """ + Connects current region to regions in exit dictionary. Passed region names must exist first. + + :param exits: exits from the region. format is {"connecting_region", "exit_name"} + :param rules: rules for the exits from this region. format is {"connecting_region", rule} + """ + for exiting_region, name in exits.items(): + ret = Entrance(self.player, name, self) if name \ + else Entrance(self.player, f"{self.name} -> {exiting_region}", self) + if rules and exiting_region in rules: + ret.access_rule = rules[exiting_region] + self.exits.append(ret) + ret.connect(self.multiworld.get_region(exiting_region, self.player)) + def __repr__(self): return self.__str__() diff --git a/test/general/TestHelpers.py b/test/general/TestHelpers.py new file mode 100644 index 00000000..e27d2263 --- /dev/null +++ b/test/general/TestHelpers.py @@ -0,0 +1,71 @@ +from random import seed +from typing import Dict, Optional, Callable, Tuple, List + +from BaseClasses import MultiWorld, CollectionState, Region +import unittest + + +class TestHelpers(unittest.TestCase): + multiworld: MultiWorld + player: int = 1 + + def setUp(self) -> None: + self.multiworld = MultiWorld(self.player) + self.multiworld.game[self.player] = "helper_test_game" + self.multiworld.player_name = {1: "Tester"} + self.multiworld.set_seed(seed) + self.multiworld.set_default_common_options() + + def testRegionHelpers(self) -> None: + regions: Dict[str, str] = { + "TestRegion1": "I'm an apple", + "TestRegion2": "I'm a banana", + } + + locations: Dict[str, Dict[str, Optional[int]]] = { + "TestRegion1": { + "loc_1": 123, + "loc_2": 456, + "event_loc": None, + }, + "TestRegion2": { + "loc_1": 321, + "loc_2": 654, + } + } + + reg_exits: Dict[str, Dict[str, Optional[str]]] = { + "TestRegion1": {"TestRegion2": "connection"}, + "TestRegion2": {"TestRegion1": None}, + } + + exit_rules: Dict[str, Callable[[CollectionState], bool]] = { + "TestRegion1": lambda state: state.has("test_item", self.player) + } + + self.multiworld.regions += [Region(region, self.player, self.multiworld, regions[region]) for region in regions] + + with self.subTest("Test Location Creation Helper"): + for region, loc_pair in locations.items(): + self.multiworld.get_region(region, self.player).add_locations(loc_pair) + + created_location_names = [loc.name for loc in self.multiworld.get_locations()] + for loc_pair in locations.values(): + for loc_name in loc_pair: + self.assertTrue(loc_name in created_location_names) + + with self.subTest("Test Exit Creation Helper"): + for region, exit_dict in reg_exits.items(): + self.multiworld.get_region(region, self.player).add_exits(exit_dict, exit_rules) + + created_exit_names = [exit.name for region in self.multiworld.get_regions() for exit in region.exits] + for parent, exit_pair in reg_exits.items(): + for exit_reg, exit_name in exit_pair.items(): + if exit_name: + self.assertTrue(exit_name in created_exit_names) + else: + self.assertTrue(f"{parent} -> {exit_reg}" in created_exit_names) + if exit_reg in exit_rules: + entrance_name = exit_name if exit_name else f"{parent} -> {exit_reg}" + self.assertEqual(exit_rules[exit_reg], + self.multiworld.get_entrance(entrance_name, self.player).access_rule)