mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 12:11:33 -06:00
GER: Only consider usable exits when calculating dead-ends (#4701)
* Only consider usable exits when calculating whether or not a region is a dead-end * Update EntranceLookup unit tests * Add new dead-end test * Add additional explanation to the new test * minor formatting tweak based on review feedback --------- Co-authored-by: CodeGorilla <3672561+Ars-Ignis@users.noreply.github.com>
This commit is contained in:
@@ -50,13 +50,15 @@ class EntranceLookup:
|
||||
_random: random.Random
|
||||
_expands_graph_cache: dict[Entrance, bool]
|
||||
_coupled: bool
|
||||
_usable_exits: set[Entrance]
|
||||
|
||||
def __init__(self, rng: random.Random, coupled: bool):
|
||||
def __init__(self, rng: random.Random, coupled: bool, usable_exits: set[Entrance]):
|
||||
self.dead_ends = EntranceLookup.GroupLookup()
|
||||
self.others = EntranceLookup.GroupLookup()
|
||||
self._random = rng
|
||||
self._expands_graph_cache = {}
|
||||
self._coupled = coupled
|
||||
self._usable_exits = usable_exits
|
||||
|
||||
def _can_expand_graph(self, entrance: Entrance) -> bool:
|
||||
"""
|
||||
@@ -95,7 +97,8 @@ class EntranceLookup:
|
||||
# randomizable exits which are not reverse of the incoming entrance.
|
||||
# uncoupled mode is an exception because in this case going back in the door you just came in could
|
||||
# actually lead somewhere new
|
||||
if not exit_.connected_region and (not self._coupled or exit_.name != entrance.name):
|
||||
if (not exit_.connected_region and (not self._coupled or exit_.name != entrance.name)
|
||||
and exit_ in self._usable_exits):
|
||||
self._expands_graph_cache[entrance] = True
|
||||
return True
|
||||
elif exit_.connected_region and exit_.connected_region not in visited:
|
||||
@@ -333,7 +336,6 @@ def randomize_entrances(
|
||||
|
||||
start_time = time.perf_counter()
|
||||
er_state = ERPlacementState(world, coupled)
|
||||
entrance_lookup = EntranceLookup(world.random, coupled)
|
||||
# similar to fill, skip validity checks on entrances if the game is beatable on minimal accessibility
|
||||
perform_validity_check = True
|
||||
|
||||
@@ -349,6 +351,7 @@ def randomize_entrances(
|
||||
|
||||
# used when membership checks are needed on the exit list, e.g. speculative sweep
|
||||
exits_set = set(exits)
|
||||
entrance_lookup = EntranceLookup(world.random, coupled, exits_set)
|
||||
for entrance in er_targets:
|
||||
entrance_lookup.add(entrance)
|
||||
|
||||
|
@@ -65,8 +65,10 @@ class TestEntranceLookup(unittest.TestCase):
|
||||
"""tests that get_targets shuffles targets between groups when requested"""
|
||||
multiworld = generate_test_multiworld()
|
||||
generate_disconnected_region_grid(multiworld, 5)
|
||||
exits_set = set([ex for region in multiworld.get_regions(1)
|
||||
for ex in region.exits if not ex.connected_region])
|
||||
|
||||
lookup = EntranceLookup(multiworld.worlds[1].random, coupled=True)
|
||||
lookup = EntranceLookup(multiworld.worlds[1].random, coupled=True, usable_exits=exits_set)
|
||||
er_targets = [entrance for region in multiworld.get_regions(1)
|
||||
for entrance in region.entrances if not entrance.parent_region]
|
||||
for entrance in er_targets:
|
||||
@@ -86,8 +88,10 @@ class TestEntranceLookup(unittest.TestCase):
|
||||
"""tests that get_targets does not shuffle targets between groups when requested"""
|
||||
multiworld = generate_test_multiworld()
|
||||
generate_disconnected_region_grid(multiworld, 5)
|
||||
exits_set = set([ex for region in multiworld.get_regions(1)
|
||||
for ex in region.exits if not ex.connected_region])
|
||||
|
||||
lookup = EntranceLookup(multiworld.worlds[1].random, coupled=True)
|
||||
lookup = EntranceLookup(multiworld.worlds[1].random, coupled=True, usable_exits=exits_set)
|
||||
er_targets = [entrance for region in multiworld.get_regions(1)
|
||||
for entrance in region.entrances if not entrance.parent_region]
|
||||
for entrance in er_targets:
|
||||
@@ -99,6 +103,30 @@ class TestEntranceLookup(unittest.TestCase):
|
||||
group_order = [prev := group.randomization_group for group in retrieved_targets if prev != group.randomization_group]
|
||||
self.assertEqual([ERTestGroups.TOP, ERTestGroups.BOTTOM], group_order)
|
||||
|
||||
def test_selective_dead_ends(self):
|
||||
"""test that entrances that EntranceLookup has not been told to consider are ignored when finding dead-ends"""
|
||||
multiworld = generate_test_multiworld()
|
||||
generate_disconnected_region_grid(multiworld, 5)
|
||||
exits_set = set([ex for region in multiworld.get_regions(1)
|
||||
for ex in region.exits if not ex.connected_region
|
||||
and ex.name != "region20_right" and ex.name != "region21_left"])
|
||||
|
||||
lookup = EntranceLookup(multiworld.worlds[1].random, coupled=True, usable_exits=exits_set)
|
||||
er_targets = [entrance for region in multiworld.get_regions(1)
|
||||
for entrance in region.entrances if not entrance.parent_region and
|
||||
entrance.name != "region20_right" and entrance.name != "region21_left"]
|
||||
for entrance in er_targets:
|
||||
lookup.add(entrance)
|
||||
# region 20 is the bottom left corner of the grid, and therefore only has a right entrance from region 21
|
||||
# and a top entrance from region 15; since we've told lookup to ignore the right entrance from region 21,
|
||||
# the top entrance from region 15 should be considered a dead-end
|
||||
dead_end_region = multiworld.get_region("region20", 1)
|
||||
for dead_end in dead_end_region.entrances:
|
||||
if dead_end.name == "region20_top":
|
||||
break
|
||||
# there should be only this one dead-end
|
||||
self.assertTrue(dead_end in lookup.dead_ends)
|
||||
self.assertEqual(len(lookup.dead_ends), 1)
|
||||
|
||||
class TestBakeTargetGroupLookup(unittest.TestCase):
|
||||
def test_lookup_generation(self):
|
||||
|
Reference in New Issue
Block a user