| 
									
										
										
										
											2024-11-29 12:25:01 -08:00
										 |  |  | from collections import Counter | 
					
						
							|  |  |  | from collections.abc import Mapping | 
					
						
							| 
									
										
										
										
											2024-01-14 06:48:30 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | from BaseClasses import CollectionState | 
					
						
							| 
									
										
										
										
											2024-01-14 06:48:30 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | from zilliandomizer.logic_components.items import Item, items | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | from zilliandomizer.logic_components.locations import Location | 
					
						
							|  |  |  | from zilliandomizer.randomizer import Randomizer | 
					
						
							| 
									
										
										
										
											2024-01-14 06:48:30 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | from .item import ZillionItem | 
					
						
							|  |  |  | from .id_maps import item_name_to_id | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | zz_empty = items[4] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # TODO: unit tests for these | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def set_randomizer_locs(cs: CollectionState, p: int, zz_r: Randomizer) -> int: | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     sync up zilliandomizer locations with archipelago locations | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     returns a hash of the player and of the set locations with their items | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2024-01-14 06:48:30 -08:00
										 |  |  |     from . import ZillionWorld | 
					
						
							| 
									
										
										
										
											2022-10-31 21:41:21 -05:00
										 |  |  |     z_world = cs.multiworld.worlds[p] | 
					
						
							| 
									
										
										
										
											2024-01-14 06:48:30 -08:00
										 |  |  |     assert isinstance(z_world, ZillionWorld) | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     _hash = p | 
					
						
							| 
									
										
										
										
											2024-01-14 06:48:30 -08:00
										 |  |  |     for z_loc in z_world.my_locations: | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |         zz_name = z_loc.zz_loc.name | 
					
						
							|  |  |  |         zz_item = z_loc.item.zz_item \ | 
					
						
							|  |  |  |             if isinstance(z_loc.item, ZillionItem) and z_loc.item.player == p \ | 
					
						
							|  |  |  |             else zz_empty | 
					
						
							|  |  |  |         zz_r.locations[zz_name].item = zz_item | 
					
						
							| 
									
										
										
										
											2023-05-30 20:56:23 -07:00
										 |  |  |         _hash += (hash(zz_name) * (z_loc.zz_loc.req.gun + 2)) ^ hash(zz_item) | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     return _hash | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 12:25:01 -08:00
										 |  |  | def item_counts(cs: CollectionState, p: int) -> tuple[tuple[str, int], ...]: | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     the zilliandomizer items that player p has collected | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ((item_name, count), (item_name, count), ...) | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-11-24 00:35:37 +01:00
										 |  |  |     return tuple((item_name, cs.count(item_name, p)) for item_name in item_name_to_id) | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 12:25:01 -08:00
										 |  |  | _cache_miss: tuple[None, frozenset[Location]] = (None, frozenset()) | 
					
						
							| 
									
										
										
										
											2024-09-18 12:09:47 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionLogicCache: | 
					
						
							| 
									
										
										
										
											2024-11-29 12:25:01 -08:00
										 |  |  |     _cache: dict[int, tuple[Counter[str], frozenset[Location]]] | 
					
						
							| 
									
										
										
										
											2024-09-18 12:09:47 -07:00
										 |  |  |     """ `{ hash: (counter_from_prog_items, accessible_zz_locations) }` """ | 
					
						
							|  |  |  |     _player: int | 
					
						
							|  |  |  |     _zz_r: Randomizer | 
					
						
							|  |  |  |     _id_to_zz_item: Mapping[int, Item] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, player: int, zz_r: Randomizer, id_to_zz_item: Mapping[int, Item]) -> None: | 
					
						
							|  |  |  |         self._cache = {} | 
					
						
							|  |  |  |         self._player = player | 
					
						
							|  |  |  |         self._zz_r = zz_r | 
					
						
							|  |  |  |         self._id_to_zz_item = id_to_zz_item | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-29 12:25:01 -08:00
										 |  |  |     def cs_to_zz_locs(self, cs: CollectionState) -> frozenset[Location]: | 
					
						
							| 
									
										
										
										
											2024-09-18 12:09:47 -07:00
										 |  |  |         """
 | 
					
						
							|  |  |  |         given an Archipelago `CollectionState`, | 
					
						
							|  |  |  |         returns frozenset of accessible zilliandomizer locations | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  |         # caching this function because it would be slow | 
					
						
							|  |  |  |         _hash = set_randomizer_locs(cs, self._player, self._zz_r) | 
					
						
							|  |  |  |         counts = item_counts(cs, self._player) | 
					
						
							|  |  |  |         _hash += hash(counts) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         cntr, locs = self._cache.get(_hash, _cache_miss) | 
					
						
							|  |  |  |         if cntr == cs.prog_items[self._player]: | 
					
						
							|  |  |  |             # print("cache hit") | 
					
						
							|  |  |  |             return locs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # print("cache miss") | 
					
						
							| 
									
										
										
										
											2024-11-29 12:25:01 -08:00
										 |  |  |         have_items: list[Item] = [] | 
					
						
							| 
									
										
										
										
											2024-09-18 12:09:47 -07:00
										 |  |  |         for name, count in counts: | 
					
						
							|  |  |  |             have_items.extend([self._id_to_zz_item[item_name_to_id[name]]] * count) | 
					
						
							|  |  |  |         # have_req is the result of converting AP CollectionState to zilliandomizer collection state | 
					
						
							|  |  |  |         have_req = self._zz_r.make_ability(have_items) | 
					
						
							|  |  |  |         # print(f"{have_req=}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # This `get_locations` is where the core of the logic comes in. | 
					
						
							|  |  |  |         # It takes a zilliandomizer collection state (a set of the abilities that I have) | 
					
						
							|  |  |  |         # and returns list of all the zilliandomizer locations I can access with those abilities. | 
					
						
							|  |  |  |         tr = frozenset(self._zz_r.get_locations(have_req)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # save result in cache | 
					
						
							|  |  |  |         self._cache[_hash] = (cs.prog_items[self._player].copy(), tr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return tr |