ruff styling, fix

This commit is contained in:
spinerak
2024-06-08 00:51:04 +02:00
parent 6250923f5e
commit da820cc165
7 changed files with 191 additions and 156 deletions

View File

@@ -1,24 +1,28 @@
from BaseClasses import Item, ItemClassification
import typing
from BaseClasses import Item, ItemClassification
class ItemData(typing.NamedTuple):
code: typing.Optional[int]
classification: ItemClassification
class YachtDiceItem(Item):
game: str = "Yacht Dice"
# the starting index is chosen semi-randomly to be 16871244000
item_table = {
# victory item, always placed manually at goal location
"Victory": ItemData(16871244000-1, ItemClassification.progression),
"Victory": ItemData(16871244000 - 1, ItemClassification.progression),
"Dice": ItemData(16871244000, ItemClassification.progression),
"Dice Fragment": ItemData(16871244001, ItemClassification.progression),
"Roll": ItemData(16871244002, ItemClassification.progression),
"Roll Fragment": ItemData(16871244003, ItemClassification.progression),
"Fixed Score Multiplier": ItemData(16871244005, ItemClassification.progression),
"Step Score Multiplier": ItemData(16871244006, ItemClassification.progression),
@@ -38,7 +42,7 @@ item_table = {
"Category Large Straight": ItemData(16871244116, ItemClassification.progression),
"Category Full House": ItemData(16871244117, ItemClassification.progression),
"Category Yacht": ItemData(16871244118, ItemClassification.progression),
"Category Distincts": ItemData(16871244123, ItemClassification.progression),
"Category Two times Ones": ItemData(16871244124, ItemClassification.progression),
"Category Half of Sixes": ItemData(16871244125, ItemClassification.progression),
@@ -55,7 +59,7 @@ item_table = {
"Category 2-1-2 Consecutive": ItemData(16871244136, ItemClassification.progression),
"Category Five Distinct Dice": ItemData(16871244137, ItemClassification.progression),
"Category 4&5 Full House": ItemData(16871244138, ItemClassification.progression),
# filler items
"Encouragement": ItemData(16871244200, ItemClassification.filler),
"Fun Fact": ItemData(16871244201, ItemClassification.filler),
@@ -63,7 +67,7 @@ item_table = {
"Good RNG": ItemData(16871244203, ItemClassification.filler),
"Bad RNG": ItemData(16871244204, ItemClassification.trap),
"Bonus Point": ItemData(16871244205, ItemClassification.useful), # not included in logic
# 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),
@@ -77,10 +81,10 @@ item_groups = {
"Fixed Score Multiplier"
},
"Categories": {
"Category Ones",
"Category Twos",
"Category Threes",
"Category Fours",
"Category Ones",
"Category Twos",
"Category Threes",
"Category Fours",
"Category Fives",
"Category Sixes",
"Category Choice",

View File

@@ -1,11 +1,14 @@
from BaseClasses import Location
import typing
from BaseClasses import Location
class LocData(typing.NamedTuple):
id: int
region: str
score: int
class YachtDiceLocation(Location):
game: str = "Yacht Dice"
@@ -13,23 +16,26 @@ class YachtDiceLocation(Location):
super().__init__(player, name, address, parent)
self.yacht_dice_score = score
all_locations = {}
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
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)
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
def ini_locations(goal_score, max_score, num_locs, dif):
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:
if dif == 1:
scaling = 3
scaling = 3
elif dif == 2:
scaling = 2.3
@@ -41,26 +47,27 @@ def ini_locations(goal_score, max_score, num_locs, dif):
# note that curscore is at most max_score-1
hiscore = 0
for i in range(num_locs - 1):
perc = (i/num_locs)
curscore = int(1 + (perc ** scaling) * (max_score-2))
perc = (i / num_locs)
curscore = int(1 + (perc ** scaling) * (max_score - 2))
if curscore <= hiscore:
curscore = hiscore + 1
hiscore = curscore
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 goal_score not in scores:
closest_num = min(scores, key=lambda x: abs(x - 500))
scores[scores.index(closest_num)] = goal_score
scores += [max_score]
location_table = {f"{score} score": LocData(starting_index + score, "Board", score) for score in scores}
return location_table, scores.index(goal_score)
lookup_id_to_name: typing.Dict[int, str] = {data.id: item_name for item_name, data in all_locations.items() if data.id}
# we need to run this function to initialize all scores from 1 to 1000, even though not all are used
all_locations = all_locations_fun(1000)
all_locations = all_locations_fun(1000)

View File

@@ -1,6 +1,8 @@
from Options import Choice, Range, PerGameCommonOptions, OptionGroup
from dataclasses import dataclass
from Options import Choice, OptionGroup, PerGameCommonOptions, Range
class GameDifficulty(Choice):
"""
Difficulty. This option determines how difficult the scores are to achieve.
@@ -16,7 +18,7 @@ class GameDifficulty(Choice):
option_extreme = 4
default = 2
class ScoreForLastCheck(Range):
"""
The items in the item pool will always allow you to reach a score of 1000.
@@ -28,7 +30,7 @@ class ScoreForLastCheck(Range):
range_end = 1000
default = 1000
class ScoreForGoal(Range):
"""
This option determines what score you need to reach to finish the game.
@@ -93,7 +95,7 @@ class AlternativeCategories(Range):
display_name = "Number of alternative categories"
range_start = 0
range_end = 16
default = 0
default = 0
class ChanceOfDice(Range):
@@ -107,7 +109,7 @@ class ChanceOfDice(Range):
display_name = "Weight of adding Dice"
range_start = 0
range_end = 100
default = 5
default = 5
class ChanceOfRoll(Range):
@@ -117,7 +119,7 @@ class ChanceOfRoll(Range):
display_name = "Weight of adding Roll"
range_start = 0
range_end = 100
default = 20
default = 20
class ChanceOfFixedScoreMultiplier(Range):
@@ -127,7 +129,7 @@ class ChanceOfFixedScoreMultiplier(Range):
display_name = "Weight of adding Fixed Score Multiplier"
range_start = 0
range_end = 100
default = 30
default = 30
class ChanceOfStepScoreMultiplier(Range):
@@ -139,7 +141,7 @@ class ChanceOfStepScoreMultiplier(Range):
display_name = "Weight of adding Step Score Multiplier"
range_start = 0
range_end = 100
default = 0
default = 0
class ChanceOfDoubleCategory(Range):
@@ -160,7 +162,7 @@ class ChanceOfPoints(Range):
display_name = "Weight of adding Points"
range_start = 0
range_end = 100
default = 20
default = 20
class PointsSize(Choice):
@@ -186,7 +188,7 @@ class MinimizeExtraItems(Choice):
display_name = "Minimize extra items"
option_no_dont = 1
option_yes_please = 2
default = 1
default = 1
class AddExtraPoints(Choice):
@@ -259,16 +261,16 @@ class YachtDiceOptions(PerGameCommonOptions):
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
alternative_categories: AlternativeCategories
allow_manual_input: AllowManual
#the following options determine what extra items are shuffled into the pool:
# 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
@@ -276,18 +278,19 @@ class YachtDiceOptions(PerGameCommonOptions):
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
yd_option_groups = [
OptionGroup("Extra progression items", [
ChanceOfDice, ChanceOfRoll, ChanceOfFixedScoreMultiplier, ChanceOfStepScoreMultiplier,
ChanceOfDoubleCategory, ChanceOfPoints, PointsSize
]),
OptionGroup("Other items", [
MinimizeExtraItems, AddExtraPoints, AddStoryChapters, WhichStory
])

View File

@@ -1,9 +1,12 @@
from worlds.generic.Rules import set_rule
from BaseClasses import MultiWorld
from .YachtWeights import yacht_weights
import math
from collections import defaultdict
from BaseClasses import MultiWorld
from worlds.generic.Rules import set_rule
from .YachtWeights import yacht_weights
# List of categories, and the name of the logic class associated with it
category_mappings = {
"Category Ones": "Ones",
@@ -22,10 +25,10 @@ category_mappings = {
"Category Large Straight": "LargeStraight",
"Category Full House": "FullHouse",
"Category Yacht": "Yacht",
"Category Distincts": "Distincts",
"Category Two times Ones": "Twos", #same weights as twos category
"Category Half of Sixes": "Threes", #same weights as threes category
"Category Two times Ones": "Twos", # same weights as twos category
"Category Half of Sixes": "Threes", # same weights as threes category
"Category Twos and Threes": "TwosAndThrees",
"Category Sum of Odds": "SumOfOdds",
"Category Sum of Evens": "SumOfEvens",
@@ -51,7 +54,7 @@ category_mappings = {
class Category:
def __init__(self, name, quantity = 1):
def __init__(self, name, quantity=1):
self.name = name
self.quantity = quantity # how many times you have the category
@@ -60,22 +63,22 @@ class Category:
if num_dice == 0 or num_rolls == 0:
return 0
mean_score = 0
for key in yacht_weights[self.name, min(8,num_dice), min(8,num_rolls)]:
mean_score += key*yacht_weights[self.name, min(8,num_dice), min(8,num_rolls)][key]/100000
for key in yacht_weights[self.name, min(8, num_dice), min(8, num_rolls)]:
mean_score += key * yacht_weights[self.name, min(8, num_dice), min(8, num_rolls)][key] / 100000
return mean_score * self.quantity
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.
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")
+ state.count("Dice Fragment") // options.number_of_dice_fragments_per_dice.value
)
number_of_rerolls = (
state.count("Roll")
state.count("Roll")
+ state.count("Roll Fragment") // options.number_of_roll_fragments_per_roll.value
)
number_of_fixed_mults = state.count("Fixed Score Multiplier")
@@ -83,17 +86,17 @@ def extract_progression(state, player, options):
categories = []
for category_name, category_value in category_mappings.items():
if state.count(category_name) >= 1:
categories += [Category(category_value, state.count(category_name))]
categories += [Category(category_value, state.count(category_name))]
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", player)
+ state.count("Dice Fragment", player) // options.number_of_dice_fragments_per_dice.value
)
number_of_rerolls = (
state.count("Roll", player)
state.count("Roll", player)
+ state.count("Roll Fragment", player) // options.number_of_roll_fragments_per_roll.value
)
number_of_fixed_mults = state.count("Fixed Score Multiplier", player)
@@ -101,26 +104,29 @@ def extract_progression(state, player, options):
categories = []
for category_name, category_value in category_mappings.items():
if state.count(category_name, player) >= 1:
categories += [Category(category_value, state.count(category_name, player))]
categories += [Category(category_value, state.count(category_name, player))]
extra_points_in_logic = state.count("1 Point", player)
extra_points_in_logic += state.count("10 Points", player) * 10
extra_points_in_logic += state.count("100 Points", player) * 100
return [categories, number_of_dice, number_of_rerolls,
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.
yachtdice_cache = {}
# 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
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 tup in yachtdice_cache.keys():
return yachtdice_cache[tup]
# 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))
@@ -132,7 +138,7 @@ def dice_simulation_strings(categories, num_dice, num_rolls, fixed_mult, step_mu
for val2, prob2 in dist2.items():
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.)
def max_dist(dist1, mults):
@@ -144,13 +150,13 @@ def dice_simulation_strings(categories, num_dice, num_rolls, fixed_mult, step_mu
for val2, prob2 in dist1.items():
new_val = int(max(val1, val2 * mult))
new_prob = prob1 * prob2
# Update the probability for the new value
if new_val in new_dist:
new_dist[new_val] += new_prob
else:
new_dist[new_val] = new_prob
return new_dist
# Returns percentile value of a distribution.
@@ -158,16 +164,16 @@ def dice_simulation_strings(categories, num_dice, num_rolls, fixed_mult, step_mu
sorted_values = sorted(dist.keys())
cumulative_prob = 0
prev_val = None
for val in sorted_values:
prev_val = val
cumulative_prob += dist[val]
if cumulative_prob >= percentile:
return prev_val # Return the value before reaching the desired percentile
# Return the first value if percentile is lower than all probabilities
return prev_val if prev_val is not None else sorted_values[0]
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.
@@ -180,25 +186,26 @@ def dice_simulation_strings(categories, num_dice, num_rolls, fixed_mult, step_mu
if num_dice == 0 or num_rolls == 0:
dist = {0: 100000}
else:
dist = yacht_weights[categories[j].name, min(8,num_dice), min(8,num_rolls)].copy()
dist = yacht_weights[categories[j].name, min(8, num_dice), min(8, num_rolls)].copy()
for key in dist.keys():
dist[key] /= 100000
cat_mult = 2 ** (categories[j].quantity-1)
cat_mult = 2 ** (categories[j].quantity - 1)
# 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)]
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
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]
# Returns the feasible score that one can reach with the current state, options and difficulty.
def dice_simulation(state, player, options):
if player == "state_is_a_list":
@@ -206,7 +213,7 @@ def dice_simulation(state, player, options):
return dice_simulation_strings(
categories, num_dice, num_rolls, fixed_mult, step_mult, options.game_difficulty.value
) + expoints
if state.prog_items[player]["state_is_fresh"] == 0:
state.prog_items[player]["state_is_fresh"] = 1
categories, num_dice, num_rolls, fixed_mult, step_mult, expoints = extract_progression(state, player, options)
@@ -226,7 +233,7 @@ def set_yacht_rules(world: MultiWorld, player: int, options):
player=player:
dice_simulation(state, player, options) >= curscore)
# Sets rules on completion condition
def set_yacht_completion_rules(world: MultiWorld, player: int):
world.completion_condition[player] = lambda state: state.has("Victory", player)

File diff suppressed because one or more lines are too long

View File

@@ -1,11 +1,13 @@
import math
from BaseClasses import Region, Entrance, Item, Tutorial, CollectionState
from .Items import YachtDiceItem, item_table, item_groups
from BaseClasses import CollectionState, Entrance, Item, Region, Tutorial
from worlds.AutoWorld import WebWorld, World
from .Items import YachtDiceItem, item_groups, item_table
from .Locations import YachtDiceLocation, all_locations, ini_locations
from .Options import YachtDiceOptions, yd_option_groups
from .Rules import set_yacht_rules, set_yacht_completion_rules, dice_simulation
from worlds.AutoWorld import World, WebWorld
from .Rules import dice_simulation, set_yacht_completion_rules, set_yacht_rules
class YachtDiceWeb(WebWorld):
@@ -18,7 +20,7 @@ class YachtDiceWeb(WebWorld):
"setup/en",
["Spineraks"]
)]
option_groups = yd_option_groups
@@ -33,13 +35,13 @@ class YachtDiceWorld(World):
"""
game: str = "Yacht Dice"
options_dataclass = YachtDiceOptions
web = YachtDiceWeb()
item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = {name: data.id for name, data in all_locations.items()}
item_name_groups = item_groups
ap_world_version = "2.0.4"
@@ -52,36 +54,36 @@ class YachtDiceWorld(World):
"player_id": self.player,
"race": self.multiworld.is_race,
}
# In generate early, we fill the item-pool, then determine the number of locations, and add filler items.
def generate_early(self):
def generate_early(self):
self.itempool = []
self.precollected = []
self.precollected = []
# 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
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
self.extra_plando_items = 0
for plando_setting in self.multiworld.plando_items[self.player]:
if plando_setting.get("from_pool", False) is False:
self.extra_plando_items += sum(value for value in plando_setting["items"].values())
# Create a list with the specified number of 1s
num_ones = self.options.alternative_categories.value
categorylist = [1] * num_ones + [0] * (16 - num_ones)
# Shuffle the list to randomize the order
self.multiworld.random.shuffle(categorylist)
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.
@@ -103,39 +105,39 @@ class YachtDiceWorld(World):
["Category Full House", "Category Five Distinct Dice"],
["Category Yacht", "Category 4&5 Full House"]
]
# categories used in this game.
possible_categories = []
for index, cats in enumerate(all_categories):
possible_categories += [cats[categorylist[index]]]
# Add Choice and Inverse choice (or their alts) to the precollected list.
if index == 0 or index == 1:
self.precollected += [cats[categorylist[index]]]
else:
self.itempool += [cats[categorylist[index]]]
# Also start with one Roll and one Dice
self.precollected += ["Roll"]
self.precollected += ["Dice"]
# 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
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 Fragment"] * (frags_per_dice * (num_of_dice-2))
self.itempool += ["Dice Fragment"] * (frags_per_dice * (num_of_dice - 2))
# 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 Fragment"] * (frags_per_roll * (num_of_rolls-2))
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.
if self.options.minimize_extra_items.value:
@@ -143,11 +145,11 @@ class YachtDiceWorld(World):
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
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.
@@ -159,22 +161,22 @@ class YachtDiceWorld(World):
self.options.weight_of_double_category.value,
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 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
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
while dice_simulation(self.itempool + self.precollected, "state_is_a_list", self.options) < 1000:
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:
@@ -182,26 +184,26 @@ class YachtDiceWorld(World):
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
# Don't allow too many multipliers
if multipliers_added > 50:
weights[2] = 0
weights[3] = 0
# Don't allow too many extra points
if extra_points_added > 300:
weights[5] = 0
weights[5] = 0
# 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
weights[2] = 1
weights[4] = 1
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
which_item_to_add = self.multiworld.random.choices([0, 1, 2, 3, 4, 5], weights = weights)[0]
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:
self.itempool += ["Dice"]
@@ -223,8 +225,8 @@ class YachtDiceWorld(World):
weights[3] /= 1.1
multipliers_added += 1
elif which_item_to_add == 4:
cat_weights = [2, 2, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1]
self.itempool += self.multiworld.random.choices(possible_categories, weights = cat_weights)
cat_weights = [2, 2, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1]
self.itempool += self.multiworld.random.choices(possible_categories, weights=cat_weights)
weights[4] /= 1.1
elif which_item_to_add == 5:
score_dist = self.options.points_size.value
@@ -237,7 +239,7 @@ class YachtDiceWorld(World):
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]
c = self.multiworld.random.choices([0, 1, 2], weights=probs)[0]
if c == 0:
self.itempool += ["1 Point"]
extra_points_added += 1
@@ -254,45 +256,45 @@ class YachtDiceWorld(World):
raise Exception("Unknown point value (Yacht Dice)")
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
# 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.
if self.options.add_bonus_points.value == 1: #all of the extra points
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.
# 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
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
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
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
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
already_items = len(self.itempool) + self.extra_plando_items + 1
self.itempool += ["Encouragement"] * min(self.number_of_locations - already_items, 5)
@@ -300,16 +302,16 @@ class YachtDiceWorld(World):
# 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.
# 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(
["Good RNG", "Bad RNG"],
weights=[p, 1-p],
["Good RNG", "Bad RNG"],
weights=[p, 1 - p],
k=self.number_of_locations - already_items
)
@@ -318,21 +320,21 @@ class YachtDiceWorld(World):
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
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
self.multiworld.early_items[self.player]["Dice"] = 1
self.multiworld.early_items[self.player]["Roll"] = 1
def create_items(self):
self.multiworld.itempool += [self.create_item(name) for name in self.itempool]
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.
location_table, goal_index = ini_locations(self.goal_score, self.max_score, self.number_of_locations,
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
@@ -342,28 +344,28 @@ class YachtDiceWorld(World):
# 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.
# Add the victory item to the correct location.
# 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
self.goal_score = board.locations[goal_index].yacht_dice_score
self.max_score = board.locations[-1].yacht_dice_score
# 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_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.
yacht_dice_data = self._get_yachtdice_data()
@@ -373,7 +375,7 @@ class YachtDiceWorld(World):
"score_for_goal",
"minimal_number_of_dice_and_rolls",
"number_of_dice_fragments_per_dice",
"number_of_roll_fragments_per_roll",
"number_of_roll_fragments_per_roll",
"alternative_categories",
"weight_of_dice",
"weight_of_roll",
@@ -388,7 +390,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

View File

@@ -0,0 +1,12 @@
line-length = 120
[lint]
preview = true
select = ["E", "F", "W", "I", "N", "Q", "UP", "RUF", "ISC", "T20"]
ignore = ["RUF012", "RUF100"]
[per-file-ignores]
# The way options definitions work right now, world devs are forced to break line length requirements.
"options.py" = ["E501"]
# Yu Gi Oh specific: The structure of the Opponents.py file makes the line length violations acceptable.
"YachtWeights.py" = ["E501"]