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.py53
1 files changed, 46 insertions, 7 deletions
diff --git a/apworld/rules.py b/apworld/rules.py index 05689e9..f859e75 100644 --- a/apworld/rules.py +++ b/apworld/rules.py
@@ -1,27 +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, letters 22 if not all(state.can_reach(region) for region in regions):
23 return False
24
25 for letter_key, letter_level in reqs.letters.items():
26 if not state.has(letter_key, world.player, letter_level):
27 return False
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
34 if len(reqs.or_logic) > 0:
35 if not all(any(lingo2_can_satisfy_requirements(state, sub_reqs, [], world) for sub_reqs in subjunction)
36 for subjunction in reqs.or_logic):
37 return False
19 38
20 for disjunction in reqs.or_logic: 39 if reqs.complete_at is not None:
21 if not any(lingo2_can_satisfy_requirements(state, sub_reqs, world) for sub_reqs in disjunction): 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:
22 return False 52 return False
23 53
24 return True 54 return True
25 55
26def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World") -> Callable[[CollectionState], bool]: 56def make_location_lambda(reqs: AccessRequirements, world: "Lingo2World",
27 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)