From 18e64c9102f526b04c44623aa39c9b3cb6dee1ff Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Wed, 27 Aug 2025 18:53:50 -0400 Subject: Letter requirements in apworld Also fixed or logic so everything actually works now. --- apworld/player_logic.py | 46 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 4 deletions(-) (limited to 'apworld/player_logic.py') diff --git a/apworld/player_logic.py b/apworld/player_logic.py index 83c16f2..6feef99 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py @@ -10,7 +10,11 @@ def calculate_letter_histogram(solution: str) -> dict[str, int]: for l in solution: if l.isalpha(): real_l = l.upper() - histogram[real_l] = min(histogram.get(l, 0) + 1, 2) + histogram[real_l] = min(histogram.get(real_l, 0) + 1, 2) + + for free_letter in "HINT": + if histogram.get(free_letter, 0) == 1: + del histogram[free_letter] return histogram @@ -53,6 +57,20 @@ class AccessRequirements: for disjunction in other.or_logic: self.or_logic.append(disjunction) + def __repr__(self): + parts = [] + if len(self.items) > 0: + parts.append(f"items={self.items}") + 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 len(self.or_logic) > 0: + parts.append(f"or_logic={self.or_logic}") + return f"AccessRequirements({", ".join(parts)})" + class PlayerLocation(NamedTuple): code: int | None @@ -100,6 +118,14 @@ class Lingo2PlayerLogic: self.locations_by_room.setdefault(letter.room_id, []).append(PlayerLocation(letter.ap_id, AccessRequirements())) + letter_name = f"{letter.key.upper()}{'2' if letter.level2 else '1'}" + event_name = f"{letter_name} (Collected)" + self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper() + + if letter.level2: + event_name = f"{letter_name} (Double Collected)" + self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper() + for mastery in world.static_logic.objects.masteries: self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id, AccessRequirements())) @@ -137,17 +163,21 @@ class Lingo2PlayerLogic: if answer is not None: reqs.add_solution(answer) elif len(panel.proxies) > 0: + possibilities = [] + for proxy in panel.proxies: proxy_reqs = AccessRequirements() proxy_reqs.add_solution(proxy.answer) - reqs.or_logic.append([proxy_reqs]) + possibilities.append(proxy_reqs) if not any(proxy.answer == panel.answer for proxy in panel.proxies): proxy_reqs = AccessRequirements() proxy_reqs.add_solution(panel.answer) - reqs.or_logic.append([proxy_reqs]) + possibilities.append(proxy_reqs) + + reqs.or_logic.append(possibilities) else: reqs.add_solution(panel.answer) @@ -175,7 +205,7 @@ class Lingo2PlayerLogic: door = self.world.static_logic.objects.doors[door_id] reqs = AccessRequirements() - # TODO: complete_at, control_center_color, switches, keyholders + # TODO: control_center_color, switches if not door.HasField("complete_at") or door.complete_at == 0: for proxy in door.panels: panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) @@ -188,6 +218,14 @@ class Lingo2PlayerLogic: # TODO: Handle complete_at > 1 pass + for keyholder_uses in door.keyholders: + key_name = keyholder_uses.key.upper() + if key_name not in reqs.letters: + reqs.letters[key_name] = 1 + + keyholder = self.world.static_logic.objects.keyholders[keyholder_uses.keyholder] + reqs.rooms.add(self.world.static_logic.get_room_region_name(keyholder.room_id)) + for room in door.rooms: reqs.rooms.add(self.world.static_logic.get_room_region_name(room)) -- cgit 1.4.1