WIP: Add character file and stat adjusted skeleton actions #2

Closed
TheTaques wants to merge 11 commits from char-file into main
10 changed files with 197 additions and 48 deletions

View File

@ -6,6 +6,8 @@ from .objects.bonemarketargumentparser import BoneMarketArgumentParser
from .objects.enumaction import EnumAction
from .objects.listaction import ListAction
from .solve import *
from .read_char import *
parser = BoneMarketArgumentParser(
prog='Bone Market Solver',
@ -71,7 +73,7 @@ skeleton_parameters = parser.add_argument_group(
skeleton_parameters.add_argument(
"-s", "--shadowy",
type=int,
required=True,
default=Char.SHADOWY.value,
help="the effective level of Shadowy used for selling to buyers",
dest='shadowy_level'
)

View File

@ -0,0 +1,27 @@
# This is a constant used to calculate difficulty checks. You almost certainly do not need to change this.
DIFFICULTY_SCALER = 0.6
def narrow_challenge(difficulty_level: int, stat: int):
offset = 6 - difficulty_level
stat += offset
if stat > 9:
return 1
elif stat < 2:
return .1
else:
return stat/10
def broad_challenge(difficulty_level: int, stat: int):
chance = DIFFICULTY_SCALER*stat/difficulty_level * 100
chance = chance // 1
chance /= 100
return chance
def mean_outcome(success: int, failure: int, chance: float):
mean_success = success*chance
mean_failure = failure*(1-chance)
combined_mean_outcome = mean_success + mean_failure
return int(combined_mean_outcome)

View File

@ -0,0 +1,32 @@
from enum import Enum
class Char(Enum):
"""Character stats"""
SHADOWY = 300
DANGEROUS = 300
PERSUASIVE = 300
WATCHFUL = 300
PLAYER_OF_CHESS = 7
ARTISAN_OF_RED_SCIENCE = 7
GLASSWORK = 7
KATALEPTIC_TOXICOLOGY = 7
MITHRIDACY = 7
MONSTROUS_ANATOMY = 7
SHAPELING_ARTS = 7
BIZARRE = 15
DREADED = 15
RESPECTABLE = 15

View File

@ -5,26 +5,32 @@ from enum import Enum
from .costs import Cost
from ..objects.action import Action
from ..read_char import Char
from ..challenge_functions import narrow_challenge, mean_outcome
class Adjustment(Enum):
"""An action that is taken after all parts have been added to a skeleton."""
CARVE_AWAY_AGE = Action(
"Carve away some evidence of age",
cost = Cost.ACTION.value,
antiquity = -2
cost = Cost.ACTION.value / narrow_challenge(6, Char.MITHRIDACY.value),
antiquity = -2,
implausibility = mean_outcome(0, 2, narrow_challenge(6, Char.MITHRIDACY.value))
)
DISGUISE_AMALGAMY = Action(
"Disguise the amalgamy of this piece",
cost = Cost.ACTION.value + 25*Cost.JADE_FRAGMENT.value,
amalgamy = -2
cost = 25*Cost.JADE_FRAGMENT.value + Cost.ACTION.value / narrow_challenge(6, Char.KATALEPTIC_TOXICOLOGY.value),
amalgamy = -2,
implausibility = mean_outcome(0, 2, narrow_challenge(6, Char.KATALEPTIC_TOXICOLOGY.value))
)
MAKE_LESS_DREADFUL = Action(
"Make your skeleton less dreadful",
cost = Cost.ACTION.value,
menace = -2
cost = Cost.ACTION.value / narrow_challenge(6, Char.KATALEPTIC_TOXICOLOGY.value),
menace = -2,
implausibility = mean_outcome(0, 2, narrow_challenge(6, Char.KATALEPTIC_TOXICOLOGY.value))
)
def __str__(self):

View File

@ -5,6 +5,9 @@ from enum import Enum
from .costs import Cost
from ..objects.action import Action
from ..read_char import Char
from ..challenge_functions import narrow_challenge, broad_challenge, mean_outcome
class Appendage(Enum):
"""An action that is taken once all skulls are added to a skeleton."""
@ -17,6 +20,7 @@ class Appendage(Enum):
amalgamy = 2
)
# TODO: Difficulty is increased by 2 for each Fin or Tentacle on the skeleton
ALBATROSS_WING = Action(
"Put an Albatross Wing on your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.ALBATROSS_WING.value,
@ -26,6 +30,7 @@ class Appendage(Enum):
amalgamy = 1
)
# TODO: Difficulty is increased by 2 for each Arm, Leg, Wing, and Tentacle that is already attached
AMBER_FIN = Action(
"Attach the Amber-Crusted Fin to your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.AMBER_FIN.value,
@ -36,6 +41,7 @@ class Appendage(Enum):
menace = 1
)
# TODO: Difficulty is increased with Fins on the skeleton
BAT_WING = Action(
"Add a Bat Wing to your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.BAT_WING.value,
@ -51,7 +57,7 @@ class Appendage(Enum):
value = 50,
tails_needed = -1,
tails = 1,
menace = 2
menace = mean_outcome(2, 1, narrow_challenge(4, Char.MONSTROUS_ANATOMY.value))
)
CRUSTACEAN_PINCER = Action(
@ -80,6 +86,8 @@ class Appendage(Enum):
legs = 1
)
# TODO: Base challenge: Narrow, Monstrous Anatomy 1
# The difficulty is increased by 2 for each Arm, Leg, Wing, and Tentacle already attached to your skeleton.
FIN_BONES = Action(
"Put Fins on your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.FIN_BONES.value,
@ -94,7 +102,7 @@ class Appendage(Enum):
value = 2750,
limbs_needed = -1,
arms = 1,
antiquity = 2
antiquity = mean_outcome(2, 1, narrow_challenge(11, Char.MONSTROUS_ANATOMY.value))
)
HELICAL_THIGH = Action(
@ -103,7 +111,7 @@ class Appendage(Enum):
value = 300,
limbs_needed = -1,
legs = 1,
amalgamy = 2
amalgamy = mean_outcome(2, 1, narrow_challenge(6, Char.SHAPELING_ARTS.value))
)
HUMAN_ARM = Action(
@ -120,7 +128,8 @@ class Appendage(Enum):
cost = Cost.ACTION.value + Cost.IVORY_FEMUR.value,
value = 6500,
limbs_needed = -1,
legs = 1
legs = 1,
implausibility = mean_outcome(0, 4, narrow_challenge(7, Char.MONSTROUS_ANATOMY.value))
)
IVORY_HUMERUS = Action(
@ -128,9 +137,12 @@ class Appendage(Enum):
cost = Cost.ACTION.value + Cost.IVORY_HUMERUS.value,
value = 1500,
limbs_needed = -1,
arms = 1
arms = 1,
implausibility = mean_outcome(0, 2, narrow_challenge(6, Char.KATALEPTIC_TOXICOLOGY.value))
)
# TODO: Base challenge: Narrow, Mithridacy 1
# Difficulty increases by 2 for each Fin or Tentacle already attached to your skeleton.
JURASSIC_THIGH = Action(
"Apply a Jurassic Thigh Bone to your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.JURASSIC_FEMUR.value,
@ -140,6 +152,8 @@ class Appendage(Enum):
antiquity = 1
)
# TODO: Base challenge: Narrow, Mithridacy 6
# Difficulty increases by 1 for each limb that is NOT a KNOTTED_HUMERUS
KNOTTED_HUMERUS = Action(
"Apply a Knotted Humerus to your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.KNOTTED_HUMERUS.value,
@ -149,6 +163,8 @@ class Appendage(Enum):
amalgamy = 1
)
# TODO: Base challenge: Monstrous Anatomy 4
# No failure info on wiki
OBSIDIAN_TAIL = Action(
"Apply an Obsidian Chitin Tail to your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.OBSIDIAN_TAIL.value,
@ -164,9 +180,11 @@ class Appendage(Enum):
value = 250,
tails_needed = -1,
tails = 1,
implausibility = 1
implausibility = mean_outcome(1, 4, narrow_challenge(4, Char.MITHRIDACY.value))
)
# TODO: Base challenge: Narrow, Monstrous Anatomy 1
# Difficulty increases by 2 for each Fin already attached to your skeleton.
TERROR_BIRD_WING = Action(
"Add the Wing of a Young Terror Bird to your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.TERROR_BIRD_WING.value,
@ -183,7 +201,7 @@ class Appendage(Enum):
value = 250,
tails_needed = -1,
tails = 1,
antiquity = 1
antiquity = mean_outcome(0, 1, narrow_challenge(4, Char.MONSTROUS_ANATOMY.value))
)
UNIDENTIFIED_THIGH = Action(
@ -194,27 +212,19 @@ class Appendage(Enum):
legs = 1
)
WITHERED_TAIL = Action(
"Apply a Withered Tentacle as a tail on your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.WITHERED_TENTACLE.value,
value = 250,
tails_needed = -1,
tails = 1,
antiquity = -1
)
WITHERED_TENTACLE = Action(
"Put a Withered Tentacle on your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.WITHERED_TENTACLE.value,
value = 250,
limbs_needed = -1,
tentacles = 1,
antiquity = -1
antiquity = -1,
implausibility = mean_outcome(0, 2, narrow_challenge(5, Char.MONSTROUS_ANATOMY.value))
)
REMOVE_TAIL = Action(
"Remove the tail from your (Skeleton Type)",
cost = Cost.ACTION.value,
cost = Cost.ACTION.value * 1 / broad_challenge(220, Char.DANGEROUS.value),
tails = -1
)

View File

@ -5,6 +5,21 @@ from enum import Enum
from .costs import Cost
from ..objects.action import Action
from ..read_char import *
from ..challenge_functions import narrow_challenge
def _convincing_history_cost():
chance = narrow_challenge(6, Char.KATALEPTIC_TOXICOLOGY.value)
if chance == 1:
cost = 3*Cost.REVISIONIST_NARRATIVE.value + Cost.ACTION.value
else:
actions = 1 / chance
cost = actions * Cost.ACTION.value
cost += Cost.REVISIONIST_NARRATIVE.value * (3 + actions - 1)
return cost
class Embellishment(Enum):
"""An action is taken after a declaration has been made for a skeleton."""
@ -17,7 +32,7 @@ class Embellishment(Enum):
CONVINCING_HISTORY = Action(
"Invest great time and skill in coming up with a convincing history",
cost = Cost.ACTION.value + 3*Cost.REVISIONIST_NARRATIVE.value,
cost = _convincing_history_cost(),
implausibility = -5
)

View File

@ -5,13 +5,15 @@ from enum import Enum
from .costs import Cost
from ..objects.action import Action
from ..challenge_functions import narrow_challenge, mean_outcome
from ..read_char import Char
class Skull(Enum):
"""An action that is taken immediately after starting a skeleton."""
BAPTIST_SKULL = Action(
"Duplicate the skull of John the Baptist, if you can call that a skull",
cost = Cost.ACTION.value + 500*Cost.BONE_FRAGMENT.value + 10*Cost.PEPPERCAPS.value,
cost = Cost.ACTION.value * 1 / narrow_challenge(6, Char.ARTISAN_OF_RED_SCIENCE.value) + 500*Cost.BONE_FRAGMENT.value + 10*Cost.PEPPERCAPS.value,
value = 1250,
skulls_needed = -1,
skulls = 1,
@ -20,13 +22,14 @@ class Skull(Enum):
BRASS_SKULL = Action(
"Affix a Bright Brass Skull to your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.BRASS_SKULL.value + 200*Cost.NEVERCOLD_BRASS.value,
value = 6500,
cost = Cost.ACTION.value + Cost.BRASS_SKULL.value + mean_outcome(200*Cost.NEVERCOLD_BRASS.value, 0, narrow_challenge(6, Char.MITHRIDACY.value)),
value = mean_outcome(6500, 6000, narrow_challenge(6, Char.MITHRIDACY.value)),
skulls_needed = -1,
skulls = 1,
implausibility = 2
implausibility = mean_outcome(2, 6, narrow_challenge(6, Char.MITHRIDACY.value)),
)
# TODO
CORAL_SKULL = Action(
"Affix a Skull in Coral to your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.CORAL_SKULL.value + Cost.SCINTILLACK.value,
@ -43,7 +46,7 @@ class Skull(Enum):
skulls_needed = -1,
skulls = 2,
amalgamy = 1,
antiquity = 2
antiquity = mean_outcome(2, 1, narrow_challenge(4, Char.MONSTROUS_ANATOMY.value))
)
# Adds Exhaustion
@ -53,7 +56,8 @@ class Skull(Enum):
value = 10000,
skulls_needed = -1,
skulls = 1,
exhaustion = 2
exhaustion = 2,
implausibility = mean_outcome(0, 2, narrow_challenge(4, Char.MITHRIDACY.value)),
)
EYELESS_SKULL = Action(
@ -72,13 +76,13 @@ class Skull(Enum):
skulls_needed = -1,
skulls = 1,
antiquity = 1,
menace = 2
menace = mean_outcome(2, 1, narrow_challenge(6, Char.MONSTROUS_ANATOMY.value))
)
# Seeking the Name of Mr. Eaten
OWN_SKULL = Action(
"Duplicate your own skull and affix it here",
cost = Cost.ACTION.value + 1000*Cost.BONE_FRAGMENT.value,
cost = Cost.ACTION.value * 1 / narrow_challenge(6, Char.ARTISAN_OF_RED_SCIENCE.value) + 1000*Cost.BONE_FRAGMENT.value,
value = -250,
skulls_needed = -1,
skulls = 1
@ -100,7 +104,7 @@ class Skull(Enum):
value = 2500,
skulls_needed = -1,
skulls = 1,
menace = 2
menace = mean_outcome(2, 1, narrow_challenge(4, Char.MONSTROUS_ANATOMY.value))
)
RUBBERY_SKULL = Action(
@ -119,19 +123,20 @@ class Skull(Enum):
skulls_needed = -1,
skulls = 1,
antiquity = 1,
menace = 1
menace = mean_outcome(1, 0, narrow_challenge(6, Char.MONSTROUS_ANATOMY.value))
)
STYGIAN_IVORY = Action(
"Use a Carved Ball of Stygian Ivory to cap off your (Skeleton Type)",
cost = Cost.ACTION.value + Cost.STYGIAN_IVORY.value,
value = 250,
skulls_needed = -1
skulls_needed = -1,
implausibility = mean_outcome(0, 2, narrow_challenge(6, Char.MITHRIDACY.value))
)
VAKE_SKULL = Action(
"Duplicate the Vake's skull and use it to decorate your (Skeleton Type)",
cost = Cost.ACTION.value + 6000*Cost.BONE_FRAGMENT.value,
cost = Cost.ACTION.value + 6000*Cost.BONE_FRAGMENT.value + mean_outcome(0, Cost.ACTION.value + 300*Cost.BONE_FRAGMENT.value, narrow_challenge(6, Char.ARTISAN_OF_RED_SCIENCE.value)),
value = 6500,
skulls_needed = -1,
skulls = 1,

View File

@ -0,0 +1,32 @@
from enum import Enum
class Char(Enum):
"""Character stats"""
SHADOWY = 300
DANGEROUS = 300
PERSUASIVE = 300
WATCHFUL = 300
PLAYER_OF_CHESS = 7
ARTISAN_OF_RED_SCIENCE = 7
GLASSWORK = 7
KATALEPTIC_TOXICOLOGY = 7
MITHRIDACY = 7
MONSTROUS_ANATOMY = 7
SHAPELING_ARTS = 7
BIZARRE = 15
DREADED = 15
RESPECTABLE = 15

View File

@ -0,0 +1,5 @@
try:
from .custom_char import Char
except:
print("Note: custom_char.py does not exist. Using default_char.py")
from .default_char import Char

View File

@ -22,12 +22,12 @@ from .data.torsos import Torso
# This multiplier is applied to the profit margin to avoid losing precision due to rounding.
PROFIT_MARGIN_MULTIPLIER = 10000000
ATTRIBUTE_MULTIPLIER = 10000000
# This is the highest number of attribute to calculate fractional exponents for.
MAXIMUM_ATTRIBUTE = 100
# This is a constant used to calculate difficulty checks. You almost certainly do not need to change this.
DIFFICULTY_SCALER = 0.6
from .challenge_functions import DIFFICULTY_SCALER
def NewIntermediateBoolVar(self, name, expression, domain):
@ -197,21 +197,36 @@ def Solve(shadowy_level, bone_market_fluctuations = None, zoological_mania = Non
model.Add(tentacles == cp_model.LinearExpr.ScalProd(actions.values(), [action.value.tentacles for action in actions.keys()]))
# Amalgamy calculation
multiplied_amalgamy = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, 'multiplied amalgamy')
model.Add(multiplied_amalgamy == cp_model.LinearExpr.ScalProd(actions.values(), [int(action.value.amalgamy*ATTRIBUTE_MULTIPLIER) for action in actions.keys()]))
amalgamy = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, 'amalgamy')
model.Add(amalgamy == cp_model.LinearExpr.ScalProd(actions.values(), [action.value.amalgamy for action in actions.keys()]))
model.AddDivisionEquality(amalgamy, multiplied_amalgamy, ATTRIBUTE_MULTIPLIER)
del multiplied_amalgamy
# Antiquity calculation
multiplied_antiquity = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, 'multiplied antiquity')
model.Add(multiplied_antiquity == cp_model.LinearExpr.ScalProd(actions.values(), [int(action.value.antiquity*ATTRIBUTE_MULTIPLIER) for action in actions.keys()]))
antiquity = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, 'antiquity')
model.Add(antiquity == cp_model.LinearExpr.ScalProd(actions.values(), [action.value.antiquity for action in actions.keys()]))
model.AddDivisionEquality(antiquity, multiplied_antiquity, ATTRIBUTE_MULTIPLIER)
del multiplied_antiquity
# Menace calculation
multiplied_menace = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, 'multiplied menace')
model.Add(multiplied_menace == cp_model.LinearExpr.ScalProd(actions.values(), [int(action.value.menace*ATTRIBUTE_MULTIPLIER) for action in actions.keys()]))
menace = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, 'menace')
model.Add(menace == cp_model.LinearExpr.ScalProd(actions.values(), [action.value.menace for action in actions.keys()]))
model.AddDivisionEquality(menace, multiplied_menace, ATTRIBUTE_MULTIPLIER)
del multiplied_menace
# Implausibility calculation
multiplied_implausibility = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, 'multiplied implausibility')
model.Add(multiplied_implausibility == cp_model.LinearExpr.ScalProd(actions.values(), [int(action.value.implausibility*ATTRIBUTE_MULTIPLIER) for action in actions.keys()]))
implausibility = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, 'implausibility')
model.Add(implausibility == cp_model.LinearExpr.ScalProd(actions.values(), [action.value.implausibility for action in actions.keys()]))
model.AddDivisionEquality(implausibility, multiplied_implausibility, ATTRIBUTE_MULTIPLIER)
del multiplied_implausibility
# Counter-church calculation
# Calculate amount of Counter-church from Holy Relics of the Thigh of Saint Fiacre
@ -1235,16 +1250,16 @@ def Solve(shadowy_level, bone_market_fluctuations = None, zoological_mania = Non
model.Add(net_profit == total_revenue - cost)
# This is necessary to preserve some degree of precision after dividing
multiplied_net_profit = model.NewIntVar(cp_model.INT32_MIN*PROFIT_MARGIN_MULTIPLIER, cp_model.INT32_MAX*PROFIT_MARGIN_MULTIPLIER, 'multiplied net profit')
multiplied_net_profit = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, 'multiplied net profit')
model.AddMultiplicationEquality(multiplied_net_profit, [net_profit, PROFIT_MARGIN_MULTIPLIER])
absolute_multiplied_net_profit = model.NewIntVar(0, cp_model.INT32_MAX*PROFIT_MARGIN_MULTIPLIER, 'absolute multiplied net profit')
absolute_multiplied_net_profit = model.NewIntVar(0, cp_model.INT32_MAX, 'absolute multiplied net profit')
model.AddAbsEquality(absolute_multiplied_net_profit, multiplied_net_profit)
absolute_profit_margin = model.NewIntVar(cp_model.INT32_MIN*PROFIT_MARGIN_MULTIPLIER, cp_model.INT32_MAX*PROFIT_MARGIN_MULTIPLIER, 'absolute profit margin')
absolute_profit_margin = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, 'absolute profit margin')
model.AddDivisionEquality(absolute_profit_margin, absolute_multiplied_net_profit, total_revenue)
profit_margin = model.NewIntVar(cp_model.INT32_MIN*PROFIT_MARGIN_MULTIPLIER, cp_model.INT32_MAX*PROFIT_MARGIN_MULTIPLIER, 'profit margin')
profit_margin = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, 'profit margin')
positive_net_profit = model.NewIntermediateBoolVar('positive net profit', net_profit, cp_model.Domain.FromFlatIntervals([0, cp_model.INT_MAX]))
model.Add(profit_margin == absolute_profit_margin).OnlyEnforceIf(positive_net_profit)