New version of the apworld, with 1000 as final score, always

Will still need to check difficulty and weights of adding items.
Website is not ready yet, so this version is not usable yet :)
This commit is contained in:
spinerak
2024-05-28 22:47:49 +02:00
parent 807405c7be
commit 1a8d807a8d
5 changed files with 380 additions and 339 deletions

View File

@@ -17,7 +17,10 @@ item_table = {
"Dice Fragment": ItemData(16871244001, ItemClassification.progression),
"Roll": ItemData(16871244002, ItemClassification.progression),
"Roll Fragment": ItemData(16871244003, ItemClassification.progression),
"Score Multiplier": ItemData(16871244004, ItemClassification.progression),
"Fixed Score Multiplier": ItemData(16871244005, ItemClassification.progression),
"Step Score Multiplier": ItemData(16871244006, ItemClassification.progression),
"Category Ones": ItemData(16871244103, ItemClassification.progression),
"Category Twos": ItemData(16871244104, ItemClassification.progression),
@@ -63,7 +66,7 @@ item_table = {
"Story Chapter": ItemData(16871244202, ItemClassification.filler),
"Good RNG": ItemData(16871244203, ItemClassification.filler),
"Bad RNG": ItemData(16871244204, ItemClassification.trap),
"Extra Point": ItemData(16871244205, ItemClassification.useful),
"Extra Point": ItemData(16871244205, ItemClassification.useful), #not included in logic
"1 Point": ItemData(16871244301, ItemClassification.progression_skip_balancing),
"10 Points": ItemData(16871244302, ItemClassification.progression),
@@ -71,6 +74,11 @@ item_table = {
}
ITEM_GROUPS = {
"Score Multiplier": {
"Score Multiplier",
"Step Score Multiplier",
"Fixed Score Multiplier"
},
"Categories": {
"Category Ones",
"Category Twos",

View File

@@ -25,9 +25,8 @@ def all_locations_fun(max_score):
return location_table
#function that loads in all locations necessary for the game, so based on options.
def ini_locations(max_score, num_locs, dif):
location_table = {}
#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:
if dif == 1:
@@ -35,25 +34,31 @@ def ini_locations(max_score, num_locs, dif):
elif dif == 2:
scaling = 2.2
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
hiscore = 0
for i in range(num_locs):
for i in range(num_locs - 1):
perc = (i/num_locs)
curscore = int( 1 + (perc ** scaling) * (max_score-2) )
if(curscore <= hiscore):
curscore = hiscore + 1
hiscore = curscore
location_table[f"{curscore} score"] = LocData(starting_index + curscore, "Board", curscore)
scores += [curscore]
#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
#Finally, add a check for the actual max_score.
#This is *not* counted in num_locs, since the victory items is not as well.
location_table[f"{max_score} score"] = LocData(starting_index + max_score, "Board", max_score)
scores += [max_score]
location_table = {f"{score} score": LocData(starting_index + score, "Board", score) for score in scores}
return location_table
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}

View File

@@ -1,26 +1,57 @@
from Options import Choice, Range, PerGameCommonOptions
from dataclasses import dataclass
class numberOfDiceAndRolls(Choice):
class gameDifficulty(Choice):
"""
Total number of dice and rolls in the pool.
You start with one dice and one roll.
This option does not change the final goal.
Difficulty. This setting determines how difficult the scores are to achieve.
Easy: for beginners. No luck required, just roll the dice and have fun. Lower final goal.
Medium: intended difficulty. If you play smart, you'll finish the game without any trouble.
Hard: you'll need to play smart and be lucky.
Extreme: really hard mode, which requires many brain wrinkles and insane luck. NOT RECOMMENDED FOR MULTIWORLDS.
"""
display_name = "Number of dice and rolls in pool"
option_5_dice_and_5_rolls = 5
option_6_dice_and_4_rolls = 6
option_7_dice_and_3_rolls = 7
option_8_dice_and_2_rolls = 8
default = 5
display_name = "Game difficulty"
option_easy = 1
option_medium = 2
option_hard = 3
option_extreme = 4
default = 2
class scoreForLastCheck(Range):
"""
The items in the itempool will always allow you to reach this score of 1000.
By default, the last check is at a score of 1000.
However, you can set the score for last check lower. This will make the game shorter and easier.
"""
display_name = "Score for last check"
range_start = 500
range_end = 1000
default = 1000
# class numberOfExtraRolls(Range):
# """Total number of extra rolls you can add to your collection.
# Wait this is always 4? Yes, I removed other options, but they might return."""
# display_name = "Number of extra rolls"
# range_start = 4
# range_end = 4
# default = 4
class scoreForGoal(Range):
"""
This setting determines what score you need to get to finish the game.
It cannot be higher than the score for last check (if it is, it is changed automatically).
"""
display_name = "Score for goal"
range_start = 500
range_end = 1000
default = 777
class minimalNumberOfDiceAndRolls(Choice):
"""
The minimal number of dice and rolls in the pool.
There is an option later on that, if selected, can put more dice or rolls in the pool.
You can never get more than 8 dice and 5 rolls.
You start with one dice and one roll.
"""
display_name = "Minimal number of dice and rolls in pool"
option_2_dice_and_2_rolls = 1
option_5_dice_and_3_rolls = 2
option_5_dice_and_5_rolls = 3
option_6_dice_and_4_rolls = 4
option_7_dice_and_3_rolls = 5
option_8_dice_and_2_rolls = 6
default = 2
class numberDiceFragmentsPerDice(Range):
"""
@@ -34,8 +65,6 @@ class numberDiceFragmentsPerDice(Range):
range_end = 5
default = 4
class numberRollFragmentsPerRoll(Range):
"""
Rolls can be split into fragments, gathering enough will give you an extra roll.
@@ -48,140 +77,117 @@ class numberRollFragmentsPerRoll(Range):
range_end = 5
default = 4
class numberExtraDiceFragments(Range):
"""
Number of extra dice fragments in the pool.
The number cannot exceed the number of dice fragments per dice,
if it does, the generation will lower this setting automatically.
The option will never give an extra full dice, but makes it easier to collect all dice.
"""
display_name = "Number of extra dice fragments in the pool"
range_start = 0
range_end = 4
default = 3
class numberExtraRollFragments(Range):
"""
Number of extra roll fragments in the pool.
The number cannot exceed the number of roll fragments per roll
if it does, the generation will lower this setting automatically.
The option will never give an extra full roll, but makes it easier to collect all roll.
"""
display_name = "Number of extra roll fragments in the pool"
range_start = 0
range_end = 4
default = 3
class gameDifficulty(Choice):
"""
Difficulty. This setting determines how difficult the scores are to achieve.
Easy: for beginners. No luck required, just roll the dice and have fun. Lower final goal.
Medium: intended difficulty. If you play smart, you'll finish the game without any trouble.
Hard: you may need to play smart, be lucky and understand the score multiplier mechanic. Higher final goal.
Extreme: more strict logic, higher final goal. ONLY FOR EXPERIENCES PLAYERS IN MULTIWORLDS.
"""
display_name = "Game difficulty"
option_easy = 1
option_medium = 2
option_hard = 3
option_extreme = 4
#option_nightmare = 5
default = 2
class goalLocationPercentage(Range):
"""
What percentage of checks you need to get, to 'finish' the game.
Low percentage means you can probably 'finish' the game with some of the dice/rolls/categories.
High percentage means you need most of the useful items, and on higher difficulties you might need them all.
"""
display_name = "Goal percentage location"
range_start = 70
range_end = 100
default = 90
class alternativeCategories(Range):
"""
There are 16 default categories, but there are also 16 alternative categories.
These alternative categories can replace the default categories.
They are a little strange, but can give a fun new experience.
In the game, you can hover over categories to check what they do.
How many alternative categories would you like to see in your game?
"""
display_name = "Number of alternative categories"
range_start = 0
range_end = 16
default = 0
default = 0
class scoreMultiplierType(Choice):
class chanceOfDice(Range):
"""
There are 10 Score Multiplier items available.
This options decides how the Score Multipliers work.
Both options are of similar difficulty.
The itempool is always filled in such a way, that you can get to a score of 1000.
Extra items are shuffled in the itempool that will help you on your quest.
fixed_multiplier: every multiplier item gives you +10%.
Every score gets multiplied with the multiplier.
So with all score multipliers, all scores get +100%.
step_multiplier: every multiplier item gives you +1%.
Your multiplier increases with this percentage after every turn.
So in the first turn you have no multiplier, but in turn 16, you can have a +150% multiplier.
So, save your high-scoring categories for last. This option allow for more strategic games.
The higher this chance compared to the others, the more Dice (or Dice Fragments if selected) are added.
And of course, more dice = more points!
"""
display_name = "Score multiplier type"
option_fixed_multiplier = 1
option_step_multiplier = 2
default = 1
display_name = "Chance of adding Dice"
range_start = 0
range_end = 100
default = 5
class gameMode(Choice):
class chanceOfRoll(Range):
"""
Yacht Dice has three main game modes:
The itempool is always filled in such a way, that you can get to a score of 1000.
Extra items are shuffled in the itempool that will help you on your quest.
Standard. Get to 500 points on medium difficulty (and a bit lower/higher on other difficulties).
Points mode: a bunch of "10 Points" items are shuffled through the item pool. Get to 1000 points.
The amount of "10 Points" items in the pool depends on your selected difficulty.
I'll add a new category soon™
The higher this chance compared to the others, the more Rolls (or Roll Fragments if selected) are added.
And with more rolls, you'll be able to reach higher scores.
"""
# Extra categories: categories may appear multiple times.
# Getting a category again gives a x2 multiplier for that category. Get to 1000 points.
# The amount of "10 Points" items in the pool depends on your selected difficulty.
display_name = "Game mode"
option_standard = 1
option_points_mode = 2
option_extra_categories = 3
default = 1
class pointsGameMode(Choice):
"""
If points mode is selected, this option determines the value of the points items.
yes_1_per_item: hundreds of "1 Point" items are shuffled into the pool.
NOT recommended in multiplayer, unless everyone is aware of the hundred of points items
yes_10_per_item: puts tens of "10 Points" (and a few 1 Points) into the item pool.
yes_100_per_item: puts a few "100 Points" (and a few 1 and 10 Points) into the item pool.
Warning: will unlock many checks if an 100 Points item is collected.
"""
display_name = "Value of points item"
option_1_per_item = 2
option_10_per_item = 3
option_100_per_item = 4
default = 3
display_name = "Chance of adding Roll"
range_start = 0
range_end = 100
default = 20
class chanceOfFixedScoreMultiplier(Range):
"""
The itempool is always filled in such a way, that you can get to a score of 1000.
Extra items are shuffled in the itempool that will help you on your quest.
The higher this chance compared to the others, the more Fixed Score Multipliers are added.
Getting a Fixed Score Multiplier will boost all future scores by 10%.
"""
display_name = "Chance of adding Fixed Score Multiplier"
range_start = 0
range_end = 100
default = 30
class chanceOfStepScoreMultiplier(Range):
"""
The itempool is always filled in such a way, that you can get to a score of 1000.
Extra items are shuffled in the itempool that will help you on your quest.
The higher this chance compared to the others, the more Step Score Multipliers are added.
The Step Score Multiplier boosts your multiplier after every roll by 1%, and resets on reset.
So, keep high scoring categories for last to get the most of them.
By default, this item is not included. It is fun however, you just need to know the above strategy.
"""
display_name = "Chance of adding Step Score Multiplier"
range_start = 0
range_end = 100
default = 0
class chanceOfDoubleCategory(Range):
"""
The itempool is always filled in such a way, that you can get to a score of 1000.
Extra items are shuffled in the itempool that will help you on your quest.
The higher this chance compared to the others, the more frequently categories will appear multiple times.
Getting a category for the second time, gives a x2 multiplier for that category.
And getting the category again will double it again!
"""
display_name = "Chance of adding Double Category"
range_start = 0
range_end = 100
default = 50
class chanceOfPoints(Range):
"""
The itempool is always filled in such a way, that you can get to a score of 1000.
Extra items are shuffled in the itempool that will help you on your quest.
The higher this chance compared to the others, the more points are added into the pool.
And getting points gives you... points. You can get 1 point, 10 points, and even 100 points.
"""
display_name = "Chance of adding Points"
range_start = 0
range_end = 100
default = 20
class pointsSize(Choice):
"""
If you choose to add points to the item pool, do you prefer many small points,
a few larger points, or a mix of both?
"""
display_name = "Size of points"
option_small = 1
option_medium = 2
option_large = 3
option_mix = 4
default = 2
class minimizeExtraItems(Choice):
"""
Would you like to minimize the number of extra items in the pool?
Note that if you put this on, categories Fives, Sixes and Pair are put early into the playthrough.
Besides necessary items, Yacht Dice needs extra items (see below) in the item pool to ensure success in generation.
It is possible however to decrease the number of extra items,
by putting categories Fives, Sixes and Pair early into the playthrough. Would you like to do this?
"""
display_name = "Minimize extra items"
option_no_dont = 1
@@ -190,12 +196,13 @@ class minimizeExtraItems(Choice):
class addExtraPoints(Choice):
"""
Yacht Dice typically has space for more items.
Would you like extra points shuffled in the item pool?
Yacht Dice typically has space for extra items.
If there is space, would you like extra points shuffled in the item pool?
They make the game a little bit easier, as they are not considered in the logic.
all_of_it: put as many extra points in locations as possible
all_of_it: fill all locations with extra points
sure: put some extra points in
never: don't but any extra points
never: don't put any extra points
"""
display_name = "Extra points in the pool"
option_all_of_it = 1
@@ -206,8 +213,12 @@ class addExtraPoints(Choice):
class addStoryChapters(Choice):
"""
Yacht Dice typically has space for more items.
Would you like story chapters shuffled in the item pool?
Note: if you have extra points on "all_of_it" there won't be story chapters.
If there is space, would you like story chapters shuffled in the item pool?
Note: if you have extra points on "all_of_it", there won't be story chapters.
all_of_it: fill all locations with story chapters
sure: if there is space left, put in 10 story chapters.
never: don't put any story chapters, I don't like reading (but I'm glad you're reading THIS!)
"""
display_name = "Extra story chapters in the pool"
option_all_of_it = 1
@@ -217,7 +228,8 @@ class addStoryChapters(Choice):
class whichStory(Choice):
"""
The most important part of Yacht Dice is the narrative.
The most important part of Yacht Dice is the narrative.
If you choose to
10 story chapters are shuffled into the item pool.
You can read them in the feed on the website.
Which story would you like to read?
@@ -234,17 +246,25 @@ class whichStory(Choice):
@dataclass
class YachtDiceOptions(PerGameCommonOptions):
number_of_dice_and_rolls: numberOfDiceAndRolls
number_of_dice_fragments_per_dice: numberDiceFragmentsPerDice
number_of_extra_dice_fragments: numberExtraDiceFragments
number_of_roll_fragments_per_roll: numberRollFragmentsPerRoll
number_of_extra_roll_fragments: numberExtraRollFragments
game_difficulty: gameDifficulty
goal_location_percentage: goalLocationPercentage
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
score_multiplier_type: scoreMultiplierType
game_mode: gameMode
points_game_mode: pointsGameMode
#the following options determine what extra items are shuffled into the pool:
chance_of_dice: chanceOfDice
chance_of_roll: chanceOfRoll
chance_of_fixed_score_multiplier: chanceOfFixedScoreMultiplier
chance_of_step_score_multiplier: chanceOfStepScoreMultiplier
chance_of_double_category: chanceOfDoubleCategory
chance_of_points: chanceOfPoints
points_size: pointsSize
minimize_extra_items: minimizeExtraItems
add_extra_points: addExtraPoints
add_story_chapters: addStoryChapters

View File

@@ -68,44 +68,57 @@ def extractProgression(state, player, options):
#method to obtain a list of what items the player has.
#this includes categories, dice, rolls and score multiplier.
number_of_dice = (
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 Fragment", player) // options.number_of_roll_fragments_per_roll.value
)
number_of_mults = state.count("Score Multiplier", player)
if player == "state_is_a_list":
number_of_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 Fragment") // options.number_of_roll_fragments_per_roll.value
)
roll = state.count("Roll")
rollfragments = state.count("Roll Fragment")
number_of_fixed_mults = state.count("Fixed Score Multiplier")
number_of_step_mults = state.count("Step Score Multiplier")
categories = []
for category_name, category_value in category_mappings.items():
if state.count(category_name) >= 1:
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:
number_of_dice = (
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 Fragment", player) // options.number_of_roll_fragments_per_roll.value
)
roll = state.count("Roll", player)
rollfragments = state.count("Roll Fragment", player)
number_of_fixed_mults = state.count("Fixed Score Multiplier", player)
number_of_step_mults = state.count("Step Score Multiplier", player)
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))]
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
score_mult = -10000
if options.score_multiplier_type.value == 1: #fixed
score_mult = 0.1 * number_of_mults
if options.score_multiplier_type.value == 2: #step
score_mult = 0.01 * number_of_mults
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))]
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, score_mult, extra_points_in_logic]
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 diceSimulationStrings(categories, nbDice, nbRolls, multiplier, diff, scoremulttype):
tup = tuple([tuple(sorted([c.name+str(c.quantity) for c in categories])), nbDice, nbRolls, multiplier, diff, scoremulttype]) #identifier
def diceSimulationStrings(categories, nbDice, nbRolls, fixed_mult, step_mult, diff):
tup = tuple([tuple(sorted([c.name+str(c.quantity) for c in categories])),
nbDice, nbRolls, fixed_mult, step_mult, diff]) #identifier
#if already computed, return the result
if tup in yachtdice_cache.keys():
@@ -161,11 +174,7 @@ def diceSimulationStrings(categories, nbDice, nbRolls, multiplier, diff, scoremu
percReturn = [0, 0.4, 0.4, 0.45, 0.45, 0.45][diff]
diffDivide = [0, 9, 5, 3, 2, 1][diff]
if scoremulttype == 2:
percReturn = [0, 0.4, 0.4, 0.45, 0.45, 0.45][diff]
diffDivide = [0, 9, 8, 5, 4, 3][diff]
diffDivide = [0, 9, 8, 5, 4, 3][diff]
#calculate total distribution
total_dist = {0: 1}
@@ -178,15 +187,10 @@ def diceSimulationStrings(categories, nbDice, nbRolls, multiplier, diff, scoremu
for key in dist.keys():
dist[key] /= 100000
#for higher difficulties, the simulation gets multiple tries for categories.
dist = max_dist(dist, max(1, len(categories) // diffDivide))
cur_mult = -100
if scoremulttype == 1: #fixed
cur_mult = multiplier
if scoremulttype == 2: #step
cur_mult = j * multiplier
cur_mult = fixed_mult + step_mult * j
total_dist = add_distributions(total_dist, dist, (1 + cur_mult) * ( 2 ** (categories[j].quantity-1) ))
#save result into the cache, then return it
@@ -195,43 +199,10 @@ def diceSimulationStrings(categories, nbDice, nbRolls, multiplier, diff, scoremu
# Returns the feasible score that one can reach with the current state, options and difficulty.
def diceSimulation(state, player, options):
categories, nbDice, nbRolls, multiplier, expoints = extractProgression(state, player, options)
return diceSimulationStrings(categories, nbDice, nbRolls, multiplier,
options.game_difficulty.value, options.score_multiplier_type.value) + expoints
categories, nbDice, nbRolls, fixed_mult, step_mult, expoints = extractProgression(state, player, options)
return diceSimulationStrings(categories, nbDice, nbRolls, fixed_mult, step_mult,
options.game_difficulty.value) + expoints
def calculateScoreInLogic(state, options):
number_of_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 Fragment") // options.number_of_roll_fragments_per_roll.value
)
number_of_mults = state.count("Score Multiplier")
score_mult = -10000
if options.score_multiplier_type.value == 1: #fixed
score_mult = 0.1 * number_of_mults
if options.score_multiplier_type.value == 2: #step
score_mult = 0.01 * number_of_mults
categories = []
for category_name, category_value in category_mappings.items():
if state.count(category_name) >= 1:
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
return diceSimulationStrings(categories, number_of_dice, number_of_rerolls, score_mult,
options.game_difficulty.value, options.score_multiplier_type.value) + extra_points_in_logic
# Sets rules on entrances and advancements that are always applied
def set_yacht_rules(world: MultiWorld, player: int, options):
for l in world.get_locations(player):

View File

@@ -2,7 +2,7 @@ from BaseClasses import Region, Entrance, Item, Tutorial
from .Items import YachtDiceItem, item_table, ITEM_GROUPS
from .Locations import YachtDiceLocation, all_locations, ini_locations
from .Options import YachtDiceOptions
from .Rules import set_yacht_rules, set_yacht_completion_rules, calculateScoreInLogic, diceSimulation
from .Rules import set_yacht_rules, set_yacht_completion_rules, diceSimulation
from ..AutoWorld import World, WebWorld
import math
import logging
@@ -38,7 +38,7 @@ class YachtDiceWorld(World):
item_name_groups = ITEM_GROUPS
ap_world_version = "1.1.1"
ap_world_version = "1.2"
def _get_yachtdice_data(self):
return {
@@ -51,24 +51,17 @@ class YachtDiceWorld(World):
def generate_early(self):
self.itempool = []
self.precollected = []
#calculate the maximum score goal:
game_difficulty = self.options.game_difficulty.value
self.precollected = []
#number of dice and rolls in the pull
numDice = self.options.number_of_dice_and_rolls.value # either 5, 6, 7 or 8
numRolls = 10 - numDice # either 5, 4, 3 en 2
ind_dice_rolls = self.options.minimal_number_of_dice_and_rolls.value
numDice = [0, 2, 5, 5, 6, 7, 8][ind_dice_rolls]
numRolls = [0, 2, 3, 5, 4, 3, 2][ind_dice_rolls]
#amount of dice and roll fragments needed to get a dice or roll
amDiceF = self.options.number_of_dice_fragments_per_dice.value
amRollsF = self.options.number_of_roll_fragments_per_roll.value
#number of extra dice and roll fragments in the pool,
#so that you don't have to wait for that one last fragment
#capped to be one less than number of fragments needed to complete a new dice/roll.
exDiceF = max(0, min(amDiceF - 1, self.options.number_of_extra_dice_fragments.value) )
exRollsF = max(0, min(amRollsF - 1, self.options.number_of_extra_roll_fragments.value) )
#count number of plando items not from pool, we need extra locations for them
self.extra_plando_items = 0
@@ -87,7 +80,7 @@ class YachtDiceWorld(World):
self.multiworld.random.shuffle(categorylist)
#add all categories. Note: not "choice" and "inverse choice", they are obtained at the start
#A list of all possible categories.
all_categories = [
["Category Choice", "Category Double Threes and Fours"],
["Category Inverse Choice", "Category Quadruple Ones and Twos"],
@@ -107,43 +100,39 @@ class YachtDiceWorld(World):
["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 one fragment per dice, just add "Dice" objects
if amDiceF == 1:
self.itempool += ["Dice"] * (numDice-1) #minus one because one is in start inventory
else:
self.itempool += ["Dice"] #always add a full dice to make generation easier
#add dice fragments, note the -2 because one is added in the previous line, one is in start inventory
self.itempool += ["Dice Fragment"] * (amDiceF * (numDice-2) + exDiceF)
self.itempool += ["Dice"] #always add a full dice to make generation easier (will be 'early')
self.itempool += ["Dice Fragment"] * (amDiceF * (numDice-2))
#if one fragment per roll, just add "Roll" objects
if amRollsF == 1:
self.itempool += ["Roll"] * (numRolls-1) #minus one because one is in start inventory
else:
self.itempool += ["Roll"] #always add a full roll to make generation easier
#add roll fragments, note the -2 because one is added in the previous line, one is in start inventory
self.itempool += ["Roll Fragment"] * (amRollsF * (numRolls-2) + exRollsF)
#always add exactly 10 score multipliers
self.itempool += ["Score Multiplier"] * 10
#At this point, the itempool has all basic items.
scoreInLogic = calculateScoreInLogic(self.itempool+self.precollected, self.options)
self.itempool += ["Roll"] #always add a full roll to make generation easier (will be 'early')
self.itempool += ["Roll Fragment"] * (amRollsF * (numRolls-2))
already_items = len(self.itempool) + self.extra_plando_items
#Yacht Dice needs extra filler items so it doesn't get stuck in generation.
if self.options.minimize_extra_items.value == 2:
extraPercentage = max(0.1, 0.5 - self.multiworld.players / 10)
else:
@@ -151,39 +140,95 @@ class YachtDiceWorld(World):
extraLocationsNeeded = max(10, math.ceil(already_items * extraPercentage))
if self.options.game_mode.value == 1:
self.max_score = scoreInLogic
#print(f"Max score: {self.max_score}, difficulty {game_difficulty}")
self.max_score = self.options.score_for_last_check.value
self.goal_score = min(self.max_score, self.options.score_for_goal.value)
if self.options.game_mode.value == 2:
self.max_score = 1000
self.extra_points_for_game_mode = self.max_score - scoreInLogic
weights = [
self.options.chance_of_dice.value,
self.options.chance_of_roll.value,
self.options.chance_of_fixed_score_multiplier.value,
self.options.chance_of_step_score_multiplier.value,
self.options.chance_of_double_category.value,
self.options.chance_of_points.value
]
if self.options.chance_of_dice.value > 0:
if amDiceF > 1:
self.itempool += ["Dice Fragment"] * (amDiceF - 1)
if self.options.chance_of_roll.value > 0:
if amRollsF > 1:
self.itempool += ["Roll Fragment"] * (amRollsF - 1)
while diceSimulation(self.itempool + self.precollected, "state_is_a_list", self.options) < 1000:
print("Max score currently ")
print(diceSimulation(self.itempool + self.precollected, "state_is_a_list", self.options))
print(self.itempool)
if(self.options.points_game_mode.value >= 4):
while self.extra_points_for_game_mode >= 89: #rather have 100 points than lot of smaller items
self.itempool += ["100 Points"]
self.extra_points_for_game_mode -= 100
allitems = self.itempool + self.precollected
dice_fragments_in_pool = allitems.count("Dice") * amDiceF + allitems.count("Dice Fragment")
if dice_fragments_in_pool + 1 >= 9 * amDiceF:
weights[0] = 0 #can't have 9 dice
roll_fragments_in_pool = allitems.count("Roll") * amDiceF + allitems.count("Roll Fragment")
if roll_fragments_in_pool + 1 >= 6 * amRollsF:
weights[1] = 0 # can't have 6 rolls
#if all weights are zero, allow to add fixed score multiplier, double category, points.
if sum(weights) == 0:
weights[2] = 1
weights[4] = 1
weights[5] = 1
which_item_to_add = self.multiworld.random.choices([0,1,2,3,4,5], weights = weights)[0]
if which_item_to_add == 0:
if amDiceF == 1:
self.itempool += ["Dice"]
else:
self.itempool += ["Dice Fragment"]
weights[0] /= 1.1
elif which_item_to_add == 1:
if amRollsF == 1:
self.itempool += ["Roll"]
else:
self.itempool += ["Roll Fragment"]
weights[1] /= 1.1
elif which_item_to_add == 2:
self.itempool += ["Fixed Score Multiplier"]
weights[2] /= 1.1
elif which_item_to_add == 3:
self.itempool += ["Step Score Multiplier"]
weights[3] /= 1.1
elif which_item_to_add == 4:
self.itempool += self.multiworld.random.choices(possible_categories)
weights[4] /= 1.1
elif which_item_to_add == 5:
score_dist = self.options.points_size.value
probs = [1,0,0]
if score_dist == 1:
probs = [0.9,0.08,0]
if score_dist == 2:
probs = [0,1,0]
if score_dist == 3:
probs = [0,0.3,0.7]
if score_dist == 4:
probs = [0.3,0.4,0.3]
self.itempool += self.multiworld.random.choices(["1 Point", "10 Points", "100 Points"],
weights = probs)
weights[5] /= 1.1
else:
raise Exception("Invalid index when adding new items in Yacht Dice")
print("Max score after adding ")
print(self.itempool)
print(diceSimulation(self.itempool + self.precollected, "state_is_a_list", self.options))
print()
if(self.options.points_game_mode.value >= 3):
while self.extra_points_for_game_mode >= 7: #rather have 10 points that lot of 1 points.
self.itempool += ["10 Points"]
self.extra_points_for_game_mode -= 10
if(self.options.points_game_mode.value >= 2 and self.extra_points_for_game_mode > 0):
self.itempool += ["1 Point"] * self.extra_points_for_game_mode
if self.options.game_mode.value == 3:
self.max_score = 1000
while calculateScoreInLogic(self.itempool+self.precollected, self.options) < 1000:
self.itempool += [self.multiworld.random.choice(possible_categories)]
#count the number of locations in the game. extra_plando_items is set in generate_early
#and counts number of plando items *not* from pool.
already_items = len(self.itempool) + self.extra_plando_items
already_items = len(self.itempool) + self.extra_plando_items + 1 #+1 because of Victory item
self.number_of_locations = already_items + extraLocationsNeeded
# From here, we'll count the number of items in the self.itempool, and add items to the pool,
@@ -191,55 +236,47 @@ class YachtDiceWorld(World):
#first, we flood the entire pool with extra points (useful), if that setting is chosen.
if self.options.add_extra_points.value == 1: #all of the extra points
already_items = len(self.itempool) + self.extra_plando_items
already_items = len(self.itempool) + self.extra_plando_items + 1
self.itempool += ["Extra 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
already_items = len(self.itempool) + self.extra_plando_items
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_extra_points.value == 2: #add extra points if wanted
already_items = len(self.itempool) + self.extra_plando_items
already_items = len(self.itempool) + self.extra_plando_items + 1
self.itempool += ["Extra 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
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 extra points if there is still room
if self.options.add_extra_points.value == 2:
already_items = len(self.itempool) + self.extra_plando_items
already_items = len(self.itempool) + self.extra_plando_items + 1
self.itempool += ["Extra 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
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
already_items = len(self.itempool) + self.extra_plando_items
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 don't do anything.
#probability of Good and Bad rng, based on difficulty for fun :)
p = 0.5
if self.options.game_difficulty.value == 1:
p = 0.9
elif self.options.game_difficulty.value == 2:
p = 0.7
elif self.options.game_difficulty.value == 3:
p = 0.5
elif self.options.game_difficulty.value == 4:
p = 0.1
p = 1.1 - 0.25 * self.options.game_difficulty.value
already_items = len(self.itempool) + self.extra_plando_items
already_items = len(self.itempool) + self.extra_plando_items + 1
self.itempool += self.multiworld.random.choices(
["Good RNG", "Bad RNG"],
weights=[p, 1-p],
@@ -247,9 +284,10 @@ class YachtDiceWorld(World):
)
#we're 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
if len(self.itempool) != self.number_of_locations:
raise Exception(f"Number in self.itempool is not number of locations {len(self.itempool)} {self.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}.")
for item in self.precollected:
self.multiworld.push_precollected(self.create_item(item))
@@ -263,12 +301,9 @@ class YachtDiceWorld(World):
self.multiworld.itempool += [item]
def create_regions(self):
#we have no complicated regions, just one rule per location.
game_difficulty = self.options.game_difficulty.value
#call the ini_locations function, that generations locations based on the inputs.
location_table = ini_locations(self.max_score, self.number_of_locations, game_difficulty)
#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
menu = Region("Menu", self.player, self.multiworld)
@@ -277,19 +312,15 @@ 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]
#parameter to see where the final check should be
goal_percentage_location = self.options.goal_location_percentage.value
#which index of all locations should have the Victory item.
victory_id = int(goal_percentage_location / 100 * len(board.locations))-1
# Add the victory item to the correct location.
# The website declares that the game is complete when the victory item is obtained.
board.locations[victory_id].place_locked_item(self.create_item("Victory"))
board.locations[goal_index].place_locked_item(self.create_item("Victory"))
#these will be slot_data input
self.goal_score = board.locations[victory_id].yacht_dice_score
self.goal_score = board.locations[goal_index].yacht_dice_score
self.max_score = board.locations[-1].yacht_dice_score
#add the regions
@@ -325,19 +356,24 @@ class YachtDiceWorld(World):
yacht_dice_data = self._get_yachtdice_data()
yacht_dice_options = self.options.as_dict(
"number_of_dice_and_rolls",
"number_of_dice_fragments_per_dice",
"number_of_roll_fragments_per_roll",
"number_of_extra_roll_fragments",
"game_difficulty",
"goal_location_percentage",
"alternative_categories",
"score_multiplier_type",
"game_mode",
"minimize_extra_items",
"add_extra_points",
"add_story_chapters",
"which_story"
"game_difficulty",
"score_for_last_check",
"score_for_goal",
"minimal_number_of_dice_and_rolls",
"number_of_dice_fragments_per_dice",
"number_of_roll_fragments_per_roll",
"alternative_categories",
"chance_of_dice",
"chance_of_roll",
"chance_of_fixed_score_multiplier",
"chance_of_step_score_multiplier",
"chance_of_double_category",
"chance_of_points",
"points_size",
"minimize_extra_items",
"add_extra_points",
"add_story_chapters",
"which_story"
)
slot_data = {**yacht_dice_data, **yacht_dice_options} #combine the two
@@ -345,6 +381,7 @@ class YachtDiceWorld(World):
slot_data["goal_score"] = self.goal_score
slot_data["last_check_score"] = self.max_score
slot_data["ap_world_version"] = self.ap_world_version
print(1/0)
return slot_data
def create_item(self, name: str) -> Item: