Witness: Changes in response to Beta run 1 (#494)
Co-authored-by: metzner <unconfigured@null.spigotmc.org>
This commit is contained in:
		
							parent
							
								
									1e592b4681
								
							
						
					
					
						commit
						9ab7c8d9e5
					
				|  | @ -1,6 +1,6 @@ | |||
| from typing import Dict | ||||
| from BaseClasses import MultiWorld | ||||
| from Options import Toggle, DefaultOnToggle, Option | ||||
| from Options import Toggle, DefaultOnToggle, Option, Range | ||||
| 
 | ||||
| 
 | ||||
| # class HardMode(Toggle): | ||||
|  | @ -30,8 +30,8 @@ class ShuffleVaultBoxes(Toggle): | |||
| 
 | ||||
| 
 | ||||
| class ShuffleUncommonLocations(Toggle): | ||||
|     """Adds the following checks to the pool: | ||||
|     Mountaintop River Shape, Tutorial Patio Floor, Theater Videos""" | ||||
|     """Adds some optional puzzles that are somewhat difficult or out of the way. | ||||
|     Examples: Mountaintop River Shape, Tutorial Patio Floor, Theater Videos""" | ||||
|     display_name = "Shuffle Uncommon Locations" | ||||
| 
 | ||||
| 
 | ||||
|  | @ -46,6 +46,14 @@ class ChallengeVictoryCondition(Toggle): | |||
|     display_name = "Victory on beating the Challenge" | ||||
| 
 | ||||
| 
 | ||||
| class TrapPercentage(Range): | ||||
|     """Replaces junk items with traps, at the specified rate.""" | ||||
|     display_name = "Trap Percentage" | ||||
|     range_start = 0 | ||||
|     range_end = 100 | ||||
|     default = 20 | ||||
| 
 | ||||
| 
 | ||||
| the_witness_options: Dict[str, Option] = { | ||||
|     # "hard_mode": HardMode, | ||||
|     # "unlock_symbols": UnlockSymbols, | ||||
|  | @ -54,7 +62,8 @@ the_witness_options: Dict[str, Option] = { | |||
|     "shuffle_vault_boxes": ShuffleVaultBoxes, | ||||
|     "shuffle_uncommon": ShuffleUncommonLocations, | ||||
|     "shuffle_hard": ShuffleHardLocations, | ||||
|     "challenge_victory": ChallengeVictoryCondition | ||||
|     "challenge_victory": ChallengeVictoryCondition, | ||||
|     "trap_percentage": TrapPercentage | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -370,7 +370,7 @@ Jungle (Jungle) - Main Island - True: | |||
| 
 | ||||
| Outside Jungle River (River) - Main Island - True - Jungle - 0x337FA: | ||||
| 0x17CAA (Rhombic Avoid to Monastery Garden) - True - Environment | ||||
| 0x15ADD (Rhombic Avoid Vault) - True - Environment | ||||
| 0x15ADD (Vault) - True - Environment & Black/White Squares & Dots | ||||
| 0x03702 (Vault Box) - 0x15ADD - True | ||||
| 
 | ||||
| Outside Bunker (Bunker) - Main Island - True - Inside Bunker - 0x0A079: | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ from .items import WitnessItem, StaticWitnessItems, WitnessPlayerItems | |||
| from .rules import set_rules | ||||
| from .regions import WitnessRegions | ||||
| from .Options import is_option_enabled, the_witness_options | ||||
| from .utils import best_junk_to_add_based_on_weights | ||||
| 
 | ||||
| 
 | ||||
| class WitnessWebWorld(WebWorld): | ||||
|  | @ -50,6 +51,8 @@ class WitnessWorld(World): | |||
|         self.items = WitnessPlayerItems(self.locat, self.world, self.player, self.player_logic) | ||||
|         self.regio = WitnessRegions(self.locat) | ||||
| 
 | ||||
|         self.junk_items_created = {key: 0 for key in self.items.JUNK_WEIGHTS.keys()} | ||||
| 
 | ||||
|     def generate_basic(self): | ||||
|         # Generate item pool | ||||
|         pool = [] | ||||
|  | @ -69,13 +72,10 @@ class WitnessWorld(World): | |||
|         pool.remove(items_by_name[random_good_item]) | ||||
| 
 | ||||
|         # Put in junk items to fill the rest | ||||
|         junk_pool = self.items.JUNK_WEIGHTS.copy() | ||||
|         junk_pool = self.world.random.choices( | ||||
|             list(junk_pool.keys()), weights=list(junk_pool.values()), | ||||
|             k=len(self.locat.CHECK_LOCATION_TABLE) - len(pool) - len(self.locat.EVENT_LOCATION_TABLE) - 1 | ||||
|         ) | ||||
|         junk_size = len(self.locat.CHECK_LOCATION_TABLE) - len(pool) - len(self.locat.EVENT_LOCATION_TABLE) - 1 | ||||
| 
 | ||||
|         pool += [self.create_item(junk) for junk in junk_pool] | ||||
|         for i in range(0, junk_size): | ||||
|             pool.append(self.create_item(self.get_filler_item_name())) | ||||
| 
 | ||||
|         # Tie Event Items to Event Locations (e.g. Laser Activations) | ||||
|         for event_location in self.locat.EVENT_LOCATION_TABLE: | ||||
|  | @ -118,10 +118,12 @@ class WitnessWorld(World): | |||
|         new_item.trap = item.trap | ||||
|         return new_item | ||||
| 
 | ||||
|     def get_filler_item_name(self) -> str:  # Used ny itemlinks | ||||
|         junk_pool = self.items.JUNK_WEIGHTS.copy() | ||||
|     def get_filler_item_name(self) -> str:  # Used by itemlinks | ||||
|         item = best_junk_to_add_based_on_weights(self.items.JUNK_WEIGHTS, self.junk_items_created) | ||||
| 
 | ||||
|         return self.world.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()))[0] | ||||
|         self.junk_items_created[item] += 1 | ||||
| 
 | ||||
|         return item | ||||
| 
 | ||||
| 
 | ||||
| class WitnessLocation(Location): | ||||
|  |  | |||
|  | @ -6,7 +6,8 @@ from typing import Dict, NamedTuple, Optional | |||
| 
 | ||||
| from BaseClasses import Item, MultiWorld | ||||
| from . import StaticWitnessLogic, WitnessPlayerLocations, WitnessPlayerLogic | ||||
| from .Options import is_option_enabled | ||||
| from .Options import get_option_value, is_option_enabled | ||||
| from fractions import Fraction | ||||
| 
 | ||||
| 
 | ||||
| class ItemData(NamedTuple): | ||||
|  | @ -33,12 +34,19 @@ class StaticWitnessItems: | |||
| 
 | ||||
|     ALL_ITEM_TABLE: Dict[str, ItemData] = {} | ||||
| 
 | ||||
|     JUNK_WEIGHTS = { | ||||
|         "Speed Boost": 1, | ||||
|         "Slowness": 0.8, | ||||
|         "Power Surge": 0.2, | ||||
|     # These should always add up to 1!!! | ||||
|     BONUS_WEIGHTS = { | ||||
|         "Speed Boost": Fraction(1, 1), | ||||
|     } | ||||
| 
 | ||||
|     # These should always add up to 1!!! | ||||
|     TRAP_WEIGHTS = {    | ||||
|         "Slowness": Fraction(8, 10), | ||||
|         "Power Surge": Fraction(2, 10), | ||||
|     } | ||||
| 
 | ||||
|     ALL_JUNK_ITEMS = set(BONUS_WEIGHTS.keys()) | set(TRAP_WEIGHTS.keys()) | ||||
| 
 | ||||
|     def __init__(self): | ||||
|         item_tab = dict() | ||||
| 
 | ||||
|  | @ -91,8 +99,28 @@ class WitnessPlayerItems: | |||
|             self.EVENT_ITEM_TABLE[location] = ItemData(None, True, True) | ||||
|             self.ITEM_TABLE[location] = ItemData(None, True, True) | ||||
| 
 | ||||
|         trap_percentage = get_option_value(world, player, "trap_percentage") | ||||
| 
 | ||||
|         self.JUNK_WEIGHTS = dict() | ||||
| 
 | ||||
|         if trap_percentage != 0: | ||||
|             # I'm sure there must be some super "pythonic" way of doing this :D | ||||
| 
 | ||||
|             for trap_name, trap_weight in StaticWitnessItems.TRAP_WEIGHTS.items(): | ||||
|                 self.JUNK_WEIGHTS[trap_name] = (trap_weight * trap_percentage) / 100 | ||||
| 
 | ||||
|         if trap_percentage != 100: | ||||
|             for bonus_name, bonus_weight in StaticWitnessItems.BONUS_WEIGHTS.items(): | ||||
|                 self.JUNK_WEIGHTS[bonus_name] = (bonus_weight * (100 - trap_percentage)) / 100 | ||||
| 
 | ||||
|         self.JUNK_WEIGHTS = { | ||||
|             key: value for (key, value) | ||||
|             in StaticWitnessItems.JUNK_WEIGHTS.items() | ||||
|             in self.JUNK_WEIGHTS.items() | ||||
|             if key in self.ITEM_TABLE.keys() | ||||
|         } | ||||
| 
 | ||||
|         # JUNK_WEIGHTS will add up to 1 if the boosts weights and the trap weights each add up to 1 respectively. | ||||
| 
 | ||||
|         for junk_item in StaticWitnessItems.ALL_JUNK_ITEMS: | ||||
|             if junk_item not in self.JUNK_WEIGHTS.keys(): | ||||
|                 del self.ITEM_TABLE[junk_item] | ||||
|  |  | |||
|  | @ -1,5 +1,45 @@ | |||
| import os | ||||
| from Utils import cache_argsless | ||||
| from itertools import accumulate | ||||
| from typing import * | ||||
| from fractions import Fraction | ||||
| 
 | ||||
| 
 | ||||
| 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 | ||||
| 
 | ||||
| 
 | ||||
| def define_new_region(region_string): | ||||
|  | @ -55,4 +95,4 @@ def get_disable_unrandomized_list(): | |||
|     path = os.path.join(os.path.dirname(__file__), adjustment_file) | ||||
| 
 | ||||
|     with open(path) as f: | ||||
|         return [line.strip() for line in f.readlines()] | ||||
|         return [line.strip() for line in f.readlines()] | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue