4 from typing import Any, Callable
7 from worlds.generic.Rules import set_rule
10 OriginRegionName = "REPL"
12 class Location(BaseClasses.Location):
16 def __init__(self, data: dict[str, Any]):
19 return self.data['name']
21 (chapter, exercise) = str(self).split(' ')[0].split('.')
22 return int(chapter) * 100 + int(exercise)
24 with open(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'locations.yml'), 'r') as input:
25 all_locations = [LocationData(data) for data in yaml.safe_load(input)]
28 def set_up_rules(loc: LocationData, root: BaseClasses.Region, region: BaseClasses.Region, player: int):
29 # Ideally this would be automatically generated from locations.yml and items.yml!
30 # But with only 40 puzzles, this is a more guaranteed approach:
33 103: ('+', '*', '<', 'if'),
34 111: ('#recursion', '<', '+'),
35 116: ('#recursion', 'zero?', 'decr', '*', '/'),
36 119: ('even?', 'zero?', '/', '*', '+', '#recursion'),
37 130: ('#recursion', '+', '='),
38 133: ('#recursion', '='),
42 143: ('#recursion', 'decr', 'zero?'),
44 201: ('cons', 'if', '<', '-'),
45 202: ('/', '+', 'car', 'cdr'),
50 217: ('#recursion', '#null-or-pair-check', 'cdr'),
51 218: ('#recursion', '#null-or-pair-check', 'car', 'cdr', 'cons'),
52 220: ('if', 'filter', '#null-or-pair-check', 'even?'),
55 227: ('reverse', 'map', '#number-or-pair-or-list-check', '#recursion'),
56 228: ('#number-or-pair-or-list-check', '#recursion', 'flat-map', '#list-or-cons'),
57 229: ('#recursion', '#number-or-pair-or-list-check', '+', '*', '='),
58 230: ('#tree-map', '*'),
60 232: ('cons', 'car', 'cdr', 'map', 'append', '#recursion', '#null-or-pair-check'),
61 236: ('map', 'car', 'cdr', '#recursion', 'accumulate', '#null-or-pair-check'),
62 240: ('#recursion', '=', 'incr', '#list-or-cons', 'append'),
63 241: ('#recursion', '=', '+', '#list-or-cons', 'append'),
64 254: ('car', 'cdr', 'if', 'eq?', '#pair-or-list-check'),
65 259: ('append', 'filter'),
66 261: ('#recursion', '#null-or-pair-check', '<', 'car', 'cdr', 'cons'),
67 262: ('#recursion', '#null-or-pair-check', '<', 'car', 'cdr', 'cons'),
68 354: ('stream-map', 'cons-stream', 'define', '*'),
69 355: ('stream-map', 'cons-stream', 'define', '+'),
70 356: ('stream-map', 'cons-stream', 'define', '*'),
71 364: ('stream-car', 'stream-cdr', '<', '-', '#recursion'),
72 370: ('null?', '<', 'stream-car', 'stream-cdr', '#recursion', 'cons-stream'),
75 c = root.connect(region, name=str(loc))
77 reqs = rules[loc.id()]
78 set_rule(c, lambda state: StateChecker(state, player).check(*reqs))
80 raise AssertionError(f'missing rules for {loc}')
83 state: BaseClasses.CollectionState
85 checks: dict[str, Callable[[], bool]]
86 checked: dict[str, bool]
88 def __init__(self, state: BaseClasses.CollectionState, player: int):
94 '=': self.has_numeric_eq,
96 'append': self.has_append,
98 'cons-stream': self.has_cons_stream,
99 'decr': self.has_decr,
100 'even?': self.has_even,
101 'filter': self.has_filter,
102 'flat-map': self.has_flat_map,
104 'incr': self.has_incr,
107 'null?': self.has_null,
109 'reverse': self.has_reverse,
110 'stream-car': self.has_car,
111 'stream-cdr': self.has_stream_cdr,
112 'stream-map': self.has_stream_map,
113 'zero?': self.has_zero,
114 '#list-or-cons': self.has_list_or_cons,
115 '#null-or-pair-check': self.has_null_or_pair_check,
116 '#number-or-pair-or-list-check': self.has_number_or_pair_or_list_check,
117 '#pair-or-list-check': self.has_pair_or_list_check,
118 '#recursion': self.has_recursion,
119 '#tree-map': self.has_tree_map,
123 def check(self, *reqs: str):
125 if req in self.checked:
126 if self.checked[req]:
130 self.checked[req] = False
131 check = self.checks.get(req, lambda: self.state.has(req, self.player))
134 self.checked[req] = True
138 return self.state.has_any(["+", "-"], self.player)
141 return self.state.has_any(["<", ">", "<=", ">="], self.player) or self.check('if', 'min', '=')
143 def has_numeric_eq(self):
144 return self.state.has_any(["=", "eq?", "equal?"], self.player) or self.check('if', '<')
147 return self.state.has("and", self.player) or self.check('if') or self.check('or', 'not')
149 def has_accumulate(self):
150 return self.state.has("accumulate", self.player) or self.check('#recursion', '#null-or-pair-check', 'car', 'cdr')
152 def has_append(self):
153 return self.state.has("append", self.player) or self.check('#recursion', '#null-or-pair-check', 'cons', 'car', 'cdr')
156 return self.state.has_any(["car", "stream-car", "list-ref", "stream-ref"], self.player)
158 def has_cons_stream(self):
159 return self.state.has("cons-stream", self.player) or self.check('cons', 'delay')
162 return self.state.has("decr", self.player) or self.check('+')
165 return self.state.has_any(["even?", "odd?"], self.player) or self.check('=', 'remainder')
167 def has_filter(self):
168 return self.state.has("filter", self.player) or self.check('#recursion', '#null-or-pair-check', 'cons', 'car', 'cdr') or self.check('accumulate', 'if', 'cons') or self.check('flat-map', 'if', '#list-or-cons')
170 def has_flat_map(self):
171 return self.state.has("flat-map", self.player) or self.check('accumulate', 'append')
174 return self.state.has_any(["if", "cond"], self.player) or self.check('and', 'or')
177 return self.state.has("decr", self.player) or self.check('+')
180 return self.state.has("map", self.player) or self.check('#recursion', '#null-or-pair-check', 'cons', 'car', 'cdr') or self.check('accumulate', 'cons') or self.check('flat-map', '#list-or-cons')
183 return self.state.has_any(["min", "max"], self.player) or self.check('if', '<')
186 return self.state.has_any(["null?", "eq?", "equal?"], self.player)
189 return self.state.has("or", self.player) or self.check('if') or self.check('and', 'not')
191 def has_reverse(self):
192 return self.state.has("reverse", self.player) or self.check('accumulate', 'cons')
194 def has_stream_cdr(self):
195 return self.state.has("stream-cdr", self.player) or self.check('cdr', 'force')
197 def has_stream_map(self):
198 return self.state.has("stream-map", self.player) or self.check('#recursion', '#null-or-pair-check', 'stream-cons', 'stream-car', 'stream-cdr')
201 return self.state.has("zero?", self.player) or self.check('=') or (self.check('if') and self.state.has_all(['positive?', 'negative?'], self.player))
203 def has_list_or_cons(self):
204 return self.state.has_any(["list", "cons"], self.player)
206 def has_null_or_pair_check(self):
207 return self.check('null?') or self.state.has("pair?", self.player)
209 def has_number_or_pair_or_list_check(self):
210 return self.state.has_any(["number?", "pair?", "list?", "atom?"], self.player)
212 def has_pair_or_list_check(self):
213 return self.state.has_any(["pair?", "list?", "atom?"], self.player)
215 def has_recursion(self):
216 return self.check('if', 'define') # Y combinator is out of logic
218 def has_tree_map(self):
219 return self.check('#recursion', '#pair-or-list-check', 'map')