Witness: Changes in response to Beta run 1 (#494)

Co-authored-by: metzner <unconfigured@null.spigotmc.org>
This commit is contained in:
NewSoupVi 2022-05-09 07:20:28 +02:00 committed by GitHub
parent 1e592b4681
commit 9ab7c8d9e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 100 additions and 21 deletions

View File

@ -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
}

View File

@ -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:

View File

@ -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):

View File

@ -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]

View File

@ -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()]