Lingo: Add a third location to Starting Room (#2839)

Despite earlier efforts, there were still rare fill errors when door shuffle and color shuffle were on and early color hallways was off, because sphere 1 was too small. This turns "Starting Room - HI" back into a location, which should give the algorithm more room.

The "forced good item" pool has been reconsidered. The problem with the specific item that caused the recent failure (Welcome Back - Shortcut to Starting Room) is that it only provided one location when color shuffle was on, which is a net of zero considering that the GOOD LUCK check was forced. It will no longer show up as a good item unless color shuffle is off. On an opposite vein, Rhyme Room Doors will now show up even if color shuffle is on, because it gives color hallways access by itself.

A good item will only be forced onto GOOD LUCK now if there is more than one player.
This commit is contained in:
Star Rauchenberger 2024-03-03 02:20:37 -05:00 committed by GitHub
parent b8bf67a166
commit b2f30d5fd0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 27 additions and 41 deletions

View File

@ -112,6 +112,7 @@
HI: HI:
id: Entry Room/Panel_hi_hi id: Entry Room/Panel_hi_hi
tag: midwhite tag: midwhite
check: True
HIDDEN: HIDDEN:
id: Entry Room/Panel_hidden_hidden id: Entry Room/Panel_hidden_hidden
tag: midwhite tag: midwhite

View File

@ -248,30 +248,44 @@ class LingoPlayerLogic:
"kind of logic error.") "kind of logic error.")
if door_shuffle != ShuffleDoors.option_none and location_classification != LocationClassification.insanity \ if door_shuffle != ShuffleDoors.option_none and location_classification != LocationClassification.insanity \
and not early_color_hallways: and not early_color_hallways and world.multiworld.players > 1:
# If shuffle doors is on, force a useful item onto the HI panel. This may not necessarily get you out of BK, # Under the combination of door shuffle, normal location checks, and no early color hallways, sphere 1 is
# but the goal is to allow you to reach at least one more check. The non-painting ones are hardcoded right # only three checks. In a multiplayer situation, this can be frustrating for the player because they are
# now. We only allow the entrance to the Pilgrim Room if color shuffle is off, because otherwise there are # more likely to be stuck in the starting room for a long time. To remedy this, we will force a useful item
# no extra checks in there. We only include the entrance to the Rhyme Room when color shuffle is off and # onto the GOOD LUCK check under these circumstances. The goal is to expand sphere 1 to at least four
# door shuffle is on simple, because otherwise there are no extra checks in there. # checks (and likely more than that).
#
# Note: A very low LEVEL 2 requirement would naturally expand sphere 1 to four checks, but this is a very
# uncommon configuration, so we will ignore it and force a good item anyway.
# Starting Room - Back Right Door gives access to OPEN and DEAD END.
# Starting Room - Exit Door gives access to OPEN and TRACE.
good_item_options: List[str] = ["Starting Room - Back Right Door", "Second Room - Exit Door"] good_item_options: List[str] = ["Starting Room - Back Right Door", "Second Room - Exit Door"]
if not color_shuffle: if not color_shuffle:
# HOT CRUST and THIS.
good_item_options.append("Pilgrim Room - Sun Painting") good_item_options.append("Pilgrim Room - Sun Painting")
if door_shuffle == ShuffleDoors.option_simple: if door_shuffle == ShuffleDoors.option_simple:
good_item_options += ["Welcome Back Doors"] # WELCOME BACK, CLOCKWISE, and DRAWL + RUNS.
good_item_options.append("Welcome Back Doors")
if not color_shuffle:
good_item_options.append("Rhyme Room Doors")
else: else:
good_item_options += ["Welcome Back Area - Shortcut to Starting Room"] # WELCOME BACK and CLOCKWISE.
good_item_options.append("Welcome Back Area - Shortcut to Starting Room")
if door_shuffle == ShuffleDoors.option_simple:
# Color hallways access (NOTE: reconsider when sunwarp shuffling exists).
good_item_options.append("Rhyme Room Doors")
# When painting shuffle is off, most Starting Room paintings give color hallways access. The Wondrous's
# painting does not, but it gives access to SHRINK and WELCOME BACK.
for painting_obj in PAINTINGS_BY_ROOM["Starting Room"]: for painting_obj in PAINTINGS_BY_ROOM["Starting Room"]:
if not painting_obj.enter_only or painting_obj.required_door is None: if not painting_obj.enter_only or painting_obj.required_door is None:
continue continue
# If painting shuffle is on, we only want to consider paintings that actually go somewhere. # If painting shuffle is on, we only want to consider paintings that actually go somewhere.
#
# NOTE: This does not guarantee that there will be any checks on the other side.
if painting_shuffle and painting_obj.id not in self.painting_mapping.keys(): if painting_shuffle and painting_obj.id not in self.painting_mapping.keys():
continue continue

View File

@ -8,8 +8,6 @@ class TestRequiredRoomLogic(LingoTestBase):
} }
def test_pilgrim_first(self) -> None: def test_pilgrim_first(self) -> None:
self.remove_forced_good_item()
self.assertFalse(self.multiworld.state.can_reach("The Seeker", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("The Seeker", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Pilgrim Antechamber", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Pilgrim Antechamber", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Pilgrim Room", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Pilgrim Room", "Region", self.player))
@ -30,8 +28,6 @@ class TestRequiredRoomLogic(LingoTestBase):
self.assertTrue(self.can_reach_location("The Seeker - Achievement")) self.assertTrue(self.can_reach_location("The Seeker - Achievement"))
def test_hidden_first(self) -> None: def test_hidden_first(self) -> None:
self.remove_forced_good_item()
self.assertFalse(self.multiworld.state.can_reach("The Seeker", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("The Seeker", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Pilgrim Room", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Pilgrim Room", "Region", self.player))
self.assertFalse(self.can_reach_location("The Seeker - Achievement")) self.assertFalse(self.can_reach_location("The Seeker - Achievement"))
@ -59,8 +55,6 @@ class TestRequiredDoorLogic(LingoTestBase):
} }
def test_through_rhyme(self) -> None: def test_through_rhyme(self) -> None:
self.remove_forced_good_item()
self.assertFalse(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall")) self.assertFalse(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall"))
self.collect_by_name("Starting Room - Rhyme Room Entrance") self.collect_by_name("Starting Room - Rhyme Room Entrance")
@ -70,8 +64,6 @@ class TestRequiredDoorLogic(LingoTestBase):
self.assertTrue(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall")) self.assertTrue(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall"))
def test_through_hidden(self) -> None: def test_through_hidden(self) -> None:
self.remove_forced_good_item()
self.assertFalse(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall")) self.assertFalse(self.can_reach_location("Rhyme Room - Circle/Looped Square Wall"))
self.collect_by_name("Starting Room - Rhyme Room Entrance") self.collect_by_name("Starting Room - Rhyme Room Entrance")
@ -91,8 +83,6 @@ class TestSimpleDoors(LingoTestBase):
} }
def test_requirement(self): def test_requirement(self):
self.remove_forced_good_item()
self.assertFalse(self.multiworld.state.can_reach("Outside The Wanderer", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Outside The Wanderer", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player))

View File

@ -8,8 +8,6 @@ class TestProgressiveOrangeTower(LingoTestBase):
} }
def test_from_welcome_back(self) -> None: def test_from_welcome_back(self) -> None:
self.remove_forced_good_item()
self.assertFalse(self.multiworld.state.can_reach("Orange Tower First Floor", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower First Floor", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Second Floor", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower Second Floor", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player))
@ -85,8 +83,6 @@ class TestProgressiveOrangeTower(LingoTestBase):
self.assertTrue(self.multiworld.state.can_reach("Orange Tower Seventh Floor", "Region", self.player)) self.assertTrue(self.multiworld.state.can_reach("Orange Tower Seventh Floor", "Region", self.player))
def test_from_hub_room(self) -> None: def test_from_hub_room(self) -> None:
self.remove_forced_good_item()
self.assertFalse(self.multiworld.state.can_reach("Orange Tower First Floor", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower First Floor", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Second Floor", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower Second Floor", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Orange Tower Third Floor", "Region", self.player))

View File

@ -7,8 +7,6 @@ class TestComplexProgressiveHallwayRoom(LingoTestBase):
} }
def test_item(self): def test_item(self):
self.remove_forced_good_item()
self.assertFalse(self.multiworld.state.can_reach("Outside The Agreeable", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Outside The Agreeable", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Hallway Room (2)", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Hallway Room (2)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Hallway Room (3)", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Hallway Room (3)", "Region", self.player))
@ -60,8 +58,6 @@ class TestSimpleHallwayRoom(LingoTestBase):
} }
def test_item(self): def test_item(self):
self.remove_forced_good_item()
self.assertFalse(self.multiworld.state.can_reach("Outside The Agreeable", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Outside The Agreeable", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Hallway Room (2)", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Hallway Room (2)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Hallway Room (3)", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Hallway Room (3)", "Region", self.player))
@ -90,8 +86,6 @@ class TestProgressiveArtGallery(LingoTestBase):
} }
def test_item(self): def test_item(self):
self.remove_forced_good_item()
self.assertFalse(self.multiworld.state.can_reach("Art Gallery", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Art Gallery", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Second Floor)", "Region", self.player))
self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player)) self.assertFalse(self.multiworld.state.can_reach("Art Gallery (Third Floor)", "Region", self.player))

View File

@ -6,12 +6,3 @@ from test.bases import WorldTestBase
class LingoTestBase(WorldTestBase): class LingoTestBase(WorldTestBase):
game = "Lingo" game = "Lingo"
player: ClassVar[int] = 1 player: ClassVar[int] = 1
def world_setup(self, *args, **kwargs):
super().world_setup(*args, **kwargs)
def remove_forced_good_item(self):
location = self.multiworld.get_location("Second Room - Good Luck", self.player)
self.remove(location.item)
self.multiworld.itempool.append(location.item)
self.multiworld.state.events.add(location)