mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-23 14:23:32 -07:00
Styling
This commit is contained in:
@@ -8,10 +8,10 @@ class ItemData(typing.NamedTuple):
|
||||
class YachtDiceItem(Item):
|
||||
game: str = "Yacht Dice"
|
||||
|
||||
#the starting index is chosen semi-randomly to be 16871244000
|
||||
# the starting index is chosen semi-randomly to be 16871244000
|
||||
|
||||
item_table = {
|
||||
#victory item, always placed manually at goal location
|
||||
# victory item, always placed manually at goal location
|
||||
"Victory": ItemData(16871244000-1, ItemClassification.progression),
|
||||
|
||||
"Dice": ItemData(16871244000, ItemClassification.progression),
|
||||
@@ -19,7 +19,6 @@ item_table = {
|
||||
"Roll": ItemData(16871244002, ItemClassification.progression),
|
||||
"Roll Fragment": ItemData(16871244003, ItemClassification.progression),
|
||||
|
||||
#"Score Multiplier": ItemData(16871244004, ItemClassification.progression), #not used anymore
|
||||
"Fixed Score Multiplier": ItemData(16871244005, ItemClassification.progression),
|
||||
"Step Score Multiplier": ItemData(16871244006, ItemClassification.progression),
|
||||
|
||||
@@ -57,21 +56,21 @@ item_table = {
|
||||
"Category Five Distinct Dice": ItemData(16871244137, ItemClassification.progression),
|
||||
"Category 4&5 Full House": ItemData(16871244138, ItemClassification.progression),
|
||||
|
||||
#filler items
|
||||
# filler items
|
||||
"Encouragement": ItemData(16871244200, ItemClassification.filler),
|
||||
"Fun Fact": ItemData(16871244201, ItemClassification.filler),
|
||||
"Story Chapter": ItemData(16871244202, ItemClassification.filler),
|
||||
"Good RNG": ItemData(16871244203, ItemClassification.filler),
|
||||
"Bad RNG": ItemData(16871244204, ItemClassification.trap),
|
||||
"Bonus Point": ItemData(16871244205, ItemClassification.useful), #not included in logic
|
||||
"Bonus Point": ItemData(16871244205, ItemClassification.useful), # not included in logic
|
||||
|
||||
#These points are included in the logic and might be necessary to progress.
|
||||
# These points are included in the logic and might be necessary to progress.
|
||||
"1 Point": ItemData(16871244301, ItemClassification.progression_skip_balancing),
|
||||
"10 Points": ItemData(16871244302, ItemClassification.progression),
|
||||
"100 Points": ItemData(16871244303, ItemClassification.progression)
|
||||
}
|
||||
|
||||
#item groups for better hinting
|
||||
# item groups for better hinting
|
||||
item_groups = {
|
||||
"Score Multiplier": {
|
||||
"Step Score Multiplier",
|
||||
|
||||
@@ -14,31 +14,31 @@ class YachtDiceLocation(Location):
|
||||
self.yacht_dice_score = score
|
||||
|
||||
all_locations = {}
|
||||
starting_index = 16871244500 #500 more than the starting index for items
|
||||
starting_index = 16871244500 # 500 more than the starting index for items
|
||||
|
||||
#Function that is called when this file is loaded, which loads in ALL possible locations, score 1 to 1000
|
||||
# Function that is called when this file is loaded, which loads in ALL possible locations, score 1 to 1000
|
||||
def all_locations_fun(max_score):
|
||||
location_table = {}
|
||||
for i in range(max_score+1):
|
||||
location_table[f"{i} score"] = LocData(starting_index+i, "Board", i)
|
||||
return location_table
|
||||
|
||||
#function that loads in all locations necessary for the game, so based on options.
|
||||
#will make sure that goal_score and max_score are included locations
|
||||
# function that loads in all locations necessary for the game, so based on options.
|
||||
# will make sure that goal_score and max_score are included locations
|
||||
def ini_locations(goal_score, max_score, num_locs, dif):
|
||||
scaling = 2 #parameter that determines how many low-score location there are.
|
||||
#need more low-score locations or lower difficulties:
|
||||
scaling = 2 # parameter that determines how many low-score location there are.
|
||||
# need more low-score locations or lower difficulties:
|
||||
if dif == 1:
|
||||
scaling = 3
|
||||
elif dif == 2:
|
||||
scaling = 2.3
|
||||
|
||||
scores = []
|
||||
#the scores follow the function int( 1 + (perc ** scaling) * (max_score-1) )
|
||||
#however, this will have many low values, sometimes repeating.
|
||||
#to avoid repeating scores, hiscore keeps tracks of the highest score location
|
||||
#and the next score will always be at least hiscore + 1
|
||||
#note that curscore is at most max_score-1
|
||||
# the scores follow the function int( 1 + (perc ** scaling) * (max_score-1) )
|
||||
# however, this will have many low values, sometimes repeating.
|
||||
# to avoid repeating scores, hiscore keeps tracks of the highest score location
|
||||
# and the next score will always be at least hiscore + 1
|
||||
# note that curscore is at most max_score-1
|
||||
hiscore = 0
|
||||
for i in range(num_locs - 1):
|
||||
perc = (i/num_locs)
|
||||
@@ -49,7 +49,7 @@ def ini_locations(goal_score, max_score, num_locs, dif):
|
||||
scores += [curscore]
|
||||
|
||||
if goal_score != max_score:
|
||||
#if the goal score is not in the list, find the closest one and make it the goal.
|
||||
# if the goal score is not in the list, find the closest one and make it the goal.
|
||||
if goal_score not in scores:
|
||||
closest_num = min(scores, key=lambda x: abs(x - 500))
|
||||
scores[scores.index(closest_num)] = goal_score
|
||||
|
||||
@@ -15,8 +15,9 @@ class GameDifficulty(Choice):
|
||||
option_hard = 3
|
||||
option_extreme = 4
|
||||
default = 2
|
||||
|
||||
|
||||
class scoreForLastCheck(Range):
|
||||
class ScoreForLastCheck(Range):
|
||||
"""
|
||||
The items in the item pool will always allow you to reach a score of 1000.
|
||||
By default, the last check is also at a score of 1000.
|
||||
@@ -26,8 +27,9 @@ class scoreForLastCheck(Range):
|
||||
range_start = 500
|
||||
range_end = 1000
|
||||
default = 1000
|
||||
|
||||
class scoreForGoal(Range):
|
||||
|
||||
|
||||
class ScoreForGoal(Range):
|
||||
"""
|
||||
This option determines what score you need to reach to finish the game.
|
||||
It cannot be higher than the score for the last check (if it is, it is changed automatically).
|
||||
@@ -37,7 +39,8 @@ class scoreForGoal(Range):
|
||||
range_end = 1000
|
||||
default = 777
|
||||
|
||||
class minimalNumberOfDiceAndRolls(Choice):
|
||||
|
||||
class MinimalNumberOfDiceAndRolls(Choice):
|
||||
"""
|
||||
The minimal number of dice and rolls in the pool.
|
||||
These are guaranteed, unlike the later items.
|
||||
@@ -52,7 +55,8 @@ class minimalNumberOfDiceAndRolls(Choice):
|
||||
option_8_dice_and_2_rolls = 6
|
||||
default = 2
|
||||
|
||||
class numberDiceFragmentsPerDice(Range):
|
||||
|
||||
class NumberDiceFragmentsPerDice(Range):
|
||||
"""
|
||||
Dice can be split into fragments, gathering enough will give you an extra dice.
|
||||
You start with one dice, and there will always be one full dice in the pool.
|
||||
@@ -63,8 +67,9 @@ class numberDiceFragmentsPerDice(Range):
|
||||
range_start = 1
|
||||
range_end = 5
|
||||
default = 4
|
||||
|
||||
class numberRollFragmentsPerRoll(Range):
|
||||
|
||||
|
||||
class NumberRollFragmentsPerRoll(Range):
|
||||
"""
|
||||
Rolls can be split into fragments, gathering enough will give you an extra roll.
|
||||
You start with one roll, and there will always be one full roll in the pool.
|
||||
@@ -75,9 +80,9 @@ class numberRollFragmentsPerRoll(Range):
|
||||
range_start = 1
|
||||
range_end = 5
|
||||
default = 4
|
||||
|
||||
|
||||
class alternativeCategories(Range):
|
||||
|
||||
|
||||
class AlternativeCategories(Range):
|
||||
"""
|
||||
There are 16 default categories, but there are also 16 alternative categories.
|
||||
These alternative categories can be randomly selected to replace the default categories.
|
||||
@@ -89,9 +94,9 @@ class alternativeCategories(Range):
|
||||
range_start = 0
|
||||
range_end = 16
|
||||
default = 0
|
||||
|
||||
|
||||
class chanceOfDice(Range):
|
||||
|
||||
|
||||
class ChanceOfDice(Range):
|
||||
"""
|
||||
The item pool is always filled in such a way that you can reach a score of 1000.
|
||||
Extra progression items are added that will help you on your quest.
|
||||
@@ -103,8 +108,9 @@ class chanceOfDice(Range):
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 5
|
||||
|
||||
class chanceOfRoll(Range):
|
||||
|
||||
|
||||
class ChanceOfRoll(Range):
|
||||
"""
|
||||
With more rolls, you will be able to reach higher scores.
|
||||
"""
|
||||
@@ -113,7 +119,8 @@ class chanceOfRoll(Range):
|
||||
range_end = 100
|
||||
default = 20
|
||||
|
||||
class chanceOfFixedScoreMultiplier(Range):
|
||||
|
||||
class ChanceOfFixedScoreMultiplier(Range):
|
||||
"""
|
||||
Getting a Fixed Score Multiplier will boost all future scores by 10%.
|
||||
"""
|
||||
@@ -121,8 +128,9 @@ class chanceOfFixedScoreMultiplier(Range):
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 30
|
||||
|
||||
class chanceOfStepScoreMultiplier(Range):
|
||||
|
||||
|
||||
class ChanceOfStepScoreMultiplier(Range):
|
||||
"""
|
||||
The Step Score Multiplier boosts your multiplier after every roll by 1%, and resets on sheet reset.
|
||||
So, keep high scoring categories for later to get the most out of them.
|
||||
@@ -132,8 +140,9 @@ class chanceOfStepScoreMultiplier(Range):
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 0
|
||||
|
||||
class chanceOfDoubleCategory(Range):
|
||||
|
||||
|
||||
class ChanceOfDoubleCategory(Range):
|
||||
"""
|
||||
This option allows categories to appear multiple times.
|
||||
Each time you get a category after the first, its score value gets doubled.
|
||||
@@ -142,8 +151,9 @@ class chanceOfDoubleCategory(Range):
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 50
|
||||
|
||||
class chanceOfPoints(Range):
|
||||
|
||||
|
||||
class ChanceOfPoints(Range):
|
||||
"""
|
||||
Getting points gives you... points. You can get 1 point, 10 points, and even 100 points.
|
||||
"""
|
||||
@@ -151,8 +161,9 @@ class chanceOfPoints(Range):
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 20
|
||||
|
||||
class pointsSize(Choice):
|
||||
|
||||
|
||||
class PointsSize(Choice):
|
||||
"""
|
||||
If you choose to add points to the item pool, do you prefer many small points,
|
||||
medium-size points, a few larger points, or a mix of them?
|
||||
@@ -163,8 +174,9 @@ class pointsSize(Choice):
|
||||
option_large = 3
|
||||
option_mix = 4
|
||||
default = 2
|
||||
|
||||
class minimizeExtraItems(Choice):
|
||||
|
||||
|
||||
class MinimizeExtraItems(Choice):
|
||||
"""
|
||||
Besides necessary items, Yacht Dice has extra useful/filler items in the item pool.
|
||||
It is possible however to decrease the number of extra items in multiplayer games.
|
||||
@@ -175,8 +187,9 @@ class minimizeExtraItems(Choice):
|
||||
option_no_dont = 1
|
||||
option_yes_please = 2
|
||||
default = 1
|
||||
|
||||
class addExtraPoints(Choice):
|
||||
|
||||
|
||||
class AddExtraPoints(Choice):
|
||||
"""
|
||||
Yacht Dice typically has space for extra items.
|
||||
If there is space, would you like bonus points shuffled in the item pool?
|
||||
@@ -191,8 +204,9 @@ class addExtraPoints(Choice):
|
||||
option_sure = 2
|
||||
option_never = 3
|
||||
default = 2
|
||||
|
||||
class addStoryChapters(Choice):
|
||||
|
||||
|
||||
class AddStoryChapters(Choice):
|
||||
"""
|
||||
Yacht Dice typically has space for more items.
|
||||
If there is space, would you like story chapters shuffled in the item pool?
|
||||
@@ -207,8 +221,9 @@ class addStoryChapters(Choice):
|
||||
option_sure = 2
|
||||
option_never = 3
|
||||
default = 3
|
||||
|
||||
class whichStory(Choice):
|
||||
|
||||
|
||||
class WhichStory(Choice):
|
||||
"""
|
||||
The most important part of Yacht Dice is the narrative.
|
||||
If you choose to
|
||||
@@ -225,8 +240,9 @@ class whichStory(Choice):
|
||||
option_a_rollin_rhyme_adventure = 6
|
||||
option_random_story = -1
|
||||
default = -1
|
||||
|
||||
class allowManual(Choice):
|
||||
|
||||
|
||||
class AllowManual(Choice):
|
||||
"""
|
||||
Yacht Dice allows players to roll IRL dice.
|
||||
By sending "manual" in the chat, an input field appears where you can type your dice rolls.
|
||||
@@ -237,32 +253,33 @@ class allowManual(Choice):
|
||||
option_yes_allow = 1
|
||||
option_no_dont_allow = 2
|
||||
default = 1
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class YachtDiceOptions(PerGameCommonOptions):
|
||||
game_difficulty: gameDifficulty
|
||||
score_for_last_check: scoreForLastCheck
|
||||
score_for_goal: scoreForGoal
|
||||
game_difficulty: GameDifficulty
|
||||
score_for_last_check: ScoreForLastCheck
|
||||
score_for_goal: ScoreForGoal
|
||||
|
||||
minimal_number_of_dice_and_rolls: minimalNumberOfDiceAndRolls
|
||||
number_of_dice_fragments_per_dice: numberDiceFragmentsPerDice
|
||||
number_of_roll_fragments_per_roll: numberRollFragmentsPerRoll
|
||||
minimal_number_of_dice_and_rolls: MinimalNumberOfDiceAndRolls
|
||||
number_of_dice_fragments_per_dice: NumberDiceFragmentsPerDice
|
||||
number_of_roll_fragments_per_roll: NumberRollFragmentsPerRoll
|
||||
|
||||
alternative_categories: alternativeCategories
|
||||
alternative_categories: AlternativeCategories
|
||||
|
||||
#the following options determine what extra items are shuffled into the pool:
|
||||
weight_of_dice: chanceOfDice
|
||||
weight_of_roll: chanceOfRoll
|
||||
weight_of_fixed_score_multiplier: chanceOfFixedScoreMultiplier
|
||||
weight_of_step_score_multiplier: chanceOfStepScoreMultiplier
|
||||
weight_of_double_category: chanceOfDoubleCategory
|
||||
weight_of_points: chanceOfPoints
|
||||
points_size: pointsSize
|
||||
weight_of_dice: ChanceOfDice
|
||||
weight_of_roll: ChanceOfRoll
|
||||
weight_of_fixed_score_multiplier: ChanceOfFixedScoreMultiplier
|
||||
weight_of_step_score_multiplier: ChanceOfStepScoreMultiplier
|
||||
weight_of_double_category: ChanceOfDoubleCategory
|
||||
weight_of_points: ChanceOfPoints
|
||||
points_size: PointsSize
|
||||
|
||||
minimize_extra_items: minimizeExtraItems
|
||||
add_bonus_points: addExtraPoints
|
||||
add_story_chapters: addStoryChapters
|
||||
which_story: whichStory
|
||||
minimize_extra_items: MinimizeExtraItems
|
||||
add_bonus_points: AddExtraPoints
|
||||
add_story_chapters: AddStoryChapters
|
||||
which_story: WhichStory
|
||||
|
||||
allow_manual_input: allowManual
|
||||
allow_manual_input: AllowManual
|
||||
|
||||
@@ -4,7 +4,7 @@ from .YachtWeights import yacht_weights
|
||||
import math
|
||||
from collections import defaultdict
|
||||
|
||||
#List of categories, and the name of the logic class associated with it
|
||||
# List of categories, and the name of the logic class associated with it
|
||||
category_mappings = {
|
||||
"Category Ones": "Ones",
|
||||
"Category Twos": "Twos",
|
||||
@@ -41,21 +41,21 @@ category_mappings = {
|
||||
"Category 4&5 Full House": "FourAndFiveFullHouse"
|
||||
}
|
||||
|
||||
#This class adds logic to the apworld.
|
||||
#In short, we ran a simulation for every possible combination of dice and rolls you can have, per category.
|
||||
#This simulation has a good strategy for locking dice.
|
||||
#This gives rise to an approximate discrete distribution per category.
|
||||
#We calculate the distribution of the total score.
|
||||
#We then pick a correct percentile to reflect the correct score that should be in logic.
|
||||
#The score is logic is *much* lower than the actual maximum reachable score.
|
||||
# This class adds logic to the apworld.
|
||||
# In short, we ran a simulation for every possible combination of dice and rolls you can have, per category.
|
||||
# This simulation has a good strategy for locking dice.
|
||||
# This gives rise to an approximate discrete distribution per category.
|
||||
# We calculate the distribution of the total score.
|
||||
# We then pick a correct percentile to reflect the correct score that should be in logic.
|
||||
# The score is logic is *much* lower than the actual maximum reachable score.
|
||||
|
||||
|
||||
class Category:
|
||||
def __init__(self, name, quantity = 1):
|
||||
self.name = name
|
||||
self.quantity = quantity #how many times you have the category
|
||||
self.quantity = quantity # how many times you have the category
|
||||
|
||||
#return mean score of a category
|
||||
# return mean score of a category
|
||||
def mean_score(self, num_dice, num_rolls):
|
||||
if num_dice == 0 or num_rolls == 0:
|
||||
return 0
|
||||
@@ -66,10 +66,10 @@ class Category:
|
||||
|
||||
|
||||
def extract_progression(state, player, options):
|
||||
#method to obtain a list of what items the player has.
|
||||
#this includes categories, dice, rolls and score multiplier etc.
|
||||
# method to obtain a list of what items the player has.
|
||||
# this includes categories, dice, rolls and score multiplier etc.
|
||||
|
||||
if player == "state_is_a_list": #the state variable is just a list with the names of the items
|
||||
if player == "state_is_a_list": # the state variable is just a list with the names of the items
|
||||
number_of_dice = (
|
||||
state.count("Dice")
|
||||
+ state.count("Dice Fragment") // options.number_of_dice_fragments_per_dice.value
|
||||
@@ -89,7 +89,7 @@ def extract_progression(state, player, options):
|
||||
extra_points_in_logic = state.count("1 Point")
|
||||
extra_points_in_logic += state.count("10 Points") * 10
|
||||
extra_points_in_logic += state.count("100 Points") * 100
|
||||
else: #state is an Archipelago object, so we need state.count(..., player)
|
||||
else: # state is an Archipelago object, so we need state.count(..., player)
|
||||
number_of_dice = (
|
||||
state.count("Dice", player)
|
||||
+ state.count("Dice Fragment", player) // options.number_of_dice_fragments_per_dice.value
|
||||
@@ -113,23 +113,23 @@ def extract_progression(state, player, options):
|
||||
return [categories, number_of_dice, number_of_rerolls,
|
||||
number_of_fixed_mults * 0.1, number_of_step_mults * 0.01, extra_points_in_logic]
|
||||
|
||||
#We will store the results of this function as it is called often for the same parameters.
|
||||
# We will store the results of this function as it is called often for the same parameters.
|
||||
yachtdice_cache = {}
|
||||
|
||||
#Function that returns the feasible score in logic based on items obtained.
|
||||
# Function that returns the feasible score in logic based on items obtained.
|
||||
def dice_simulation_strings(categories, num_dice, num_rolls, fixed_mult, step_mult, diff):
|
||||
tup = tuple([tuple(sorted([c.name+str(c.quantity) for c in categories])),
|
||||
num_dice, num_rolls, fixed_mult, step_mult, diff]) #identifier
|
||||
|
||||
#if already computed, return the result
|
||||
# if already computed, return the result
|
||||
if tup in yachtdice_cache.keys():
|
||||
return yachtdice_cache[tup]
|
||||
|
||||
#sort categories because for the step multiplier, you will want low-scoring categories first
|
||||
# sort categories because for the step multiplier, you will want low-scoring categories first
|
||||
categories.sort(key=lambda category: category.mean_score(num_dice, num_rolls))
|
||||
|
||||
#function to add two discrete distribution.
|
||||
#defaultdict is a dict where you don't need to check if an id is present, you can just use += (lot faster)
|
||||
# function to add two discrete distribution.
|
||||
# defaultdict is a dict where you don't need to check if an id is present, you can just use += (lot faster)
|
||||
def add_distributions(dist1, dist2):
|
||||
combined_dist = defaultdict(float)
|
||||
for val1, prob1 in dist1.items():
|
||||
@@ -137,8 +137,8 @@ def dice_simulation_strings(categories, num_dice, num_rolls, fixed_mult, step_mu
|
||||
combined_dist[val1 + val2] += prob1 * prob2
|
||||
return dict(combined_dist)
|
||||
|
||||
#function to take the maximum of "times" i.i.d. dist1.
|
||||
#(I have tried using defaultdict here too but this made it slower.)
|
||||
# function to take the maximum of "times" i.i.d. dist1.
|
||||
# (I have tried using defaultdict here too but this made it slower.)
|
||||
def max_dist(dist1, mults):
|
||||
new_dist = {0: 1}
|
||||
for mult in mults:
|
||||
@@ -157,7 +157,7 @@ def dice_simulation_strings(categories, num_dice, num_rolls, fixed_mult, step_mu
|
||||
|
||||
return new_dist
|
||||
|
||||
#Returns percentile value of a distribution.
|
||||
# Returns percentile value of a distribution.
|
||||
def percentile_distribution(dist, percentile):
|
||||
sorted_values = sorted(dist.keys())
|
||||
cumulative_prob = 0
|
||||
@@ -172,13 +172,13 @@ def dice_simulation_strings(categories, num_dice, num_rolls, fixed_mult, step_mu
|
||||
# Return the first value if percentile is lower than all probabilities
|
||||
return prev_val if prev_val is not None else sorted_values[0]
|
||||
|
||||
#parameters for logic.
|
||||
#perc_return is, per difficulty, the percentages of total score it returns (it averages out the values)
|
||||
#diff_divide determines how many shots the logic gets per category. Lower = more shots.
|
||||
# parameters for logic.
|
||||
# perc_return is, per difficulty, the percentages of total score it returns (it averages out the values)
|
||||
# diff_divide determines how many shots the logic gets per category. Lower = more shots.
|
||||
perc_return = [[0], [0.1, 0.5], [0.3, 0.7], [0.55, 0.85], [0.85, 0.95]][diff]
|
||||
diff_divide = [0, 9, 7, 3, 2][diff]
|
||||
|
||||
#calculate total distribution
|
||||
# calculate total distribution
|
||||
total_dist = {0: 1}
|
||||
for j in range(len(categories)):
|
||||
if num_dice == 0 or num_rolls == 0:
|
||||
@@ -191,14 +191,14 @@ def dice_simulation_strings(categories, num_dice, num_rolls, fixed_mult, step_mu
|
||||
|
||||
cat_mult = 2 ** (categories[j].quantity-1)
|
||||
|
||||
#for higher difficulties, the simulation gets multiple tries for categories.
|
||||
# for higher difficulties, the simulation gets multiple tries for categories.
|
||||
max_tries = j // diff_divide
|
||||
mults = [(1 + fixed_mult + step_mult * ii) * cat_mult for ii in range(max(0,j - max_tries), j+1)]
|
||||
dist = max_dist(dist, mults)
|
||||
|
||||
total_dist = add_distributions(total_dist, dist)
|
||||
|
||||
#save result into the cache, then return it
|
||||
# save result into the cache, then return it
|
||||
outcome = sum([percentile_distribution(total_dist, perc) for perc in perc_return]) / len(perc_return)
|
||||
yachtdice_cache[tup] = max(5, math.floor(outcome))
|
||||
return yachtdice_cache[tup]
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -56,17 +56,17 @@ class YachtDiceWorld(World):
|
||||
self.itempool = []
|
||||
self.precollected = []
|
||||
|
||||
#number of dice and rolls in the pull
|
||||
# number of dice and rolls in the pull
|
||||
ind_dice_rolls = self.options.minimal_number_of_dice_and_rolls.value
|
||||
|
||||
num_of_dice = [0, 2, 5, 5, 6, 7, 8][ind_dice_rolls]
|
||||
num_of_rolls = [0, 2, 3, 5, 4, 3, 2][ind_dice_rolls]
|
||||
|
||||
#amount of dice and roll fragments needed to get a dice or roll
|
||||
# amount of dice and roll fragments needed to get a dice or roll
|
||||
frags_per_dice = self.options.number_of_dice_fragments_per_dice.value
|
||||
frags_per_roll = self.options.number_of_roll_fragments_per_roll.value
|
||||
|
||||
#count number of plando items not from pool, we need extra locations for them
|
||||
# count number of plando items not from pool, we need extra locations for them
|
||||
self.extra_plando_items = 0
|
||||
|
||||
for plando_setting in self.multiworld.plando_items[self.player]:
|
||||
@@ -80,9 +80,9 @@ class YachtDiceWorld(World):
|
||||
# Shuffle the list to randomize the order
|
||||
self.multiworld.random.shuffle(categorylist)
|
||||
|
||||
#A list of all possible categories.
|
||||
#Every entry in the list has two categories, one 'default' category and one 'alt'.
|
||||
#You get either of the two for every entry, so a total of 16 unique categories.
|
||||
# A list of all possible categories.
|
||||
# Every entry in the list has two categories, one 'default' category and one 'alt'.
|
||||
# You get either of the two for every entry, so a total of 16 unique categories.
|
||||
all_categories = [
|
||||
["Category Choice", "Category Double Threes and Fours"],
|
||||
["Category Inverse Choice", "Category Quadruple Ones and Twos"],
|
||||
@@ -102,7 +102,7 @@ class YachtDiceWorld(World):
|
||||
["Category Yacht", "Category 4&5 Full House"]
|
||||
]
|
||||
|
||||
#categories used in this game.
|
||||
# categories used in this game.
|
||||
possible_categories = []
|
||||
|
||||
for index, cats in enumerate(all_categories):
|
||||
@@ -118,37 +118,37 @@ class YachtDiceWorld(World):
|
||||
self.precollected += ["Roll"]
|
||||
self.precollected += ["Dice"]
|
||||
|
||||
#if one fragment per dice, just add "Dice" objects
|
||||
# if one fragment per dice, just add "Dice" objects
|
||||
if frags_per_dice == 1:
|
||||
self.itempool += ["Dice"] * (num_of_dice-1) # minus one because one is in start inventory
|
||||
else:
|
||||
self.itempool += ["Dice"] #always add a full dice to make generation easier (will be early)
|
||||
self.itempool += ["Dice"] # always add a full dice to make generation easier (will be early)
|
||||
self.itempool += ["Dice Fragment"] * (frags_per_dice * (num_of_dice-2))
|
||||
|
||||
#if one fragment per roll, just add "Roll" objects
|
||||
# if one fragment per roll, just add "Roll" objects
|
||||
if frags_per_roll == 1:
|
||||
self.itempool += ["Roll"] * (num_of_rolls-1) #minus one because one is in start inventory
|
||||
self.itempool += ["Roll"] * (num_of_rolls-1) # minus one because one is in start inventory
|
||||
else:
|
||||
self.itempool += ["Roll"] #always add a full roll to make generation easier (will be early)
|
||||
self.itempool += ["Roll"] # always add a full roll to make generation easier (will be early)
|
||||
self.itempool += ["Roll Fragment"] * (frags_per_roll * (num_of_rolls-2))
|
||||
|
||||
already_items = len(self.itempool) + self.extra_plando_items
|
||||
|
||||
#Yacht Dice needs extra filler items so it doesn't get stuck in generation.
|
||||
#For now, we calculate the number of extra items we'll need later.
|
||||
# Yacht Dice needs extra filler items so it doesn't get stuck in generation.
|
||||
# For now, we calculate the number of extra items we'll need later.
|
||||
if self.options.minimize_extra_items.value:
|
||||
extraPercentage = max(0.1, 0.8 - self.multiworld.players / 10)
|
||||
else:
|
||||
extraPercentage = 0.7
|
||||
extra_locations_needed = max(10, math.ceil(already_items * extraPercentage))
|
||||
|
||||
#max score is the value of the last check. Goal score is the score needed to 'finish' the game
|
||||
# max score is the value of the last check. Goal score is the score needed to 'finish' the game
|
||||
self.max_score = self.options.score_for_last_check.value
|
||||
self.goal_score = min(self.max_score, self.options.score_for_goal.value)
|
||||
|
||||
#Yacht Dice adds items into the pool until a score of at least 1000 is reached.
|
||||
#the yaml contains weights, which determine how likely it is that specific items get added.
|
||||
#If all weights are 0, some of them will be made to be non-zero later.
|
||||
# Yacht Dice adds items into the pool until a score of at least 1000 is reached.
|
||||
# the yaml contains weights, which determine how likely it is that specific items get added.
|
||||
# If all weights are 0, some of them will be made to be non-zero later.
|
||||
weights = [
|
||||
self.options.weight_of_dice.value,
|
||||
self.options.weight_of_roll.value,
|
||||
@@ -158,39 +158,39 @@ class YachtDiceWorld(World):
|
||||
self.options.weight_of_points.value
|
||||
]
|
||||
|
||||
#if the player wants extra rolls or dice, fill the pool with fragments until close to an extra roll/dice
|
||||
# if the player wants extra rolls or dice, fill the pool with fragments until close to an extra roll/dice
|
||||
if weights[0] > 0 and frags_per_dice > 1:
|
||||
self.itempool += ["Dice Fragment"] * (frags_per_dice - 1)
|
||||
if weights[1] > 0 and frags_per_roll > 1:
|
||||
self.itempool += ["Roll Fragment"] * (frags_per_roll - 1)
|
||||
|
||||
#calibrate the weights, since the impact of each of the items is different
|
||||
# calibrate the weights, since the impact of each of the items is different
|
||||
weights[0] = weights[0] / 5 * frags_per_dice
|
||||
weights[1] = weights[1] / 5 * frags_per_roll
|
||||
|
||||
extra_points_added = 0
|
||||
multipliers_added = 0
|
||||
|
||||
#Keep adding items until a score of 1000 is in logic
|
||||
# Keep adding items until a score of 1000 is in logic
|
||||
while dice_simulation(self.itempool + self.precollected, "state_is_a_list", self.options) < 1000:
|
||||
all_items = self.itempool + self.precollected
|
||||
dice_fragments_in_pool = all_items.count("Dice") * frags_per_dice + all_items.count("Dice Fragment")
|
||||
if dice_fragments_in_pool + 1 >= 9 * frags_per_dice:
|
||||
weights[0] = 0 #don't allow >=9 dice
|
||||
weights[0] = 0 # don't allow >=9 dice
|
||||
roll_fragments_in_pool = all_items.count("Roll") * frags_per_roll + all_items.count("Roll Fragment")
|
||||
if roll_fragments_in_pool + 1 >= 6 * frags_per_roll:
|
||||
weights[1] = 0 # don't allow >= 6 rolls
|
||||
weights[1] = 0 # don't allow >= 6 rolls
|
||||
|
||||
#Don't allow too many multipliers
|
||||
# Don't allow too many multipliers
|
||||
if multipliers_added > 50:
|
||||
weights[2] = 0
|
||||
weights[3] = 0
|
||||
|
||||
#Don't allow too many extra points
|
||||
# Don't allow too many extra points
|
||||
if extra_points_added > 300:
|
||||
weights[5] = 0
|
||||
|
||||
#if all weights are zero, allow to add fixed score multiplier, double category, points.
|
||||
# if all weights are zero, allow to add fixed score multiplier, double category, points.
|
||||
if sum(weights) == 0:
|
||||
if multipliers_added <= 50:
|
||||
weights[2] = 1
|
||||
@@ -198,7 +198,7 @@ class YachtDiceWorld(World):
|
||||
if extra_points_added <= 300:
|
||||
weights[5] = 1
|
||||
|
||||
#Next, add the appropriate item. We'll slightly alter weights to avoid too many of the same item
|
||||
# Next, add the appropriate item. We'll slightly alter weights to avoid too many of the same item
|
||||
which_item_to_add = self.multiworld.random.choices([0, 1, 2, 3, 4, 5], weights = weights)[0]
|
||||
if which_item_to_add == 0:
|
||||
if frags_per_dice == 1:
|
||||
@@ -226,16 +226,16 @@ class YachtDiceWorld(World):
|
||||
weights[4] /= 1.1
|
||||
elif which_item_to_add == 5:
|
||||
score_dist = self.options.points_size.value
|
||||
probs = [1,0,0]
|
||||
probs = [1, 0, 0]
|
||||
if score_dist == 1:
|
||||
probs = [0.9,0.08,0]
|
||||
probs = [0.9, 0.08, 0]
|
||||
if score_dist == 2:
|
||||
probs = [0,1,0]
|
||||
probs = [0, 1, 0]
|
||||
if score_dist == 3:
|
||||
probs = [0,0.3,0.7]
|
||||
probs = [0, 0.3, 0.7]
|
||||
if score_dist == 4:
|
||||
probs = [0.3,0.4,0.3]
|
||||
c = self.multiworld.random.choices([0,1,2], weights = probs)[0]
|
||||
probs = [0.3, 0.4, 0.3]
|
||||
c = self.multiworld.random.choices([0, 1, 2], weights = probs)[0]
|
||||
if c == 0:
|
||||
self.itempool += ["1 Point"]
|
||||
extra_points_added += 1
|
||||
@@ -253,56 +253,56 @@ class YachtDiceWorld(World):
|
||||
else:
|
||||
raise Exception("Invalid index when adding new items in Yacht Dice")
|
||||
|
||||
#count the number of locations in the game.
|
||||
already_items = len(self.itempool) + self.extra_plando_items + 1 #+1 because of Victory item
|
||||
# count the number of locations in the game.
|
||||
already_items = len(self.itempool) + self.extra_plando_items + 1 # +1 because of Victory item
|
||||
|
||||
#We need to add more filler/useful items if there are many items in the pool to guarantee successful generation
|
||||
# We need to add more filler/useful items if there are many items in the pool to guarantee successful generation
|
||||
extra_locations_needed += (already_items - 45) // 15
|
||||
self.number_of_locations = already_items + extra_locations_needed
|
||||
|
||||
# From here, we will count the number of items in the self.itempool, and add useful/filler items to the pool,
|
||||
# making sure not to exceed the number of locations.
|
||||
|
||||
#first, we flood the entire pool with extra points (useful), if that setting is chosen.
|
||||
# first, we flood the entire pool with extra points (useful), if that setting is chosen.
|
||||
if self.options.add_bonus_points.value == 1: #all of the extra points
|
||||
already_items = len(self.itempool) + self.extra_plando_items + 1
|
||||
self.itempool += ["Bonus Point"] * min(self.number_of_locations - already_items, 100)
|
||||
|
||||
#second, we flood the entire pool with story chapters (filler), if that setting is chosen.
|
||||
if self.options.add_story_chapters.value == 1: #all of the story chapters
|
||||
# second, we flood the entire pool with story chapters (filler), if that setting is chosen.
|
||||
if self.options.add_story_chapters.value == 1: # all of the story chapters
|
||||
already_items = len(self.itempool) + self.extra_plando_items + 1
|
||||
number_of_items = min(self.number_of_locations - already_items, 100)
|
||||
number_of_items = (number_of_items // 10) * 10 #story chapters always come in multiples of 10
|
||||
number_of_items = (number_of_items // 10) * 10 # story chapters always come in multiples of 10
|
||||
self.itempool += ["Story Chapter"] * number_of_items
|
||||
|
||||
#add some extra points (useful)
|
||||
if self.options.add_bonus_points.value == 2: #add extra points if wanted
|
||||
# add some extra points (useful)
|
||||
if self.options.add_bonus_points.value == 2: # add extra points if wanted
|
||||
already_items = len(self.itempool) + self.extra_plando_items + 1
|
||||
self.itempool += ["Bonus Point"] * min(self.number_of_locations - already_items, 10)
|
||||
|
||||
#add some story chapters (filler)
|
||||
if self.options.add_story_chapters.value == 2: #add extra points if wanted
|
||||
# add some story chapters (filler)
|
||||
if self.options.add_story_chapters.value == 2: # add extra points if wanted
|
||||
already_items = len(self.itempool) + self.extra_plando_items + 1
|
||||
if self.number_of_locations - already_items >= 10:
|
||||
self.itempool += ["Story Chapter"] * 10
|
||||
|
||||
#add some more extra points if there is still room
|
||||
# add some more extra points if there is still room
|
||||
if self.options.add_bonus_points.value == 2:
|
||||
already_items = len(self.itempool) + self.extra_plando_items + 1
|
||||
self.itempool += ["Bonus Point"] * min(self.number_of_locations - already_items, 10)
|
||||
|
||||
#add some encouragements filler-items if there is still room
|
||||
# add some encouragements filler-items if there is still room
|
||||
already_items = len(self.itempool) + self.extra_plando_items + 1
|
||||
self.itempool += ["Encouragement"] * min(self.number_of_locations - already_items, 5)
|
||||
|
||||
#add some fun facts filler-items if there is still room
|
||||
# add some fun facts filler-items if there is still room
|
||||
already_items = len(self.itempool) + self.extra_plando_items + 1
|
||||
self.itempool += ["Fun Fact"] * min(self.number_of_locations - already_items, 5)
|
||||
|
||||
#finally, add some "Good RNG" and "Bad RNG" items to complete the item pool
|
||||
#these items are filler and do not do anything.
|
||||
# finally, add some "Good RNG" and "Bad RNG" items to complete the item pool
|
||||
# these items are filler and do not do anything.
|
||||
|
||||
#probability of Good and Bad rng, based on difficulty for fun :)
|
||||
# probability of Good and Bad rng, based on difficulty for fun :)
|
||||
p = 1.1 - 0.25 * self.options.game_difficulty.value
|
||||
already_items = len(self.itempool) + self.extra_plando_items + 1
|
||||
self.itempool += self.multiworld.random.choices(
|
||||
@@ -311,17 +311,17 @@ class YachtDiceWorld(World):
|
||||
k=self.number_of_locations - already_items
|
||||
)
|
||||
|
||||
#we are done adding items. Now because of the last step, number of items should be number of locations
|
||||
# we are done adding items. Now because of the last step, number of items should be number of locations
|
||||
already_items = len(self.itempool) + self.extra_plando_items + 1
|
||||
if already_items != self.number_of_locations:
|
||||
raise Exception(f"[Yacht Dice] Number in self.itempool is not number of locations "
|
||||
f"{already_items} {self.number_of_locations}.")
|
||||
|
||||
#add precollected items using push_precollected. Items in self.itempool get created in create_items
|
||||
# add precollected items using push_precollected. Items in self.itempool get created in create_items
|
||||
for item in self.precollected:
|
||||
self.multiworld.push_precollected(self.create_item(item))
|
||||
|
||||
#make sure one dice and one roll is early, so that you will have 2 dice and 2 rolls soon
|
||||
# make sure one dice and one roll is early, so that you will have 2 dice and 2 rolls soon
|
||||
self.multiworld.early_items[self.player]["Dice"] = 1
|
||||
self.multiworld.early_items[self.player]["Roll"] = 1
|
||||
|
||||
@@ -329,41 +329,41 @@ class YachtDiceWorld(World):
|
||||
self.multiworld.itempool += [self.create_item(name) for name in self.itempool]
|
||||
|
||||
def create_regions(self):
|
||||
#call the ini_locations function, that generates locations based on the inputs.
|
||||
# call the ini_locations function, that generates locations based on the inputs.
|
||||
location_table, goal_index = ini_locations(self.goal_score, self.max_score, self.number_of_locations,
|
||||
self.options.game_difficulty.value)
|
||||
|
||||
#simple menu-board construction
|
||||
# simple menu-board construction
|
||||
menu = Region("Menu", self.player, self.multiworld)
|
||||
board = Region("Board", self.player, self.multiworld)
|
||||
|
||||
#add locations to board, one for every location in the location_table
|
||||
# add locations to board, one for every location in the location_table
|
||||
board.locations = [YachtDiceLocation(self.player, loc_name, loc_data.score, loc_data.id, board)
|
||||
for loc_name, loc_data in location_table.items() if loc_data.region == board.name]
|
||||
|
||||
#which index of all locations should have the Victory item.
|
||||
# which index of all locations should have the Victory item.
|
||||
|
||||
# Add the victory item to the correct location.
|
||||
# The website declares that the game is complete when the victory item is obtained.
|
||||
board.locations[goal_index].place_locked_item(self.create_item("Victory"))
|
||||
|
||||
#these will be slot_data input
|
||||
# these will be slot_data input
|
||||
self.goal_score = board.locations[goal_index].yacht_dice_score
|
||||
self.max_score = board.locations[-1].yacht_dice_score
|
||||
|
||||
#add the regions
|
||||
# add the regions
|
||||
connection = Entrance(self.player, "New Board", menu)
|
||||
menu.exits.append(connection)
|
||||
connection.connect(board)
|
||||
self.multiworld.regions += [menu, board]
|
||||
|
||||
def set_rules(self):
|
||||
#set rules per location, and add the rule for beating the game
|
||||
# set rules per location, and add the rule for beating the game
|
||||
set_yacht_rules(self.multiworld, self.player, self.options)
|
||||
set_yacht_completion_rules(self.multiworld, self.player)
|
||||
|
||||
def fill_slot_data(self):
|
||||
#make slot data, which consists of yachtdice_data, options, and some other variables.
|
||||
# make slot data, which consists of yachtdice_data, options, and some other variables.
|
||||
yacht_dice_data = self._get_yachtdice_data()
|
||||
yacht_dice_options = self.options.as_dict(
|
||||
"game_difficulty",
|
||||
@@ -386,7 +386,7 @@ class YachtDiceWorld(World):
|
||||
"which_story",
|
||||
"allow_manual_input"
|
||||
)
|
||||
slot_data = {**yacht_dice_data, **yacht_dice_options} #combine the two
|
||||
slot_data = {**yacht_dice_data, **yacht_dice_options} # combine the two
|
||||
slot_data["goal_score"] = self.goal_score
|
||||
slot_data["last_check_score"] = self.max_score
|
||||
slot_data["ap_world_version"] = self.ap_world_version
|
||||
@@ -397,6 +397,7 @@ class YachtDiceWorld(World):
|
||||
item = YachtDiceItem(name, item_data.classification, item_data.code, self.player)
|
||||
return item
|
||||
|
||||
# We overwrite these function to monitor when states have changed. See also dice_simulation in Rules.py
|
||||
def collect(self, state: CollectionState, item: Item) -> bool:
|
||||
change = super().collect(state, item)
|
||||
if change:
|
||||
|
||||
Reference in New Issue
Block a user