| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | import os | 
					
						
							|  |  |  | from Utils import cache_argsless | 
					
						
							| 
									
										
										
										
											2022-05-09 07:20:28 +02:00
										 |  |  | from itertools import accumulate | 
					
						
							|  |  |  | from typing import * | 
					
						
							|  |  |  | from fractions import Fraction | 
					
						
							| 
									
										
										
										
											2023-03-03 00:08:24 +01:00
										 |  |  | from collections import Counter | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def make_warning_string(any_j: bool, any_u: bool, any_d: bool, all_j: bool, all_u: bool, all_d: bool) -> str: | 
					
						
							|  |  |  |     warning_string = "" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if any_j: | 
					
						
							|  |  |  |         if all_j: | 
					
						
							|  |  |  |             warning_string += "all " | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             warning_string += "some " | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         warning_string += "junk" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if any_u or any_d: | 
					
						
							|  |  |  |         if warning_string: | 
					
						
							|  |  |  |             warning_string += " and " | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if all_u: | 
					
						
							|  |  |  |             warning_string += "all " | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             warning_string += "some " | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         warning_string += "usefuls" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if any_d: | 
					
						
							|  |  |  |         warning_string += ", including " | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if all_d: | 
					
						
							|  |  |  |             warning_string += "all " | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             warning_string += "some " | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         warning_string += "non-essential door items" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return warning_string | 
					
						
							| 
									
										
										
										
											2022-05-09 07:20:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def best_junk_to_add_based_on_weights(weights: Dict[Any, Fraction], created_junk: Dict[Any, int]): | 
					
						
							|  |  |  |     min_error = ("", 2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for junk_name, instances in created_junk.items(): | 
					
						
							|  |  |  |         new_dist = created_junk.copy() | 
					
						
							|  |  |  |         new_dist[junk_name] += 1 | 
					
						
							|  |  |  |         new_dist_length = sum(new_dist.values()) | 
					
						
							|  |  |  |         new_dist = {key: Fraction(value/1)/new_dist_length for key, value in new_dist.items()} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         errors = {key: abs(new_dist[key] - weights[key]) for key in created_junk.keys()} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         new_min_error = max(errors.values()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if min_error[1] > new_min_error: | 
					
						
							|  |  |  |             min_error = (junk_name, new_min_error) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return min_error[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def weighted_list(weights: Dict[Any, Fraction], length): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Example: | 
					
						
							|  |  |  |         weights = {A: 0.3, B: 0.3, C: 0.4} | 
					
						
							|  |  |  |         length = 10 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         returns: [A, A, A, B, B, B, C, C, C, C] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Makes sure to match length *exactly*, might approximate as a result | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     vals = accumulate(map(lambda x: x * length, weights.values()), lambda x, y: x + y) | 
					
						
							|  |  |  |     output_list = [] | 
					
						
							|  |  |  |     for k, v in zip(weights.keys(), vals): | 
					
						
							|  |  |  |         while len(output_list) < v: | 
					
						
							|  |  |  |             output_list.append(k) | 
					
						
							|  |  |  |     return output_list | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def define_new_region(region_string): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Returns a region object by parsing a line in the logic file | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     region_string = region_string[:-1] | 
					
						
							|  |  |  |     line_split = region_string.split(" - ") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     region_name_full = line_split.pop(0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     region_name_split = region_name_full.split(" (") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     region_name = region_name_split[0] | 
					
						
							|  |  |  |     region_name_simple = region_name_split[1][:-1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     options = set() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for _ in range(len(line_split) // 2): | 
					
						
							|  |  |  |         connected_region = line_split.pop(0) | 
					
						
							|  |  |  |         corresponding_lambda = line_split.pop(0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         options.add( | 
					
						
							|  |  |  |             (connected_region, parse_lambda(corresponding_lambda)) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     region_obj = { | 
					
						
							|  |  |  |         "name": region_name, | 
					
						
							|  |  |  |         "shortName": region_name_simple, | 
					
						
							|  |  |  |         "panels": set() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return region_obj, options | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def parse_lambda(lambda_string): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Turns a lambda String literal like this: a | b & c | 
					
						
							|  |  |  |     into a set of sets like this: {{a}, {b, c}} | 
					
						
							|  |  |  |     The lambda has to be in DNF. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if lambda_string == "True": | 
					
						
							|  |  |  |         return frozenset([frozenset()]) | 
					
						
							|  |  |  |     split_ands = set(lambda_string.split(" | ")) | 
					
						
							|  |  |  |     lambda_set = frozenset({frozenset(a.split(" & ")) for a in split_ands}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return lambda_set | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  | class lazy(object): | 
					
						
							|  |  |  |     def __init__(self, func, name=None): | 
					
						
							|  |  |  |         self.func = func | 
					
						
							|  |  |  |         self.name = name if name is not None else func.__name__ | 
					
						
							|  |  |  |         self.__doc__ = func.__doc__ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __get__(self, instance, class_): | 
					
						
							|  |  |  |         if instance is None: | 
					
						
							|  |  |  |             res = self.func(class_) | 
					
						
							|  |  |  |             setattr(class_, self.name, res) | 
					
						
							|  |  |  |             return res | 
					
						
							|  |  |  |         res = self.func(instance) | 
					
						
							|  |  |  |         setattr(instance, self.name, res) | 
					
						
							|  |  |  |         return res | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-16 03:04:45 +02:00
										 |  |  | def get_adjustment_file(adjustment_file): | 
					
						
							| 
									
										
										
										
											2022-04-29 00:42:11 +02:00
										 |  |  |     path = os.path.join(os.path.dirname(__file__), adjustment_file) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with open(path) as f: | 
					
						
							| 
									
										
										
										
											2022-05-09 07:20:28 +02:00
										 |  |  |         return [line.strip() for line in f.readlines()] | 
					
						
							| 
									
										
										
										
											2022-06-16 03:04:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_disable_unrandomized_list(): | 
					
						
							| 
									
										
										
										
											2022-07-17 12:56:22 +02:00
										 |  |  |     return get_adjustment_file("settings/Disable_Unrandomized.txt") | 
					
						
							| 
									
										
										
										
											2022-06-16 03:04:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_early_utm_list(): | 
					
						
							| 
									
										
										
										
											2022-07-17 12:56:22 +02:00
										 |  |  |     return get_adjustment_file("settings/Early_UTM.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_symbol_shuffle_list(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/Symbol_Shuffle.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_door_panel_shuffle_list(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/Door_Panel_Shuffle.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_doors_simple_list(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/Doors_Simple.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_doors_complex_list(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/Doors_Complex.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_doors_max_list(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/Doors_Max.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_laser_shuffle(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/Laser_Shuffle.txt") | 
					
						
							| 
									
										
										
										
											2022-10-09 04:13:52 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_audio_logs(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/Audio_Logs.txt") | 
					
						
							| 
									
										
										
										
											2023-02-01 21:18:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_ep_all_individual(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/EP_Shuffle/EP_All.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_ep_obelisks(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/EP_Shuffle/EP_Sides.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_ep_easy(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/EP_Shuffle/EP_Easy.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_ep_no_eclipse(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/EP_Shuffle/EP_NoEclipse.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_ep_no_caves(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/EP_Shuffle/EP_NoCavesEPs.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_ep_no_mountain(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/EP_Shuffle/EP_NoMountainEPs.txt") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @cache_argsless | 
					
						
							|  |  |  | def get_ep_no_videos(): | 
					
						
							|  |  |  |     return get_adjustment_file("settings/EP_Shuffle/EP_Videos.txt") |