def load_distribution(self): if self.enable_distribution_file: if self.distribution_file: try: self.distribution = Distribution.from_file( self, self.distribution_file) except FileNotFoundError: logging.getLogger('').warning( "Distribution file not found at %s" % (self.distribution_file)) self.enable_distribution_file = False else: logging.getLogger('').warning( "Plandomizer enabled, but no distribution file provided.") self.enable_distribution_file = False elif self.distribution_file: logging.getLogger('').warning( "Distribution file provided, but using it not enabled. " "Did you mean to set enable_distribution_file?") else: self.distribution = Distribution(self) self.reset_distribution() self.numeric_seed = self.get_numeric_seed()
def load_distribution(self): if self.enable_distribution_file: if self.distribution_file: try: self.distribution = Distribution.from_file( self, self.distribution_file) self.using_distribution_file = True except FileNotFoundError: logging.getLogger('').warning( "Distribution file not found at %s" % (self.distribution_file)) else: logging.getLogger('').warning( "Plandomizer enabled, but no distribution file provided.") elif self.distribution_file: logging.getLogger('').warning( "Distribution file provided, but using it not enabled. " "Did you mean to set enable_distribution_file?") else: self.distribution = Distribution(self) for location in self.disabled_locations: self.distribution.add_location(location, '#Junk') self.numeric_seed = self.get_numeric_seed()
def load_distribution(self): if self.distribution_file is not None and self.distribution_file != '': try: self.distribution = Distribution.from_file( self, self.distribution_file) except FileNotFoundError: logging.getLogger('').warning( "Distribution file not found at %s" % (self.distribution_file)) else: self.distribution = Distribution(self) self.numeric_seed = self.get_numeric_seed()
def load_distribution(self): if self.enable_distribution_file and self.distribution_file is not None and self.distribution_file != '': try: self.distribution = Distribution.from_file(self, self.distribution_file) except FileNotFoundError: logging.getLogger('').warning("Distribution file not found at %s" % (self.distribution_file)) else: self.distribution = Distribution(self) for location in self.disabled_locations: self.distribution.add_location(location, '#Junk') self.numeric_seed = self.get_numeric_seed()
def __init__(self, settings_dict): self.__dict__.update(settings_dict) for info in setting_infos: if info.name not in self.__dict__: self.__dict__[info.name] = info.default if self.world_count < 1: self.world_count = 1 if self.world_count > 255: self.world_count = 255 self.settings_string = self.get_settings_string() self.distribution = Distribution(self) self.update_seed(self.seed)
def __init__(self, settings_dict): self.__dict__.update(settings_dict) for info in setting_infos: if info.name not in self.__dict__: self.__dict__[info.name] = info.default self.settings_string = self.get_settings_string() self.distribution = Distribution(self) self.update_seed(self.seed)
class Settings: def get_settings_display(self): padding = 0 for setting in filter(lambda s: s.shared, setting_infos): padding = max(len(setting.name), padding) padding += 2 output = '' for setting in filter(lambda s: s.shared, setting_infos): name = setting.name + ': ' + ' ' * (padding - len(setting.name)) if setting.type == list: val = ('\n' + (' ' * (padding + 2))).join( self.__dict__[setting.name]) else: val = str(self.__dict__[setting.name]) output += name + val + '\n' return output def get_settings_string(self): bits = [] for setting in filter(lambda s: s.shared and s.bitwidth > 0, setting_infos): value = self.__dict__[setting.name] i_bits = [] if setting.type == bool: i_bits = [1 if value else 0] if setting.type == str: try: index = setting.choice_list.index(value) except ValueError: index = setting.choice_list.index(setting.default) # https://stackoverflow.com/questions/10321978/integer-to-bitfield-as-a-list i_bits = [1 if digit == '1' else 0 for digit in bin(index)[2:]] i_bits.reverse() if setting.type == int: value = int(value) value = value - (setting.gui_params.get('min', 0)) value = int(value / (setting.gui_params.get('step', 1))) value = min(value, (setting.gui_params.get('max', value))) # https://stackoverflow.com/questions/10321978/integer-to-bitfield-as-a-list i_bits = [1 if digit == '1' else 0 for digit in bin(value)[2:]] i_bits.reverse() if setting.type == list: if len(value) > len(setting.choice_list) / 2: value = [ item for item in setting.choice_list if item not in value ] terminal = [1] * setting.bitwidth else: terminal = [0] * setting.bitwidth item_indexes = [] for item in value: try: item_indexes.append(setting.choice_list.index(item)) except ValueError: continue item_indexes.sort() for index in item_indexes: item_bits = [ 1 if digit == '1' else 0 for digit in bin(index + 1)[2:] ] item_bits.reverse() item_bits += [0] * (setting.bitwidth - len(item_bits)) i_bits.extend(item_bits) i_bits.extend(terminal) # pad it i_bits += [0] * (setting.bitwidth - len(i_bits)) bits += i_bits return bit_string_to_text(bits) def update_with_settings_string(self, text): bits = text_to_bit_string(text) for setting in filter(lambda s: s.shared and s.bitwidth > 0, setting_infos): cur_bits = bits[:setting.bitwidth] bits = bits[setting.bitwidth:] value = None if setting.type == bool: value = True if cur_bits[0] == 1 else False if setting.type == str: index = 0 for b in range(setting.bitwidth): index |= cur_bits[b] << b value = setting.choice_list[index] if setting.type == int: value = 0 for b in range(setting.bitwidth): value |= cur_bits[b] << b value = value * setting.gui_params.get('step', 1) value = value + setting.gui_params.get('min', 0) if setting.type == list: value = [] max_index = (1 << setting.bitwidth) - 1 while True: index = 0 for b in range(setting.bitwidth): index |= cur_bits[b] << b if index == 0: break if index == max_index: value = [ item for item in setting.choice_list if item not in value ] break value.append(setting.choice_list[index - 1]) cur_bits = bits[:setting.bitwidth] bits = bits[setting.bitwidth:] self.__dict__[setting.name] = value self.settings_string = self.get_settings_string() self.numeric_seed = self.get_numeric_seed() def get_numeric_seed(self): # salt seed with the settings, and hash to get a numeric seed distribution = json.dumps( self.distribution.to_json(include_output=False), sort_keys=True) full_string = self.settings_string + distribution + __version__ + self.seed return int(hashlib.sha256(full_string.encode('utf-8')).hexdigest(), 16) def sanitize_seed(self): # leave only alphanumeric and some punctuation self.seed = re.sub(r'[^a-zA-Z0-9_-]', '', self.seed, re.UNICODE) def update_seed(self, seed): if seed is None or seed == '': # https://stackoverflow.com/questions/2257441/random-string-generation-with-upper-case-letters-and-digits-in-python self.seed = ''.join( random_choices(string.ascii_uppercase + string.digits, k=10)) else: self.seed = seed self.sanitize_seed() self.numeric_seed = self.get_numeric_seed() def update(self): self.settings_string = self.get_settings_string() self.numeric_seed = self.get_numeric_seed() def load_distribution(self): if self.enable_distribution_file: if self.distribution_file: try: self.distribution = Distribution.from_file( self, self.distribution_file) except FileNotFoundError: logging.getLogger('').warning( "Distribution file not found at %s" % (self.distribution_file)) self.enable_distribution_file = False else: logging.getLogger('').warning( "Plandomizer enabled, but no distribution file provided.") self.enable_distribution_file = False elif self.distribution_file: logging.getLogger('').warning( "Distribution file provided, but using it not enabled. " "Did you mean to set enable_distribution_file?") else: self.distribution = Distribution(self) self.reset_distribution() self.numeric_seed = self.get_numeric_seed() def reset_distribution(self): self.distribution.reset() for location in self.disabled_locations: self.distribution.add_location(location, '#Junk') def check_dependency(self, setting_name, check_random=True): return self.get_dependency(setting_name, check_random) == None def get_dependency(self, setting_name, check_random=True): info = get_setting_info(setting_name) if check_random and 'randomize_key' in info.gui_params and self.__dict__[ info.gui_params['randomize_key']]: return info.disabled_default elif info.dependency != None: return info.disabled_default if info.dependency(self) else None else: return None def remove_disabled(self): for info in setting_infos: if info.dependency != None: new_value = self.get_dependency(info.name) if new_value != None: self.__dict__[info.name] = new_value self._disabled.add(info.name) self.settings_string = self.get_settings_string() self.numeric_seed = self.get_numeric_seed() def resolve_random_settings(self, cosmetic): sorted_infos = list(setting_infos) sort_key = lambda info: 0 if info.dependency is None else 1 sorted_infos.sort(key=sort_key) for info in sorted_infos: # only randomize cosmetics options or non-cosmetic if cosmetic == info.shared: continue if self.check_dependency(info.name, check_random=True): continue if 'randomize_key' in info.gui_params and self.__dict__[ info.gui_params['randomize_key']]: choices, weights = zip(*info.gui_params['distribution']) self.__dict__[info.name] = random_choices(choices, weights=weights)[0] # add the settings as fields, and calculate information based on them def __init__(self, settings_dict): self.__dict__.update(settings_dict) for info in setting_infos: if info.name not in self.__dict__: self.__dict__[info.name] = info.default if self.world_count < 1: self.world_count = 1 if self.world_count > 255: self.world_count = 255 self._disabled = set() self.settings_string = self.get_settings_string() self.distribution = Distribution(self) self.update_seed(self.seed) def to_json(self): return { setting.name: self.__dict__[setting.name] for setting in setting_infos if setting.shared and setting.name not in self._disabled }