about summary refs log tree commit diff stats
path: root/apworld/rules.py
diff options
context:
space:
mode:
Diffstat (limited to 'apworld/rules.py')
-rw-r--r--apworld/rules.py46
1 files changed, 40 insertions, 6 deletions
diff --git a/apworld/rules.py b/apworld/rules.py index 4a84acf..f859e75 100644 --- a/apworld/rules.py +++ b/apworld/rules.py
@@ -1,32 +1,66 @@
1from collections.abc import Callable 1from collections.abc import Callable
2from typing import TYPE_CHECKING 2from typing import TYPE_CHECKING
3 3
4from BaseClasses import CollectionState 4from BaseClasses import CollectionState, Region
5from .player_logic import AccessRequirements 5from .player_logic import AccessRequirements
6 6
7if TYPE_CHECKING: 7if TYPE_CHECKING:
8 from . import Lingo2World 8 from . import Lingo2World
9 9
10 10
11def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirements, world: "Lingo2World") -> bool: 11def lingo2_can_satisfy_requirements(state: CollectionState, reqs: AccessRequirements, regions: list[Region],
12 world: "Lingo2World") -> bool:
12 if not all(state.has(item, world.player) for item in reqs.items): 13 if not all(state.has(item, world.player) for item in reqs.items):
13 return False 14 return False
14 15
16 if not all(state.has(item, world.player, amount) for item, amount in reqs.progressives.items()):
17 return False
18
15 if not all(state.can_reach_region(region_name, world.player) for region_name in reqs.rooms): 19 if not all(state.can_reach_region(region_name, world.player) for region_name in reqs.rooms):
16 return False 20 return False
17 21
18 # TODO: symbols 22 if not all(state.can_reach(region) for region in regions):
23 return False
19 24
20 for letter_key, letter_level in reqs.letters.items(): 25 for letter_key, letter_level in reqs.letters.items():
21 if not state.has(letter_key, world.player, letter_level): 26 if not state.has(letter_key, world.player, letter_level):
22 return False 27 return False
23 28
29 if reqs.cyans:
30 if not any(state.has(letter, world.player, amount)
31 for letter, amount in world.player_logic.double_letter_amount.items()):
32 return False
33
24 if len(reqs.or_logic) > 0: 34 if len(reqs.or_logic) > 0:
25 if not all(any(lingo2_can_satisfy_requirements(state, sub_reqs, world) for sub_reqs in subjunction) 35 if not all(any(lingo2_can_satisfy_requirements(state, sub_reqs, [], world) for sub_reqs in subjunction)
26 for subjunction in reqs.or_logic): 36 for subjunction in reqs.or_logic):
27 return False 37 return False
28 38
39 if reqs.complete_at is not None:
40 completed = 0
41 checked = 0
42 for possibility in reqs.possibilities:
43 checked += 1
44 if lingo2_can_satisfy_requirements(state, possibility, [], world):
45 completed += 1
46 if completed >= reqs.complete_at:
47 break
48 elif len(reqs.possibilities) - checked + completed < reqs.complete_at:
49 # There aren't enough remaining possibilities for the check to pass.
50 return False
51 if completed < reqs.complete_at:
52 return False
53
29 return True 54 return True
30 55
31def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World") -> Callable[[CollectionState], bool]: 56def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World",
32 return lambda state: lingo2_can_satisfy_requirements(state, reqs, world) 57 regions: dict[str, Region] | None) -> Callable[[CollectionState], bool]:
58 # Replace required rooms with regions for the top level requirement, which saves looking up the regions during rule
59 # checking.
60 if regions is not None:
61 required_regions = [regions[room_name] for room_name in reqs.rooms]
62 else:
63 required_regions = [world.multiworld.get_region(room_name, world.player) for room_name in reqs.rooms]
64 new_reqs = reqs.copy()
65 new_reqs.rooms.clear()
66 return lambda state: lingo2_can_satisfy_requirements(state, new_reqs, required_regions, world)