diff options
| author | Star Rauchenberger <fefferburbia@gmail.com> | 2025-08-27 18:53:50 -0400 |
|---|---|---|
| committer | Star Rauchenberger <fefferburbia@gmail.com> | 2025-08-27 18:53:50 -0400 |
| commit | 18e64c9102f526b04c44623aa39c9b3cb6dee1ff (patch) | |
| tree | 39b856ae472a1b9033a9122e63be28bddd55bea2 | |
| parent | 4ae885f392261b939e6f21b47d545f609c6e1965 (diff) | |
| download | lingo2-archipelago-18e64c9102f526b04c44623aa39c9b3cb6dee1ff.tar.gz lingo2-archipelago-18e64c9102f526b04c44623aa39c9b3cb6dee1ff.tar.bz2 lingo2-archipelago-18e64c9102f526b04c44623aa39c9b3cb6dee1ff.zip | |
Letter requirements in apworld
Also fixed or logic so everything actually works now.
| -rw-r--r-- | apworld/player_logic.py | 46 | ||||
| -rw-r--r-- | apworld/rules.py | 11 | ||||
| -rw-r--r-- | data/connections.txtpb | 16 |
3 files changed, 66 insertions, 7 deletions
| 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]: | |||
| 10 | for l in solution: | 10 | for l in solution: |
| 11 | if l.isalpha(): | 11 | if l.isalpha(): |
| 12 | real_l = l.upper() | 12 | real_l = l.upper() |
| 13 | histogram[real_l] = min(histogram.get(l, 0) + 1, 2) | 13 | histogram[real_l] = min(histogram.get(real_l, 0) + 1, 2) |
| 14 | |||
| 15 | for free_letter in "HINT": | ||
| 16 | if histogram.get(free_letter, 0) == 1: | ||
| 17 | del histogram[free_letter] | ||
| 14 | 18 | ||
| 15 | return histogram | 19 | return histogram |
| 16 | 20 | ||
| @@ -53,6 +57,20 @@ class AccessRequirements: | |||
| 53 | for disjunction in other.or_logic: | 57 | for disjunction in other.or_logic: |
| 54 | self.or_logic.append(disjunction) | 58 | self.or_logic.append(disjunction) |
| 55 | 59 | ||
| 60 | def __repr__(self): | ||
| 61 | parts = [] | ||
| 62 | if len(self.items) > 0: | ||
| 63 | parts.append(f"items={self.items}") | ||
| 64 | if len(self.rooms) > 0: | ||
| 65 | parts.append(f"rooms={self.rooms}") | ||
| 66 | if len(self.symbols) > 0: | ||
| 67 | parts.append(f"symbols={self.symbols}") | ||
| 68 | if len(self.letters) > 0: | ||
| 69 | parts.append(f"letters={self.letters}") | ||
| 70 | if len(self.or_logic) > 0: | ||
| 71 | parts.append(f"or_logic={self.or_logic}") | ||
| 72 | return f"AccessRequirements({", ".join(parts)})" | ||
| 73 | |||
| 56 | 74 | ||
| 57 | class PlayerLocation(NamedTuple): | 75 | class PlayerLocation(NamedTuple): |
| 58 | code: int | None | 76 | code: int | None |
| @@ -100,6 +118,14 @@ class Lingo2PlayerLogic: | |||
| 100 | self.locations_by_room.setdefault(letter.room_id, []).append(PlayerLocation(letter.ap_id, | 118 | self.locations_by_room.setdefault(letter.room_id, []).append(PlayerLocation(letter.ap_id, |
| 101 | AccessRequirements())) | 119 | AccessRequirements())) |
| 102 | 120 | ||
| 121 | letter_name = f"{letter.key.upper()}{'2' if letter.level2 else '1'}" | ||
| 122 | event_name = f"{letter_name} (Collected)" | ||
| 123 | self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper() | ||
| 124 | |||
| 125 | if letter.level2: | ||
| 126 | event_name = f"{letter_name} (Double Collected)" | ||
| 127 | self.event_loc_item_by_room.setdefault(letter.room_id, {})[event_name] = letter.key.upper() | ||
| 128 | |||
| 103 | for mastery in world.static_logic.objects.masteries: | 129 | for mastery in world.static_logic.objects.masteries: |
| 104 | self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id, | 130 | self.locations_by_room.setdefault(mastery.room_id, []).append(PlayerLocation(mastery.ap_id, |
| 105 | AccessRequirements())) | 131 | AccessRequirements())) |
| @@ -137,17 +163,21 @@ class Lingo2PlayerLogic: | |||
| 137 | if answer is not None: | 163 | if answer is not None: |
| 138 | reqs.add_solution(answer) | 164 | reqs.add_solution(answer) |
| 139 | elif len(panel.proxies) > 0: | 165 | elif len(panel.proxies) > 0: |
| 166 | possibilities = [] | ||
| 167 | |||
| 140 | for proxy in panel.proxies: | 168 | for proxy in panel.proxies: |
| 141 | proxy_reqs = AccessRequirements() | 169 | proxy_reqs = AccessRequirements() |
| 142 | proxy_reqs.add_solution(proxy.answer) | 170 | proxy_reqs.add_solution(proxy.answer) |
| 143 | 171 | ||
| 144 | reqs.or_logic.append([proxy_reqs]) | 172 | possibilities.append(proxy_reqs) |
| 145 | 173 | ||
| 146 | if not any(proxy.answer == panel.answer for proxy in panel.proxies): | 174 | if not any(proxy.answer == panel.answer for proxy in panel.proxies): |
| 147 | proxy_reqs = AccessRequirements() | 175 | proxy_reqs = AccessRequirements() |
| 148 | proxy_reqs.add_solution(panel.answer) | 176 | proxy_reqs.add_solution(panel.answer) |
| 149 | 177 | ||
| 150 | reqs.or_logic.append([proxy_reqs]) | 178 | possibilities.append(proxy_reqs) |
| 179 | |||
| 180 | reqs.or_logic.append(possibilities) | ||
| 151 | else: | 181 | else: |
| 152 | reqs.add_solution(panel.answer) | 182 | reqs.add_solution(panel.answer) |
| 153 | 183 | ||
| @@ -175,7 +205,7 @@ class Lingo2PlayerLogic: | |||
| 175 | door = self.world.static_logic.objects.doors[door_id] | 205 | door = self.world.static_logic.objects.doors[door_id] |
| 176 | reqs = AccessRequirements() | 206 | reqs = AccessRequirements() |
| 177 | 207 | ||
| 178 | # TODO: complete_at, control_center_color, switches, keyholders | 208 | # TODO: control_center_color, switches |
| 179 | if not door.HasField("complete_at") or door.complete_at == 0: | 209 | if not door.HasField("complete_at") or door.complete_at == 0: |
| 180 | for proxy in door.panels: | 210 | for proxy in door.panels: |
| 181 | panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) | 211 | panel_reqs = self.get_panel_reqs(proxy.panel, proxy.answer if proxy.HasField("answer") else None) |
| @@ -188,6 +218,14 @@ class Lingo2PlayerLogic: | |||
| 188 | # TODO: Handle complete_at > 1 | 218 | # TODO: Handle complete_at > 1 |
| 189 | pass | 219 | pass |
| 190 | 220 | ||
| 221 | for keyholder_uses in door.keyholders: | ||
| 222 | key_name = keyholder_uses.key.upper() | ||
| 223 | if key_name not in reqs.letters: | ||
| 224 | reqs.letters[key_name] = 1 | ||
| 225 | |||
| 226 | keyholder = self.world.static_logic.objects.keyholders[keyholder_uses.keyholder] | ||
| 227 | reqs.rooms.add(self.world.static_logic.get_room_region_name(keyholder.room_id)) | ||
| 228 | |||
| 191 | for room in door.rooms: | 229 | for room in door.rooms: |
| 192 | reqs.rooms.add(self.world.static_logic.get_room_region_name(room)) | 230 | reqs.rooms.add(self.world.static_logic.get_room_region_name(room)) |
| 193 | 231 | ||
| diff --git a/apworld/rules.py b/apworld/rules.py index 05689e9..4a84acf 100644 --- a/apworld/rules.py +++ b/apworld/rules.py | |||
| @@ -15,10 +15,15 @@ def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirem | |||
| 15 | if not all(state.can_reach_region(region_name, world.player) for region_name in reqs.rooms): | 15 | if not all(state.can_reach_region(region_name, world.player) for region_name in reqs.rooms): |
| 16 | return False | 16 | return False |
| 17 | 17 | ||
| 18 | # TODO: symbols, letters | 18 | # TODO: symbols |
| 19 | 19 | ||
| 20 | for disjunction in reqs.or_logic: | 20 | for letter_key, letter_level in reqs.letters.items(): |
| 21 | if not any(lingo2_can_satisfy_requirements(state, sub_reqs, world) for sub_reqs in disjunction): | 21 | if not state.has(letter_key, world.player, letter_level): |
| 22 | return False | ||
| 23 | |||
| 24 | if len(reqs.or_logic) > 0: | ||
| 25 | if not all(any(lingo2_can_satisfy_requirements(state, sub_reqs, world) for sub_reqs in subjunction) | ||
| 26 | for subjunction in reqs.or_logic): | ||
| 22 | return False | 27 | return False |
| 23 | 28 | ||
| 24 | return True | 29 | return True |
| diff --git a/data/connections.txtpb b/data/connections.txtpb index c3be3f1..057d8bb 100644 --- a/data/connections.txtpb +++ b/data/connections.txtpb | |||
| @@ -2388,3 +2388,19 @@ connections { | |||
| 2388 | } | 2388 | } |
| 2389 | oneway: true | 2389 | oneway: true |
| 2390 | } | 2390 | } |
| 2391 | connections { | ||
| 2392 | from { | ||
| 2393 | port { | ||
| 2394 | map: "the_entry" | ||
| 2395 | room: "X Area" | ||
| 2396 | name: "CC" | ||
| 2397 | } | ||
| 2398 | } | ||
| 2399 | to { | ||
| 2400 | port { | ||
| 2401 | map: "control_center" | ||
| 2402 | room: "Entry Entrance" | ||
| 2403 | name: "ENTRY" | ||
| 2404 | } | ||
| 2405 | } | ||
| 2406 | } | ||
