mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-23 11:03:20 -07:00
ruff styling, fix
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
])
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
12
worlds/yachtdice/ruff.toml
Normal file
12
worlds/yachtdice/ruff.toml
Normal 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"]
|
||||
Reference in New Issue
Block a user