From 8de745f4d3350ac848c9362a33e223c0ff94fdcf Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Tue, 9 Sep 2025 16:44:09 -0400 Subject: Added symbol shuffle Also fixed unlocked letters + any double letter cyan doors, and tweaked some logic related to important panels with symbols on them. --- apworld/player_logic.py | 54 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 17 deletions(-) (limited to 'apworld/player_logic.py') diff --git a/apworld/player_logic.py b/apworld/player_logic.py index dbd340c..42b36e6 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py @@ -1,6 +1,7 @@ from enum import IntEnum, auto from .generated import data_pb2 as data_pb2 +from .items import SYMBOL_ITEMS from typing import TYPE_CHECKING, NamedTuple from .options import VictoryCondition, ShuffleLetters, CyanDoorBehavior @@ -23,7 +24,6 @@ class AccessRequirements: items: set[str] progressives: dict[str, int] rooms: set[str] - symbols: set[str] letters: dict[str, int] cyans: bool @@ -34,7 +34,6 @@ class AccessRequirements: self.items = set() self.progressives = dict() self.rooms = set() - self.symbols = set() self.letters = dict() self.cyans = False self.or_logic = list() @@ -49,9 +48,6 @@ class AccessRequirements: for room in other.rooms: self.rooms.add(room) - for symbol in other.symbols: - self.symbols.add(symbol) - for letter, level in other.letters.items(): self.letters[letter] = max(self.letters.get(letter, 0), level) @@ -60,6 +56,10 @@ class AccessRequirements: for disjunction in other.or_logic: self.or_logic.append(disjunction) + def is_empty(self) -> bool: + return (len(self.items) == 0 and len(self.progressives) == 0 and len(self.rooms) == 0 and len(self.letters) == 0 + and not self.cyans and len(self.or_logic) == 0) + def __repr__(self): parts = [] if len(self.items) > 0: @@ -68,8 +68,6 @@ class AccessRequirements: parts.append(f"progressives={self.progressives}") if len(self.rooms) > 0: parts.append(f"rooms={self.rooms}") - if len(self.symbols) > 0: - parts.append(f"symbols={self.symbols}") if len(self.letters) > 0: parts.append(f"letters={self.letters}") if self.cyans: @@ -231,6 +229,10 @@ class Lingo2PlayerLogic: self.locations_by_room.setdefault(keyholder.room_id, []).append(PlayerLocation(keyholder.ap_id, reqs)) + if self.world.options.shuffle_symbols: + for symbol_name in SYMBOL_ITEMS.values(): + self.real_items.append(symbol_name) + def get_panel_reqs(self, panel_id: int, answer: str | None) -> AccessRequirements: if answer is None: if panel_id not in self.panel_reqs: @@ -253,25 +255,35 @@ class Lingo2PlayerLogic: self.add_solution_reqs(reqs, answer) elif len(panel.proxies) > 0: possibilities = [] + already_filled = False for proxy in panel.proxies: proxy_reqs = AccessRequirements() self.add_solution_reqs(proxy_reqs, proxy.answer) - possibilities.append(proxy_reqs) + if not proxy_reqs.is_empty(): + possibilities.append(proxy_reqs) + else: + already_filled = True + break - if not any(proxy.answer == panel.answer for proxy in panel.proxies): + if not already_filled and not any(proxy.answer == panel.answer for proxy in panel.proxies): proxy_reqs = AccessRequirements() self.add_solution_reqs(proxy_reqs, panel.answer) - possibilities.append(proxy_reqs) + if not proxy_reqs.is_empty(): + possibilities.append(proxy_reqs) + else: + already_filled = True - reqs.or_logic.append(possibilities) + if not already_filled: + reqs.or_logic.append(possibilities) else: self.add_solution_reqs(reqs, panel.answer) - for symbol in panel.symbols: - reqs.symbols.add(symbol) + if self.world.options.shuffle_symbols: + for symbol in panel.symbols: + reqs.items.add(SYMBOL_ITEMS.get(symbol)) if panel.HasField("required_door"): door_reqs = self.get_door_open_reqs(panel.required_door) @@ -300,9 +312,16 @@ class Lingo2PlayerLogic: panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) reqs.merge(panel_reqs) elif door.complete_at == 1: - reqs.or_logic.append([self.get_panel_reqs(proxy.panel, - proxy.answer if proxy.HasField("answer") else None) - for proxy in door.panels]) + disjunction = [] + for proxy in door.panels: + proxy_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) + if proxy_reqs.is_empty(): + disjunction.clear() + break + else: + disjunction.append(proxy_reqs) + if len(disjunction) > 0: + reqs.or_logic.append(disjunction) else: # TODO: Handle complete_at > 1 pass @@ -316,7 +335,8 @@ class Lingo2PlayerLogic: if self.world.options.cyan_door_behavior == CyanDoorBehavior.option_collect_h2: reqs.rooms.add("The Repetitive - Main Room") elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_any_double_letter: - reqs.cyans = True + if self.world.options.shuffle_letters != ShuffleLetters.option_unlocked: + reqs.cyans = True elif self.world.options.cyan_door_behavior == CyanDoorBehavior.option_item: # There shouldn't be any locations that are cyan doors. pass -- cgit 1.4.1