2021-06-08 22:28:15 +00:00
import functools
import enum
import os
from enum import auto
from ortools . sat . python import cp_model
# This multiplier is applied to the profit margin to avoid losing precision due to rounding.
PROFIT_MARGIN_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
# This is the effective level of Shadowy used for attempting to sell.
SHADOWY_LEVEL = 300
# The maximum number of pennies that should be invested in this skeleton.
MAXIMUM_COST = cp_model . INT32_MAX
# The maximum Exhaustion that this skeleton should generate.
MAXIMUM_EXHAUSTION = 4
2021-06-08 23:55:56 +00:00
# The number of pennies needed to produce a quality.
class Value ( enum . Enum ) :
# This is your baseline EPA: the pennies you could generate using an action for a generic grind.
ACTION = 400
2021-06-09 00:55:30 +00:00
# Antique Mystery
ANTIQUE_MYSTERY = 1250
2021-06-11 19:04:48 +00:00
# Favours: Bohemians
# Various opportunity cards
BOHEMIAN_FAVOURS = ACTION
2021-06-09 00:55:30 +00:00
# Bone Fragment
BONE_FRAGMENT = 1
2021-06-09 17:56:24 +00:00
# Bright Brass Skull
# Merrigans Exchange
BRASS_SKULL = 6250
2021-06-09 00:55:30 +00:00
# Cartographer's Hoard
CARTOGRAPHERS_HOARD = 31250
2021-06-11 19:55:55 +00:00
# Favours: The Church
# Various opportunity cards
CHURCH_FAVOURS = ACTION
2021-06-09 00:55:30 +00:00
# Collection Note: There's a 'Package' in London
# Station VIII Lab
COLLECTION_NOTE = ACTION
# Volume of Collated Research
COLLATED_RESEARCH = 250
2021-06-11 19:04:48 +00:00
# Deep-Zee Catch
# Spear-fishing at the bottom of the Evenlode, 7 at a time
DEEP_ZEE_CATCH = ACTION / 7
# Crustacean Pincer
# Ealing Gardens Butcher, 2 at a time
CRUSTACEAN_PINCER = ( ACTION + DEEP_ZEE_CATCH ) / 2
2021-06-11 19:55:55 +00:00
# Femur of a Surface Deer
# Dumbwaiter of Balmoral, 25 at a time
DEER_FEMUR = ACTION / 25
2021-06-09 17:56:24 +00:00
# Favours: The Docks
# Various opportunity cards
DOCK_FAVOURS = ACTION
# Eyeless Skull
# No consistent source
EYELESS_SKULL = cp_model . INT32_MAX
2021-06-11 19:55:55 +00:00
# Holy Relic of the Thigh of Saint Fiacre
# Jericho Locks statue, 2 at a time
FIACRE_THIGH = ( ACTION + 4 * CHURCH_FAVOURS ) / 2
# Fin Bones, Collected
# Hunt and dissect Pinewood Shark, 40 at a time
FIN_BONES = ( 11 * ACTION ) / 40
# Amber-Crusted Fin
# Helicon House
AMBER_FIN = ACTION + 10 * FIN_BONES
2021-06-09 00:55:30 +00:00
# Five-Pointed Ribcage
# Upwards
FIVE_POINTED_RIBCAGE = 9 * ACTION + CARTOGRAPHERS_HOARD
2021-06-09 17:56:24 +00:00
# Esteem of the Guild
# Jericho Parade, 2 at a time
GUILD_ESTEEM = ( ACTION + 5 * DOCK_FAVOURS ) / 2
# Skull in Coral
# Persephone, 1-2 at a time
CORAL_SKULL = 1.5 * ( 2 * ACTION + 3 * GUILD_ESTEEM )
2021-06-09 00:55:30 +00:00
# Headless Skeleton
# These are accumulated while acquiring other qualities.
HEADLESS_SKELETON = 0
2021-06-09 17:56:24 +00:00
# Hinterland Scrip
HINTERLAND_SCRIP = 50
2021-06-11 19:04:48 +00:00
# Fossilised Forelimb
# Anning and Daughters
FOSSILISED_FORELIMB = 50 * HINTERLAND_SCRIP
2021-06-11 19:55:55 +00:00
# Hedonist
# Handsome Townhouse, 3cp at a time
HEDONIST_CP = ACTION / 3
2021-06-11 19:04:48 +00:00
# Human Arm
# These are accumulated while acquiring other qualities.
HUMAN_ARM = 0
2021-06-09 17:56:24 +00:00
# Crate of Incorruptible Biscuits
INCORRUPTIBLE_BISCUITS = 250
# Inkling of Identity
INKLING_OF_IDENTITY = 10
# A Custom-Engraved Skull
# Feast of the Exceptional Rose, sent by one player and accepted by another
ENGRAVED_SKULL = 2 * ACTION + 200 * INKLING_OF_IDENTITY
2021-06-11 19:04:48 +00:00
# Ivory Humerus
# Ealing Gardens statue, 2 at a time
IVORY_HUMERUS = ( ACTION + 4 * BOHEMIAN_FAVOURS ) / 2
2021-06-11 19:55:55 +00:00
# Femur of a Jurassic Beast
# Brawling for yourself, large Bone Market crate, 12 at a time
JURASSIC_FEMUR = ( 10 * ACTION ) / 12
2021-06-11 19:04:48 +00:00
# Knotted Humerus
# These are accumulated while acquiring other qualities.
KNOTTED_HUMERUS = 0
2021-06-09 00:55:30 +00:00
# Nevercold Brass Sliver
NEVERCOLD_BRASS = 1
2021-06-11 19:55:55 +00:00
# Parabolan Orange-apple
# Parabolan Base-camp, electricity and hedonism, 2 at a time
ORANGE_APPLE = ( 2 * ACTION + 100 * BONE_FRAGMENT + 21 * HEDONIST_CP ) / 2
# Ivory Femur
# Bohemian Sculptress
IVORY_FEMUR = ACTION + 750 * BONE_FRAGMENT + 3 * ORANGE_APPLE
2021-06-09 17:56:24 +00:00
# Pentagrammic Skull
# Upwards
PENTAGRAMMIC_SKULL = 9 * ACTION
# Hand-picked Peppercaps
PEPPERCAPS = HINTERLAND_SCRIP
2021-06-09 00:55:30 +00:00
# Knob of Scintillack
SCINTILLACK = 250
# Searing Enigma
SEARING_ENIGMA = 6250
2021-06-09 17:56:24 +00:00
# Carved Ball of Stygian Ivory
STYGIAN_IVORY = 250
2021-06-09 00:55:30 +00:00
# Preserved Surface Blooms
SURFACE_BLOOMS = 250
# Consignment of Scintillack Snuff
# Laboratory Manufacturing
SCINTILLACK_SNUFF = ( ACTION + 8 * SCINTILLACK + SURFACE_BLOOMS ) / 2
# Elation at Feline Oration
# Pinnock
ELATION_AT_FELINE_ORATION = ACTION + 2 * ANTIQUE_MYSTERY + COLLECTION_NOTE + 2 * SCINTILLACK_SNUFF
# Oil of Companionship
# Station VIII Lab
OIL_OF_COMPANIONSHIP = ACTION + ELATION_AT_FELINE_ORATION
2021-06-08 23:55:56 +00:00
# Survey of the Neath's Bones
# Laboratory Research
SURVEY = 6 * ACTION / 25
2021-06-09 00:55:30 +00:00
# Human Ribcage
# Ealing Gardens
HUMAN_RIBCAGE = ACTION + 15 * SURVEY
# Mammoth Ribcage
# Laboratory Research
MAMMOTH_RIBCAGE = 16 * ACTION + HUMAN_RIBCAGE
# Palaeontological Discovery
# Plain of Thirsty Grasses
PALAEONTOLOGICAL_DISCOVERY = ( ACTION + 140 * SURVEY ) / 6
2021-06-11 19:55:55 +00:00
# Helical Thighbone
# Results of Excavation, 6 at a time
HELICAL_THIGH = ( 2 * PALAEONTOLOGICAL_DISCOVERY ) / 6
2021-06-09 00:55:30 +00:00
# Leviathan Frame
# Results of Excavation
LEVIATHAN_FRAME = 25 * PALAEONTOLOGICAL_DISCOVERY
# Thorned Ribcage
# Iron-Toothed Terror Bird
THORNED_RIBCAGE = 6 * ACTION
# Flourishing Ribcage
# Helicon House
FLOURISHING_RIBCAGE = ACTION + HUMAN_RIBCAGE + THORNED_RIBCAGE
2021-06-09 17:56:24 +00:00
# Time Remaining in the Woods
# Compel Ghillie, 5 at a time
TIME_REMAINING_IN_THE_WOODS = ( ACTION + 4 * COLLATED_RESEARCH ) / 5
# Observation: Fox
# Balmoral Woods
FOX_OBSERVATION = 10 * ACTION + 8 * TIME_REMAINING_IN_THE_WOODS
# Doubled Skull
# Keeper of the Marigold Menagerie
DOUBLED_SKULL = ACTION + FOX_OBSERVATION
2021-06-09 00:55:30 +00:00
# Nodule of Trembling Amber
TREMBLING_AMBER = 1250
# Ribcage with a Bouquet of Eight Spines
# Helicon House
RIBCAGE_WITH_EIGHT_SPINES = ACTION + 3 * SEARING_ENIGMA + THORNED_RIBCAGE + 3 * TREMBLING_AMBER
2021-06-09 17:56:24 +00:00
# Rubbery Skull
# Flute Street, including travel due to quality cap
RUBBERY_SKULL = 25 * ACTION
2021-06-09 00:55:30 +00:00
# Rumour of the Upper River
RUMOUR_OF_THE_UPPER_RIVER = 250
# Prismatic Frame
# Expedition at Station VIII
PRISMATIC_FRAME = ACTION + OIL_OF_COMPANIONSHIP + 98 * RUMOUR_OF_THE_UPPER_RIVER
2021-06-11 19:55:55 +00:00
# Unidentified Thigh Bone
# These are accumulated while acquiring other qualities.
UNIDENTIFIED_THIGH = 0
2021-06-09 00:55:30 +00:00
# Nodule of Warm Amber
WARM_AMBER = 10
2021-06-11 19:39:55 +00:00
# Albatross Wing
# Ealing Gardens Butcher, 2 at a time
ALBATROSS_WING = ( ACTION + 2000 * BONE_FRAGMENT + 25 * WARM_AMBER ) / 2
# Bat Wing
# Ealing Gardens Butcher, 2 at a time
BAT_WING = ( ACTION + 100 * BONE_FRAGMENT + 2 * WARM_AMBER ) / 2
2021-06-09 17:56:24 +00:00
# Horned Skull
# Ealing Gardens Butcher
HORNED_SKULL = ACTION + 1000 * BONE_FRAGMENT + 5 * WARM_AMBER
# Plated Skull
# Ealing Gardens Butcher
PLATED_SKULL = ACTION + 1750 * BONE_FRAGMENT + INCORRUPTIBLE_BISCUITS + 25 * WARM_AMBER
# Sabre-toothed Skull
# Ealing Gardens Butcher
SABRE_TOOTHED_SKULL = ACTION + 4900 * BONE_FRAGMENT + 125 * WARM_AMBER
2021-06-11 19:39:55 +00:00
# Wing of a Young Terror Bird
# Ealing Gardens Butcher, 2 at a time
TERROR_BIRD_WING = ( ACTION + 100 * BONE_FRAGMENT + 25 * WARM_AMBER ) / 2
2021-06-09 00:55:30 +00:00
# Warbler Skeleton
# Ealing Gardens Butcher
WARBLER_SKELETON = ACTION + 130 * BONE_FRAGMENT + 2 * WARM_AMBER
# Skeleton with Seven Necks
# Laboratory Research
SKELETON_WITH_SEVEN_NECKS = 16 * ACTION + 1000 * NEVERCOLD_BRASS + WARBLER_SKELETON
2021-06-11 19:55:55 +00:00
# Withered Tentacle
# Helicon House, 3 at a time
WITHERED_TENTACLE = ( ACTION + 5 * WARM_AMBER ) / 3
2021-06-08 23:55:56 +00:00
2021-06-08 22:28:15 +00:00
# Adds a fully-reified implication using an intermediate Boolean variable.
def NewIntermediateBoolVar ( self , name , expression , domain ) :
intermediate = self . NewBoolVar ( name )
self . AddLinearExpressionInDomain ( expression , domain ) . OnlyEnforceIf ( intermediate )
self . AddLinearExpressionInDomain ( expression , domain . Complement ( ) ) . OnlyEnforceIf ( intermediate . Not ( ) )
return intermediate
setattr ( cp_model . CpModel , ' NewIntermediateBoolVar ' , NewIntermediateBoolVar )
del NewIntermediateBoolVar
# Adds an approximate exponentiation equality using a lookup table.
# Set `upto` to a value that is unlikely to come into play.
def AddApproximateExponentiationEquality ( self , target , var , exp , upto ) :
return self . AddAllowedAssignments ( [ target , var ] , [ ( int ( base * * exp ) , base ) for base in range ( upto + 1 ) ] )
setattr ( cp_model . CpModel , ' AddApproximateExponentiationEquality ' , AddApproximateExponentiationEquality )
del AddApproximateExponentiationEquality
# Adds a multiplication equality for any number of terms using intermediate variables.
def AddGeneralMultiplicationEquality ( self , target , * variables ) :
# This is used for producing unique names for intermediate variables.
term_index = 1
def function ( a , b ) :
nonlocal term_index
intermediate = self . NewIntVar ( cp_model . INT32_MIN , cp_model . INT32_MAX , ' {} term {} ' . format ( target . Name ( ) , term_index ) )
term_index + = 1
self . AddMultiplicationEquality ( intermediate , [ a , b ] )
return intermediate
product = functools . reduce ( function , variables )
return self . Add ( target == product )
setattr ( cp_model . CpModel , ' AddGeneralMultiplicationEquality ' , AddGeneralMultiplicationEquality )
del AddGeneralMultiplicationEquality
# A way to convert a skeleton into revenue.
class Buyer ( enum . Enum ) :
A_PALAEONTOLOGIST_WITH_HOARDING_PROPENSITIES = auto ( )
A_NAIVE_COLLECTOR = auto ( )
A_FAMILIAR_BOHEMIAN_SCULPTRESS = auto ( )
A_PEDAGOGICALLY_INCLINED_GRANDMOTHER = auto ( )
A_THEOLOGIAN_OF_THE_OLD_SCHOOL = auto ( )
AN_ENTHUSIAST_OF_THE_ANCIENT_WORLD = auto ( )
MRS_PLENTY = auto ( )
A_TENTACLED_SERVANT = auto ( )
AN_INVESTMENT_MINDED_AMBASSADOR = auto ( )
A_TELLER_OF_TERRORS = auto ( )
A_TENTACLED_ENTREPRENEUR = auto ( )
AN_AUTHOR_OF_GOTHIC_TALES = auto ( )
A_ZAILOR_WITH_PARTICULAR_INTERESTS = auto ( )
A_RUBBERY_COLLECTOR = auto ( )
A_CONSTABLE = auto ( )
AN_ENTHUSIAST_IN_SKULLS = auto ( )
A_DREARY_MIDNIGHTER = auto ( )
THE_DUMBWAITER_OF_BALMORAL = auto ( )
# An action that affects a skeleton's qualities.
class Action :
2021-06-08 22:49:45 +00:00
def __init__ ( self , name , cost , torso_style = None , value = 0 , skulls_needed = 0 , limbs_needed = 0 , tails_needed = 0 , skulls = 0 , arms = 0 , legs = 0 , tails = 0 , wings = 0 , fins = 0 , tentacles = 0 , amalgamy = 0 , antiquity = 0 , menace = 0 , implausibility = 0 , counter_church = 0 , exhaustion = 0 ) :
2021-06-08 22:28:15 +00:00
self . name = name
# Cost in pennies of using this action, including the value of the actions spent
self . cost = cost
2021-06-08 22:49:45 +00:00
# Skeleton: Torso Style
self . torso_style = torso_style
2021-06-08 22:28:15 +00:00
# Approximate Value of Your Skeleton in Pennies
self . value = value
# Skeleton: Skulls Needed
self . skulls_needed = skulls_needed
# Skeleton: Limbs Needed
self . limbs_needed = limbs_needed
# Skeleton: Tails Needed
self . tails_needed = tails_needed
# Skeleton: Skulls
self . skulls = skulls
# Skeleton: Arms
self . arms = arms
# Skeleton: Legs
self . legs = legs
# Skeleton: Tails
self . tails = tails
# Skeleton: Wings
self . wings = wings
# Skeleton: Fins
self . fins = fins
# Skeleton: Tentacles
self . tentacles = tentacles
# Skeleton: Amalgamy
self . amalgamy = amalgamy
# Skeleton: Antiquity
self . antiquity = antiquity
# Skeleton: Menace
self . menace = menace
# Skeleton: Self-Evident Implausibility
self . implausibility = implausibility
# Skeleton: Support for a Counter-church Theology
self . counter_church = counter_church
# Bone Market Exhaustion
self . exhaustion = exhaustion
def __str__ ( self ) :
return str ( self . name )
2021-06-08 23:23:30 +00:00
# Actions that initiate a skeleton.
class Torso ( enum . Enum ) :
2021-06-09 00:55:30 +00:00
HEADLESS_HUMANOID = Action (
" Reassemble your Headless Humanoid " ,
cost = Value . ACTION . value + Value . HEADLESS_SKELETON . value ,
torso_style = 10 ,
value = 250 ,
skulls_needed = 1 ,
arms = 2 ,
legs = 2
)
2021-06-08 23:23:30 +00:00
2021-06-09 00:55:30 +00:00
# Licentiate
2021-06-09 15:29:56 +00:00
# VICTIM_SKELETON = Action(
2021-06-09 00:55:30 +00:00
# "Supply a skeleton of your own",
# cost = Value.ACTION.value,
# torso_style = 10,
# value = 250,
# skulls_needed = 1,
# arms = 2,
# legs = 2
# )
HUMAN_RIBCAGE = Action (
" Build on the Human Ribcage " ,
cost = Value . ACTION . value + Value . HUMAN_RIBCAGE . value ,
torso_style = 15 ,
value = 1250 ,
skulls_needed = 1 ,
limbs_needed = 4
)
THORNED_RIBCAGE = Action (
" Make something of your Thorned Ribcage " ,
cost = Value . ACTION . value + Value . THORNED_RIBCAGE . value ,
torso_style = 20 ,
value = 1250 ,
skulls_needed = 1 ,
limbs_needed = 4 ,
tails_needed = 1 ,
amalgamy = 1 ,
menace = 1
)
SKELETON_WITH_SEVEN_NECKS = Action (
" Build on the Skeleton with Seven Necks " ,
cost = Value . ACTION . value + Value . SKELETON_WITH_SEVEN_NECKS . value ,
torso_style = 30 ,
value = 6250 ,
skulls_needed = 7 ,
limbs_needed = 2 ,
legs = 2 ,
amalgamy = 2 ,
menace = 1
)
FLOURISHING_RIBCAGE = Action (
" Build on the Flourishing Ribcage " ,
cost = Value . ACTION . value + Value . FLOURISHING_RIBCAGE . value ,
torso_style = 40 ,
value = 1250 ,
skulls_needed = 2 ,
limbs_needed = 6 ,
tails_needed = 1 ,
amalgamy = 2
)
MAMMOTH_RIBCAGE = Action (
" Build on the Mammoth Ribcage " ,
cost = Value . ACTION . value + Value . MAMMOTH_RIBCAGE . value ,
torso_style = 50 ,
value = 6250 ,
skulls_needed = 1 ,
limbs_needed = 4 ,
tails_needed = 1 ,
antiquity = 2
)
RIBCAGE_WITH_A_BOUQUET_OF_EIGHT_SPINES = Action (
" Build on the Ribcage with the Eight Spines " ,
cost = Value . ACTION . value + Value . RIBCAGE_WITH_EIGHT_SPINES . value ,
torso_style = 60 ,
value = 31250 ,
skulls_needed = 8 ,
limbs_needed = 4 ,
tails_needed = 1 ,
amalgamy = 1 ,
menace = 2
)
LEVIATHAN_FRAME = Action ( " Build on the Leviathan Frame " ,
cost = Value . ACTION . value + Value . LEVIATHAN_FRAME . value ,
torso_style = 70 ,
value = 31250 ,
skulls_needed = 1 ,
limbs_needed = 2 ,
tails = 1 ,
antiquity = 1 ,
menace = 1
)
PRISMATIC_FRAME = Action (
" Build on the Prismatic Frame " ,
cost = Value . ACTION . value + Value . PRISMATIC_FRAME . value ,
torso_style = 80 ,
value = 31250 ,
skulls_needed = 3 ,
limbs_needed = 3 ,
tails_needed = 3 ,
amalgamy = 2 ,
antiquity = 2
)
FIVE_POINTED_FRAME = Action (
" Build on the Five-Pointed Frame " ,
cost = Value . ACTION . value + Value . FIVE_POINTED_RIBCAGE . value ,
torso_style = 100 ,
value = 31250 ,
skulls_needed = 5 ,
limbs_needed = 5 ,
amalgamy = 2 ,
menace = 1
)
2021-06-08 23:23:30 +00:00
def __str__ ( self ) :
return str ( self . value )
2021-06-09 15:29:56 +00:00
# Actions that are taken immediately after starting a skeleton.
class Skull ( enum . Enum ) :
2021-06-09 17:56:24 +00:00
BAPTIST_SKULL = Action (
" Duplicate the skull of John the Baptist, if you can call that a skull " ,
cost = Value . ACTION . value + 500 * Value . BONE_FRAGMENT . value + 10 * Value . PEPPERCAPS . value ,
value = 1500 ,
skulls_needed = - 1 ,
skulls = 1 ,
counter_church = 2
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
BRASS_SKULL = Action (
" Affix a Bright Brass Skull to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . BRASS_SKULL . value + 200 * Value . NEVERCOLD_BRASS . value ,
value = 6500 ,
skulls_needed = - 1 ,
skulls = 1 ,
implausibility = 2
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
CORAL_SKULL = Action (
" Affix a Skull in Coral to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . CORAL_SKULL . value + Value . SCINTILLACK . value ,
value = 1750 ,
skulls_needed = - 1 ,
skulls = 1 ,
amalgamy = 2
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
DOUBLED_SKULL = Action (
" Affix a Doubled Skull to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . DOUBLED_SKULL . value ,
value = 6250 ,
skulls_needed = - 1 ,
skulls = 2 ,
amalgamy = 1 ,
antiquity = 2
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
# Adds Exhaustion
# ENGRAVED_SKULL = Action(
# "Affix a Custom-Engraved Skull to your (Skeleton Type)",
# cost = Value.ACTION.value + Value.ENGRAVED_SKULL.value,
# value = 10000,
# skulls_needed = -1,
# skulls = 1,
# exhaustion = 2
# )
EYELESS_SKULL = Action (
" Affix an Eyeless Skull to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . EYELESS_SKULL . value ,
value = 3000 ,
skulls_needed = - 1 ,
skulls = 1 ,
menace = 2
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
HORNED_SKULL = Action (
" Affix a Horned Skull to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . HORNED_SKULL . value ,
value = 1250 ,
skulls_needed = - 1 ,
skulls = 1 ,
antiquity = 1 ,
menace = 2
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
# Seeking the Name of Mr. Eaten
# OWN_SKULL = Action(
# "Duplicate your own skull and affix it here",
# cost = Value.ACTION.value + 1000*Value.BONE_FRAGMENT.value,
# value = -250,
# skulls_needed = -1,
# skulls = 1
# )
PENTAGRAMMIC_SKULL = Action (
" Affix a Pentagrammic Skull to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . PENTAGRAMMIC_SKULL . value ,
value = 1250 ,
skulls_needed = - 1 ,
skulls = 1 ,
amalgamy = 2 ,
menace = 1
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
PLATED_SKULL = Action (
" Affix a Plated Skull to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . PLATED_SKULL . value ,
value = 2500 ,
skulls_needed = - 1 ,
skulls = 1 ,
menace = 2
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
RUBBERY_SKULL = Action (
" Affix a Rubbery Skull to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . RUBBERY_SKULL . value ,
value = 600 ,
skulls_needed = - 1 ,
skulls = 1 ,
amalgamy = 1
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
SABRE_TOOTHED_SKULL = Action (
" Affix a Sabre-toothed Skull to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . SABRE_TOOTHED_SKULL . value ,
value = 6250 ,
skulls_needed = - 1 ,
skulls = 1 ,
antiquity = 1 ,
menace = 1
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
STYGIAN_IVORY = Action (
" Use a Carved Ball of Stygian Ivory to cap off your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . STYGIAN_IVORY . value ,
value = 250 ,
skulls_needed = - 1
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
VAKE_SKULL = Action (
" Duplicate the Vake ' s skull and use it to decorate your (Skeleton Type) " ,
cost = Value . ACTION . value + 6000 * Value . BONE_FRAGMENT . value ,
value = 6500 ,
skulls_needed = - 1 ,
skulls = 1 ,
menace = 3
)
2021-06-09 15:29:56 +00:00
2021-06-09 17:56:24 +00:00
# Licentiate
# VICTIM_SKULL = Action(
# "Cap this with a victim’ s skull",
# cost = Value.ACTION.value,
# value = 250,
# skulls_needed = -1,
# skulls = 1
# )
2021-06-09 15:29:56 +00:00
def __str__ ( self ) :
return str ( self . value )
2021-06-11 18:43:37 +00:00
# Actions that are taken once all skulls are added to a skeleton.
class Appendage ( enum . Enum ) :
2021-06-11 23:04:21 +00:00
# Cost from this scales with limbs and is partially implemented separately
ADD_JOINTS = Action (
" Add four more joints to your skeleton " ,
cost = Value . ACTION . value + Value . TREMBLING_AMBER . value ,
limbs_needed = 4 ,
amalgamy = 2
)
2021-06-11 19:39:55 +00:00
ALBATROSS_WING = Action (
" Put an Albatross Wing on your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . ALBATROSS_WING . value ,
value = 1250 ,
limbs_needed = - 1 ,
wings = 1 ,
amalgamy = 1
)
2021-06-11 19:55:55 +00:00
AMBER_FIN = Action (
" Attach the Amber-Crusted Fin to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . AMBER_FIN . value ,
value = 1500 ,
limbs_needed = - 1 ,
fins = 1 ,
amalgamy = 1 ,
menace = 1
)
2021-06-11 19:39:55 +00:00
BAT_WING = Action (
" Add a Bat Wing to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . BAT_WING . value ,
value = 1 ,
limbs_needed = - 1 ,
wings = 1 ,
menace = - 1
)
2021-06-11 19:04:48 +00:00
CRUSTACEAN_PINCER = Action (
" Apply a Crustacean Pincer to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . CRUSTACEAN_PINCER . value ,
limbs_needed = - 1 ,
arms = 1 ,
menace = 1
)
2021-06-11 18:43:37 +00:00
2021-06-11 19:55:55 +00:00
DEER_FEMUR = Action (
" Apply the Femur of a Surface Deer to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . DEER_FEMUR . value ,
value = 10 ,
limbs_needed = - 1 ,
legs = 1 ,
menace = - 1
)
# Counter-Church theology from this scales with torso style and is implemented separately
FIACRE_THIGH = Action (
" Affix Saint Fiacre ' s Thigh Relic to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . FIACRE_THIGH . value ,
value = 1250 ,
limbs_needed = - 1 ,
legs = 1
)
FIN_BONES = Action (
" Put Fins on your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . FIN_BONES . value ,
value = 50 ,
limbs_needed = - 1 ,
fins = 1
)
2021-06-11 19:04:48 +00:00
FOSSILISED_FORELIMB = Action (
" Apply a Fossilised Forelimb to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . FOSSILISED_FORELIMB . value ,
value = 2750 ,
limbs_needed = - 1 ,
arms = 1 ,
antiquity = 2
)
2021-06-11 18:43:37 +00:00
2021-06-11 19:55:55 +00:00
HELICAL_THIGH = Action (
" Affix the Helical Thighbone to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . HELICAL_THIGH . value ,
value = 300 ,
limbs_needed = - 1 ,
legs = 1 ,
amalgamy = 2
)
2021-06-11 19:04:48 +00:00
HUMAN_ARM = Action (
" Join a Human Arm to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . HUMAN_ARM . value ,
value = 250 ,
limbs_needed = - 1 ,
arms = 1 ,
menace = - 1
)
2021-06-11 18:43:37 +00:00
2021-06-11 19:55:55 +00:00
IVORY_FEMUR = Action (
" Apply an Ivory Femur to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . IVORY_FEMUR . value ,
value = 6500 ,
limbs_needed = - 1 ,
legs = 1
)
2021-06-11 19:04:48 +00:00
IVORY_HUMERUS = Action (
" Apply an Ivory Humerus to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . IVORY_HUMERUS . value ,
value = 1500 ,
limbs_needed = - 1 ,
arms = 1
)
2021-06-11 18:43:37 +00:00
2021-06-11 19:55:55 +00:00
JURASSIC_THIGH = Action (
" Apply a Jurassic Thigh Bone to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . JURASSIC_FEMUR . value ,
value = 300 ,
limbs_needed = - 1 ,
legs = 1 ,
antiquity = 1
)
2021-06-11 19:04:48 +00:00
KNOTTED_HUMERUS = Action (
" Apply a Knotted Humerus to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . KNOTTED_HUMERUS . value ,
value = 150 ,
limbs_needed = - 1 ,
arms = 1 ,
amalgamy = 1
)
2021-06-11 18:43:37 +00:00
2021-06-11 19:39:55 +00:00
TERROR_BIRD_WING = Action (
" Add the Wing of a Young Terror Bird to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . TERROR_BIRD_WING . value ,
value = 250 ,
limbs_needed = - 1 ,
wings = 1 ,
antiquity = 1 ,
menace = 1
)
2021-06-11 19:34:45 +00:00
2021-06-11 19:55:55 +00:00
UNIDENTIFIED_THIGH = Action (
" Apply an Unidentified Thigh Bone to your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . UNIDENTIFIED_THIGH . value ,
value = 100 ,
limbs_needed = - 1 ,
legs = 1
)
WITHERED_TENTACLE = Action (
" Put a Withered Tentacle on your (Skeleton Type) " ,
cost = Value . ACTION . value + Value . WITHERED_TENTACLE . value ,
value = 250 ,
limbs_needed = - 1 ,
tentacles = 1 ,
antiquity = - 1
)
2021-06-11 18:43:37 +00:00
def __str__ ( self ) :
return str ( self . value )
2021-06-09 15:29:56 +00:00
2021-06-08 22:28:15 +00:00
# Which kind of skeleton is to be declared.
class Declaration ( enum . Enum ) :
2021-06-08 23:55:56 +00:00
CHIMERA = Action ( " Declare your (Skeleton Type) a completed Chimera " , cost = Value . ACTION . value , implausibility = 3 )
HUMANOID = Action ( " Declare your (Skeleton Type) a completed Humanoid " , cost = Value . ACTION . value )
APE = Action ( " Declare your (Skeleton Type) a completed Ape " , cost = Value . ACTION . value )
MONKEY = Action ( " Declare your (Skeleton Type) a completed Monkey " , cost = Value . ACTION . value )
BIRD = Action ( " Declare your (Skeleton Type) a completed Bird " , cost = Value . ACTION . value )
CURATOR = Action ( " Declare your (Skeleton Type) a completed Curator " , cost = Value . ACTION . value )
REPTILE = Action ( " Declare your (Skeleton Type) a completed Reptile " , cost = Value . ACTION . value )
AMPHIBIAN = Action ( " Declare your (Skeleton Type) a completed Amphibian " , cost = Value . ACTION . value )
FISH = Action ( " Declare your (Skeleton Type) a completed Fish " , cost = Value . ACTION . value )
INSECT = Action ( " Declare your (Skeleton Type) a completed Insect " , cost = Value . ACTION . value )
SPIDER = Action ( " Declare your (Skeleton Type) a completed Spider " , cost = Value . ACTION . value )
2021-06-08 22:28:15 +00:00
def __str__ ( self ) :
return str ( self . value )
# Which skeleton attribute is currently boosted.
class Fluctuation ( enum . Enum ) :
ANTIQUITY = 1
AMALGAMY = 2
def create_data_model ( ) :
data = { }
data [ ' buyer ' ] = Buyer . AN_ENTHUSIAST_IN_SKULLS
# The current value of Bone Market Fluctuations, which grants various bonuses to certain buyers.
data [ ' bone_market_fluctuations ' ] = Fluctuation . AMALGAMY
# The current value of Zoological Mania, which grants a 10% bonus to value for a certain declaration.
data [ ' zoological_mania ' ] = Declaration . AMPHIBIAN
2021-06-11 18:43:37 +00:00
data [ ' actions ' ] = [ torso . value for torso in Torso ] + [ skull . value for skull in Skull ] + [ appendage . value for appendage in Appendage ] + [
2021-06-08 22:28:15 +00:00
# Carpenter's Granddaughter, 2 at a time
2021-06-08 23:55:56 +00:00
Action ( " Apply Plaster Tail Bones to your (Skeleton Type) " , cost = Value . ACTION . value * 1.5 + Value . SURVEY . value * 5 , value = 250 , tails_needed = - 1 , tails = 1 , implausibility = 1 ) ,
Action ( " Apply a Tomb-Lion ' s Tail to your (Skeleton Type) " , cost = 220 + Value . ACTION . value * 2 , value = 250 , tails_needed = - 1 , tails = 1 , antiquity = 1 ) ,
2021-06-08 22:28:15 +00:00
# Geology of Winewound
2021-06-08 23:55:56 +00:00
Action ( " Apply a Jet Black Stinger to your (Skeleton Type) " , cost = Value . ACTION . value * 2 + Value . SURVEY . value , value = 50 , tails_needed = - 1 , tails = 1 , menace = 2 ) ,
2021-06-08 22:28:15 +00:00
# No consistent source
# Action("Apply an Obsidian Chitin Tail to your (Skeleton Type)", cost = cp_model.INT32_MAX, value = 500, tails_needed = -1, tails = 1, amalgamy = 1),
# Helicon House, 3 at a time
2021-06-08 23:55:56 +00:00
Action ( " Apply a Withered Tentacle as a tail on your (Skeleton Type) " , cost = 50 / 3 + Value . ACTION . value * 4 / 3 , value = 250 , tails_needed = - 1 , tails = 1 , antiquity = - 1 ) ,
2021-06-08 22:28:15 +00:00
# This actually sets Skeleton: Tails Needed to 0
2021-06-08 23:55:56 +00:00
Action ( " Decide your Tailless Animal needs no tail " , cost = Value . ACTION . value , tails_needed = - 1 ) ,
Action ( " Remove the tail from your (Skeleton Type) " , cost = Value . ACTION . value , tails = - 1 ) ,
2021-06-08 22:28:15 +00:00
2021-06-08 23:55:56 +00:00
Action ( " Make your skeleton less dreadful " , cost = Value . ACTION . value , menace = - 2 ) ,
Action ( " Disguise the amalgamy of this piece " , cost = 25 + Value . ACTION . value , amalgamy = - 2 ) ,
Action ( " Carve away some evidence of age " , cost = Value . ACTION . value , antiquity = - 2 )
2021-06-08 22:28:15 +00:00
]
return data
def Solve ( ) :
data = create_data_model ( )
model = cp_model . CpModel ( )
# Any number of any action, except only one torso
torsos = { }
actions = { }
for action in data [ ' actions ' ] :
2021-06-08 22:49:45 +00:00
if action . torso_style is not None :
2021-06-08 22:28:15 +00:00
torsos [ action ] = model . NewBoolVar ( action . name )
actions [ action ] = torsos [ action ]
else :
actions [ action ] = model . NewIntVar ( 0 , cp_model . INT32_MAX , action . name )
model . Add ( cp_model . LinearExpr . Sum ( torsos . values ( ) ) == 1 )
# Skeleton must be declared something
declarations = { }
for declaration in Declaration :
declarations [ declaration ] = model . NewBoolVar ( declaration . value . name )
actions [ declaration . value ] = declarations [ declaration ]
model . Add ( cp_model . LinearExpr . Sum ( declarations . values ( ) ) == 1 )
# Value calculation
original_value = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' original value ' )
model . Add ( cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . value for action in actions . keys ( ) ] ) == original_value )
multiplied_value = model . NewIntVar ( 0 , cp_model . INT32_MAX * 11 , " multiplied value " )
model . Add ( multiplied_value == original_value * 11 ) . OnlyEnforceIf ( declarations [ data [ ' zoological_mania ' ] ] )
model . Add ( multiplied_value == original_value * 10 ) . OnlyEnforceIf ( declarations [ data [ ' zoological_mania ' ] ] . Not ( ) )
value = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' value ' )
model . AddDivisionEquality ( value , multiplied_value , 10 )
del original_value , multiplied_value
# Torso Style calculation
torso_style = model . NewIntVarFromDomain ( cp_model . Domain . FromValues ( [ torso . torso_style for torso in torsos . keys ( ) ] ) , ' torso_style ' )
for torso , torso_variable in torsos . items ( ) :
model . Add ( torso_style == torso . torso_style ) . OnlyEnforceIf ( torso_variable )
# Skulls calculation
skulls = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' skulls ' )
model . Add ( skulls == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . skulls for action in actions . keys ( ) ] ) )
# Arms calculation
arms = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' arms ' )
model . Add ( arms == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . arms for action in actions . keys ( ) ] ) )
# Legs calculation
legs = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' legs ' )
model . Add ( legs == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . legs for action in actions . keys ( ) ] ) )
# Tails calculation
tails = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' tails ' )
model . Add ( tails == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . tails for action in actions . keys ( ) ] ) )
# Wings calculation
wings = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' wings ' )
model . Add ( wings == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . wings for action in actions . keys ( ) ] ) )
# Fins calculation
fins = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' fins ' )
model . Add ( fins == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . fins for action in actions . keys ( ) ] ) )
# Tentacles calculation
tentacles = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' tentacles ' )
model . Add ( tentacles == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . tentacles for action in actions . keys ( ) ] ) )
# Amalgamy calculation
amalgamy = model . NewIntVar ( cp_model . INT32_MIN , cp_model . INT32_MAX , ' amalgamy ' )
model . Add ( amalgamy == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . amalgamy for action in actions . keys ( ) ] ) )
# Antiquity calculation
antiquity = model . NewIntVar ( cp_model . INT32_MIN , cp_model . INT32_MAX , ' antiquity ' )
model . Add ( antiquity == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . antiquity for action in actions . keys ( ) ] ) )
# Menace calculation
menace = model . NewIntVar ( cp_model . INT32_MIN , cp_model . INT32_MAX , ' menace ' )
model . Add ( menace == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . menace for action in actions . keys ( ) ] ) )
# Implausibility calculation
implausibility = model . NewIntVar ( cp_model . INT32_MIN , cp_model . INT32_MAX , ' implausibility ' )
model . Add ( implausibility == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . implausibility for action in actions . keys ( ) ] ) )
# Counter-church calculation
# Calculate amount of Counter-church from Holy Relics of the Thigh of Saint Fiacre
holy_relic = next ( filter ( lambda action : action [ 0 ] . name == " Affix Saint Fiacre ' s Thigh Relic to your (Skeleton Type) " , actions . items ( ) ) ) [ 1 ]
torso_style_divided_by_ten = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' torso style divided by ten ' )
model . AddDivisionEquality ( torso_style_divided_by_ten , torso_style , 10 )
holy_relic_counter_church = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' holy relic counter-church ' )
model . AddMultiplicationEquality ( holy_relic_counter_church , [ holy_relic , torso_style_divided_by_ten ] )
counter_church = model . NewIntVar ( cp_model . INT32_MIN , cp_model . INT32_MAX , ' counter-church ' )
model . Add ( counter_church == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . counter_church for action in actions . keys ( ) ] ) + holy_relic_counter_church )
del holy_relic , torso_style_divided_by_ten , holy_relic_counter_church
# Exhaustion calculation
exhaustion = model . NewIntVar ( 0 , MAXIMUM_EXHAUSTION , ' exhaustion ' )
# Profit intermediate variables
primary_revenue = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' primary revenue ' )
secondary_revenue = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' secondary revenue ' )
total_revenue = model . NewIntVar ( 0 , cp_model . INT32_MAX * 2 , ' total revenue ' )
model . Add ( total_revenue == cp_model . LinearExpr . Sum ( [ primary_revenue , secondary_revenue ] ) )
# Cost
# Calculate value of actions needed to sell the skeleton.
difficulty_level = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' difficulty level ' )
non_zero_difficulty_level = model . NewIntVar ( 1 , cp_model . INT32_MAX , ' non-zero difficulty level ' )
model . AddMaxEquality ( non_zero_difficulty_level , [ difficulty_level , 1 ] )
sale_actions_times_action_value = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' sale actions times action value ' )
2021-06-08 23:55:56 +00:00
model . AddDivisionEquality ( sale_actions_times_action_value , model . NewConstant ( round ( DIFFICULTY_SCALER * SHADOWY_LEVEL * Value . ACTION . value ) ) , non_zero_difficulty_level )
2021-06-08 22:28:15 +00:00
abstract_sale_cost = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' abstract sale cost ' )
2021-06-08 23:55:56 +00:00
model . AddDivisionEquality ( abstract_sale_cost , Value . ACTION . value * * 2 , sale_actions_times_action_value )
2021-06-08 22:28:15 +00:00
sale_cost = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' sale cost ' )
2021-06-08 23:55:56 +00:00
model . AddMaxEquality ( sale_cost , [ abstract_sale_cost , Value . ACTION . value ] )
2021-06-08 22:28:15 +00:00
del non_zero_difficulty_level , sale_actions_times_action_value , abstract_sale_cost
# Calculate cost of adding joints
# This is a partial sum formula.
add_joints_amber_cost = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' add joints amber cost ' )
add_joints = next ( filter ( lambda action : action [ 0 ] . name == " Add four more joints to your skeleton " , actions . items ( ) ) ) [ 1 ]
base_joints = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' base joints ' )
model . Add ( base_joints == cp_model . LinearExpr . ScalProd ( torsos . values ( ) , [ action . limbs_needed for torso in torsos . keys ( ) ] ) )
add_joints_amber_cost_multiple = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' add joints amber cost multiple ' )
add_joints_amber_cost_multiple_first_term = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' add joints amber cost multiple first term ' )
2021-06-11 23:04:21 +00:00
model . AddGeneralMultiplicationEquality ( add_joints_amber_cost_multiple_first_term , 25 , base_joints , base_joints , add_joints )
2021-06-08 22:28:15 +00:00
add_joints_amber_cost_multiple_second_term = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' add joints amber cost multiple second term ' )
2021-06-11 23:04:21 +00:00
model . AddGeneralMultiplicationEquality ( add_joints_amber_cost_multiple_second_term , 100 , base_joints , add_joints , add_joints )
2021-06-08 22:28:15 +00:00
add_joints_amber_cost_multiple_third_term = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' add joints amber cost multiple third term ' )
2021-06-11 23:04:21 +00:00
model . AddGeneralMultiplicationEquality ( add_joints_amber_cost_multiple_third_term , 100 , base_joints , add_joints )
2021-06-08 22:28:15 +00:00
add_joints_amber_cost_multiple_fourth_term = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' add joints amber cost multiple fourth term ' )
add_joints_amber_cost_multiple_fourth_term_numerator = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' add joints amber cost multiple fourth term numerator ' )
add_joints_amber_cost_multiple_fourth_term_numerator_first_term = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' add joints amber cost multiple fourth term numerator first term ' )
2021-06-11 23:04:21 +00:00
model . AddGeneralMultiplicationEquality ( add_joints_amber_cost_multiple_fourth_term_numerator_first_term , 400 , add_joints , add_joints , add_joints )
model . Add ( add_joints_amber_cost_multiple_fourth_term_numerator == add_joints_amber_cost_multiple_fourth_term_numerator_first_term + 200 * add_joints )
2021-06-08 22:28:15 +00:00
model . AddDivisionEquality ( add_joints_amber_cost_multiple_fourth_term , add_joints_amber_cost_multiple_fourth_term_numerator , 3 )
del add_joints_amber_cost_multiple_fourth_term_numerator , add_joints_amber_cost_multiple_fourth_term_numerator_first_term
add_joints_amber_cost_multiple_fifth_term = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' add joints amber cost multiple fifth term ' )
2021-06-11 23:04:21 +00:00
model . AddGeneralMultiplicationEquality ( add_joints_amber_cost_multiple_fifth_term , 200 , add_joints , add_joints )
2021-06-08 22:28:15 +00:00
model . Add ( add_joints_amber_cost_multiple == add_joints_amber_cost_multiple_first_term + add_joints_amber_cost_multiple_second_term - add_joints_amber_cost_multiple_third_term + add_joints_amber_cost_multiple_fourth_term - add_joints_amber_cost_multiple_fifth_term )
del add_joints_amber_cost_multiple_first_term , add_joints_amber_cost_multiple_second_term , add_joints_amber_cost_multiple_third_term , add_joints_amber_cost_multiple_fourth_term , add_joints_amber_cost_multiple_fifth_term
2021-06-11 23:04:21 +00:00
model . AddGeneralMultiplicationEquality ( add_joints_amber_cost , add_joints , add_joints_amber_cost_multiple , Value . WARM_AMBER . value )
2021-06-08 22:28:15 +00:00
del add_joints , add_joints_amber_cost_multiple
cost = model . NewIntVar ( 0 , MAXIMUM_COST , ' cost ' )
model . Add ( cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ int ( action . cost ) for action in actions . keys ( ) ] ) + add_joints_amber_cost + sale_cost == cost )
del sale_cost , add_joints_amber_cost
# Type of skeleton
skeleton_in_progress = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' skeleton in progress ' )
# Chimera
model . Add ( skeleton_in_progress == 100 ) \
. OnlyEnforceIf ( declarations [ Declaration . CHIMERA ] )
# Humanoid
model . Add ( skeleton_in_progress == 110 ) \
. OnlyEnforceIf ( declarations [ Declaration . HUMANOID ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' humanoid antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ cp_model . INT_MIN , 0 ] ) ) )
# Ancient Humanoid (UNCERTAIN)
model . Add ( skeleton_in_progress == 111 ) \
. OnlyEnforceIf ( declarations [ Declaration . HUMANOID ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' ancient humanoid antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 1 , 5 ] ) ) )
# Neanderthal
model . Add ( skeleton_in_progress == 112 ) \
. OnlyEnforceIf ( declarations [ Declaration . HUMANOID ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' neanderthal antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 6 , cp_model . INT_MAX ] ) ) )
# Ape (UNCERTAIN)
model . Add ( skeleton_in_progress == 120 ) \
. OnlyEnforceIf ( declarations [ Declaration . APE ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' ape antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ cp_model . INT_MIN , 1 ] ) ) )
# Primordial Ape (UNCERTAIN)
model . Add ( skeleton_in_progress == 121 ) \
. OnlyEnforceIf ( declarations [ Declaration . APE ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' primordial ape antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 2 , cp_model . INT_MAX ] ) ) )
# Monkey
model . Add ( skeleton_in_progress == 125 ) \
. OnlyEnforceIf ( declarations [ Declaration . MONKEY ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' monkey antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ cp_model . INT_MIN , 0 ] ) ) )
# Catarrhine Monkey (UNCERTAIN)
model . Add ( skeleton_in_progress == 126 ) \
. OnlyEnforceIf ( declarations [ Declaration . MONKEY ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' catarrhine monkey 126 antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 1 , 8 ] ) ) )
# Catarrhine Monkey
model . Add ( skeleton_in_progress == 128 ) \
. OnlyEnforceIf ( declarations [ Declaration . MONKEY ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' catarrhine monkey 128 antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 9 , cp_model . INT_MAX ] ) ) )
# Crocodile
model . Add ( skeleton_in_progress == 160 ) \
. OnlyEnforceIf ( declarations [ Declaration . REPTILE ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' crocodile antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ cp_model . INT_MIN , 1 ] ) ) )
# Dinosaur
model . Add ( skeleton_in_progress == 161 ) \
. OnlyEnforceIf ( declarations [ Declaration . REPTILE ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' dinosaur antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 2 , 4 ] ) ) )
# Mesosaur (UNCERTAIN)
model . Add ( skeleton_in_progress == 162 ) \
. OnlyEnforceIf ( declarations [ Declaration . REPTILE ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' mesosaur antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 5 , cp_model . INT_MAX ] ) ) )
# Toad
model . Add ( skeleton_in_progress == 170 ) \
. OnlyEnforceIf ( declarations [ Declaration . AMPHIBIAN ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' toad antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ cp_model . INT_MIN , 1 ] ) ) )
# Primordial Amphibian
model . Add ( skeleton_in_progress == 171 ) \
. OnlyEnforceIf ( declarations [ Declaration . AMPHIBIAN ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' primordial amphibian antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 2 , 4 ] ) ) )
# Temnospondyl
model . Add ( skeleton_in_progress == 172 ) \
. OnlyEnforceIf ( declarations [ Declaration . AMPHIBIAN ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' temnospondyl antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 5 , cp_model . INT_MAX ] ) ) )
# Owl
model . Add ( skeleton_in_progress == 180 ) \
. OnlyEnforceIf ( declarations [ Declaration . BIRD ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' owl antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ cp_model . INT_MIN , 1 ] ) ) )
# Archaeopteryx
model . Add ( skeleton_in_progress == 181 ) \
. OnlyEnforceIf ( declarations [ Declaration . BIRD ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' archaeopteryx antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 2 , 4 ] ) ) )
# Ornithomimosaur (UNCERTAIN)
model . Add ( skeleton_in_progress == 182 ) \
. OnlyEnforceIf ( declarations [ Declaration . BIRD ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' ornithomimosaur antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 5 , cp_model . INT_MAX ] ) ) )
# Lamprey
model . Add ( skeleton_in_progress == 190 ) \
. OnlyEnforceIf ( declarations [ Declaration . FISH ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' lamprey antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ cp_model . INT_MIN , 0 ] ) ) )
# Coelacanth (UNCERTAIN)
model . Add ( skeleton_in_progress == 191 ) \
. OnlyEnforceIf ( declarations [ Declaration . FISH ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' coelacanth antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 1 , cp_model . INT_MAX ] ) ) )
# Spider (UNCERTAIN)
model . Add ( skeleton_in_progress == 200 ) \
. OnlyEnforceIf ( declarations [ Declaration . SPIDER ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' spider antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ cp_model . INT_MIN , 1 ] ) ) )
# Primordial Orb-Weaver (UNCERTAIN)
model . Add ( skeleton_in_progress == 201 ) \
. OnlyEnforceIf ( declarations [ Declaration . SPIDER ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' primordial orb-weaver antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 2 , 7 ] ) ) )
# Trigonotarbid
model . Add ( skeleton_in_progress == 203 ) \
. OnlyEnforceIf ( declarations [ Declaration . SPIDER ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' trigonotarbid antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 8 , cp_model . INT_MAX ] ) ) )
# Beetle (UNCERTAIN)
model . Add ( skeleton_in_progress == 210 ) \
. OnlyEnforceIf ( declarations [ Declaration . INSECT ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' beetle antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ cp_model . INT_MIN , 1 ] ) ) )
# Primordial Beetle (UNCERTAIN)
model . Add ( skeleton_in_progress == 211 ) \
. OnlyEnforceIf ( declarations [ Declaration . INSECT ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' primordial beetle antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 2 , 6 ] ) ) )
# Rhyniognatha
model . Add ( skeleton_in_progress == 212 ) \
. OnlyEnforceIf ( declarations [ Declaration . INSECT ] ) \
. OnlyEnforceIf ( model . NewIntermediateBoolVar ( ' rhyniognatha antiquity ' , antiquity , cp_model . Domain . FromFlatIntervals ( [ 7 , cp_model . INT_MAX ] ) ) )
# Curator
model . Add ( skeleton_in_progress == 300 ) \
. OnlyEnforceIf ( declarations [ Declaration . CURATOR ] )
# Humanoid requirements
model . Add ( skulls == 1 ) . OnlyEnforceIf ( declarations [ Declaration . HUMANOID ] )
model . Add ( legs == 2 ) . OnlyEnforceIf ( declarations [ Declaration . HUMANOID ] )
model . Add ( arms == 2 ) . OnlyEnforceIf ( declarations [ Declaration . HUMANOID ] )
model . Add ( torso_style > = 10 ) . OnlyEnforceIf ( declarations [ Declaration . HUMANOID ] )
model . Add ( torso_style < = 20 ) . OnlyEnforceIf ( declarations [ Declaration . HUMANOID ] )
for prohibited_quality in [ tails , fins , wings ] :
model . Add ( prohibited_quality == 0 ) . OnlyEnforceIf ( declarations [ Declaration . HUMANOID ] )
# Ape requirements
model . Add ( skulls == 1 ) . OnlyEnforceIf ( declarations [ Declaration . APE ] )
model . Add ( arms == 4 ) . OnlyEnforceIf ( declarations [ Declaration . APE ] )
model . Add ( torso_style > = 10 ) . OnlyEnforceIf ( declarations [ Declaration . APE ] )
model . Add ( torso_style < = 20 ) . OnlyEnforceIf ( declarations [ Declaration . APE ] )
for prohibited_quality in [ legs , tails , fins , wings ] :
model . Add ( prohibited_quality == 0 ) . OnlyEnforceIf ( declarations [ Declaration . APE ] )
# Monkey requirements
model . Add ( skulls == 1 ) . OnlyEnforceIf ( declarations [ Declaration . MONKEY ] )
model . Add ( arms == 4 ) . OnlyEnforceIf ( declarations [ Declaration . MONKEY ] )
model . Add ( tails == 1 ) . OnlyEnforceIf ( declarations [ Declaration . MONKEY ] )
model . Add ( torso_style > = 10 ) . OnlyEnforceIf ( declarations [ Declaration . MONKEY ] )
model . Add ( torso_style < = 20 ) . OnlyEnforceIf ( declarations [ Declaration . MONKEY ] )
for prohibited_quality in [ legs , fins , wings ] :
model . Add ( prohibited_quality == 0 ) . OnlyEnforceIf ( declarations [ Declaration . MONKEY ] )
# Bird requirements
model . Add ( legs == 2 ) . OnlyEnforceIf ( declarations [ Declaration . BIRD ] )
model . Add ( wings == 2 ) . OnlyEnforceIf ( declarations [ Declaration . BIRD ] )
model . Add ( torso_style > = 20 ) . OnlyEnforceIf ( declarations [ Declaration . BIRD ] )
for prohibited_quality in [ arms , fins ] :
model . Add ( prohibited_quality == 0 ) . OnlyEnforceIf ( declarations [ Declaration . BIRD ] )
model . Add ( tails < 2 ) . OnlyEnforceIf ( declarations [ Declaration . BIRD ] )
# Curator requirements
model . Add ( skulls == 1 ) . OnlyEnforceIf ( declarations [ Declaration . CURATOR ] )
model . Add ( arms == 2 ) . OnlyEnforceIf ( declarations [ Declaration . CURATOR ] )
model . Add ( legs == 2 ) . OnlyEnforceIf ( declarations [ Declaration . CURATOR ] )
model . Add ( wings == 2 ) . OnlyEnforceIf ( declarations [ Declaration . CURATOR ] )
for prohibited_quality in [ fins , tails ] :
model . Add ( prohibited_quality == 0 ) . OnlyEnforceIf ( declarations [ Declaration . CURATOR ] )
# Reptile requirements
model . Add ( torso_style > = 20 ) . OnlyEnforceIf ( declarations [ Declaration . REPTILE ] )
model . Add ( tails == 1 ) . OnlyEnforceIf ( declarations [ Declaration . REPTILE ] )
model . Add ( skulls == 1 ) . OnlyEnforceIf ( declarations [ Declaration . REPTILE ] )
for prohibited_quality in [ fins , wings , arms ] :
model . Add ( prohibited_quality == 0 ) . OnlyEnforceIf ( declarations [ Declaration . REPTILE ] )
model . Add ( legs < 5 ) . OnlyEnforceIf ( declarations [ Declaration . REPTILE ] )
# Amphibian requirements
model . Add ( torso_style > = 20 ) . OnlyEnforceIf ( declarations [ Declaration . AMPHIBIAN ] )
model . Add ( legs == 4 ) . OnlyEnforceIf ( declarations [ Declaration . AMPHIBIAN ] )
model . Add ( skulls == 1 ) . OnlyEnforceIf ( declarations [ Declaration . AMPHIBIAN ] )
for prohibited_quality in [ tails , fins , wings , arms ] :
model . Add ( prohibited_quality == 0 ) . OnlyEnforceIf ( declarations [ Declaration . AMPHIBIAN ] )
# Fish requirements
model . Add ( skulls == 1 ) . OnlyEnforceIf ( declarations [ Declaration . FISH ] )
model . Add ( fins > = 2 ) . OnlyEnforceIf ( declarations [ Declaration . FISH ] )
model . Add ( tails < = 1 ) . OnlyEnforceIf ( declarations [ Declaration . FISH ] )
model . Add ( torso_style > = 20 ) . OnlyEnforceIf ( declarations [ Declaration . FISH ] )
for prohibited_quality in [ arms , legs , wings ] :
model . Add ( prohibited_quality == 0 ) . OnlyEnforceIf ( declarations [ Declaration . FISH ] )
# Insect requirements
model . Add ( skulls == 1 ) . OnlyEnforceIf ( declarations [ Declaration . INSECT ] )
model . Add ( legs == 6 ) . OnlyEnforceIf ( declarations [ Declaration . INSECT ] )
model . Add ( torso_style > = 20 ) . OnlyEnforceIf ( declarations [ Declaration . INSECT ] )
for prohibited_quality in [ arms , fins , tails ] :
model . Add ( prohibited_quality == 0 ) . OnlyEnforceIf ( declarations [ Declaration . INSECT ] )
model . Add ( wings < 5 ) . OnlyEnforceIf ( declarations [ Declaration . INSECT ] )
# Spider requirements
model . Add ( legs == 8 ) . OnlyEnforceIf ( declarations [ Declaration . SPIDER ] )
model . Add ( tails < = 1 ) . OnlyEnforceIf ( declarations [ Declaration . SPIDER ] )
model . Add ( torso_style > = 20 ) . OnlyEnforceIf ( declarations [ Declaration . SPIDER ] )
for prohibited_quality in [ skulls , arms , wings , fins ] :
model . Add ( prohibited_quality == 0 ) . OnlyEnforceIf ( declarations [ Declaration . SPIDER ] )
# Skeleton must be finished
for needed_quality in [ lambda action : action . skulls_needed , lambda action : action . limbs_needed , lambda action : action . tails_needed ] :
model . Add ( cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ needed_quality ( action ) for action in actions . keys ( ) ] ) == 0 )
if data [ ' buyer ' ] == Buyer . A_PALAEONTOLOGIST_WITH_HOARDING_PROPENSITIES :
model . Add ( skeleton_in_progress > = 100 )
# Revenue
model . Add ( primary_revenue == value + 5 )
model . Add ( secondary_revenue == 500 )
# Difficulty Level
model . Add ( difficulty_level == 40 * implausibility )
# Exhaustion
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . A_NAIVE_COLLECTOR :
model . Add ( skeleton_in_progress > = 100 )
value_remainder = model . NewIntVar ( 0 , 249 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 250 )
# Revenue
model . Add ( primary_revenue == value - value_remainder )
model . Add ( secondary_revenue == 0 )
# Difficulty Level
model . Add ( difficulty_level == 25 * implausibility )
# Exhaustion
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . A_FAMILIAR_BOHEMIAN_SCULPTRESS :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( antiquity < = 0 )
value_remainder = model . NewIntVar ( 0 , 249 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 250 )
# Revenue
model . Add ( primary_revenue == value - value_remainder + 1000 )
model . Add ( secondary_revenue == 250 * counter_church )
# Difficulty Level
model . Add ( difficulty_level == 50 * implausibility )
# Exhaustion
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . A_PEDAGOGICALLY_INCLINED_GRANDMOTHER :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( menace < = 0 )
value_remainder = model . NewIntVar ( 0 , 49 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 50 )
# Revenue
model . Add ( primary_revenue == value - value_remainder + 1000 )
model . Add ( secondary_revenue == 0 )
# Difficulty Level
model . Add ( difficulty_level == 50 * implausibility )
# Exhaustion
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . A_THEOLOGIAN_OF_THE_OLD_SCHOOL :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( amalgamy < = 0 )
value_remainder = model . NewIntVar ( 0 , 249 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 250 )
# Revenue
model . Add ( primary_revenue == value - value_remainder + 1000 )
model . Add ( secondary_revenue == 0 )
# Difficulty Level
model . Add ( difficulty_level == 50 * implausibility )
# Exhaustion
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . AN_ENTHUSIAST_OF_THE_ANCIENT_WORLD :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( antiquity > 0 )
value_remainder = model . NewIntVar ( 0 , 49 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 50 )
# Revenue
model . Add ( primary_revenue == value - value_remainder )
model . Add ( secondary_revenue == 250 * antiquity + ( 250 if data [ ' bone_market_fluctuations ' ] == Fluctuation . ANTIQUITY else 0 ) )
# Difficulty Level
model . Add ( difficulty_level == 45 * implausibility )
# Exhaustion
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . MRS_PLENTY :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( menace > 0 )
value_remainder = model . NewIntVar ( 0 , 49 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 50 )
# Revenue
model . Add ( primary_revenue == value - value_remainder )
model . Add ( secondary_revenue == 250 * menace )
# Difficulty Level
model . Add ( difficulty_level == 45 * implausibility )
# Exhaustion
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . A_TENTACLED_SERVANT :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( amalgamy > 0 )
value_remainder = model . NewIntVar ( 0 , 49 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 50 )
# Revenue
model . Add ( primary_revenue == value - value_remainder + 250 )
model . Add ( secondary_revenue == 250 * amalgamy + ( 250 if data [ ' bone_market_fluctuations ' ] == Fluctuation . AMALGAMY else 0 ) )
# Difficulty Level
model . Add ( difficulty_level == 45 * implausibility )
# Exhaustion
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . AN_INVESTMENT_MINDED_AMBASSADOR :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( antiquity > 0 )
antiquity_squared = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' antiquity squared ' )
model . AddMultiplicationEquality ( antiquity_squared , [ antiquity , antiquity ] )
tailfeathers = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' tailfeathers ' )
if data [ ' bone_market_fluctuations ' ] == Fluctuation . ANTIQUITY :
model . AddApproximateExponentiationEquality ( tailfeathers , antiquity , 2.2 , MAXIMUM_ATTRIBUTE )
else :
model . Add ( tailfeathers == antiquity_squared )
value_remainder = model . NewIntVar ( 0 , 49 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 50 )
extra_value = model . NewIntermediateBoolVar ( ' extra value ' , value_remainder , cp_model . Domain . FromFlatIntervals ( [ 0 , cp_model . INT_MAX ] ) )
# Revenue
model . Add ( primary_revenue == value + 50 * extra_value + 250 )
model . Add ( secondary_revenue == 250 * tailfeathers )
# Difficulty Level
model . Add ( difficulty_level == 75 * implausibility )
# Exhaustion
derived_exhaustion = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' derived exhaustion ' )
model . AddDivisionEquality ( derived_exhaustion , antiquity_squared , 20 )
model . Add ( exhaustion == derived_exhaustion + cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . A_TELLER_OF_TERRORS :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( menace > 0 )
menace_squared = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' menace squared ' )
model . AddMultiplicationEquality ( menace_squared , [ menace , menace ] )
value_remainder = model . NewIntVar ( 0 , 9 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 10 )
# Revenue
model . Add ( primary_revenue == value - value_remainder + 50 )
model . Add ( secondary_revenue == 50 * menace_squared )
# Difficulty Level
model . Add ( difficulty_level == 75 * implausibility )
# Exhaustion
derived_exhaustion = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' derived exhaustion ' )
model . AddDivisionEquality ( derived_exhaustion , menace_squared , 100 )
model . Add ( exhaustion == derived_exhaustion + cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . A_TENTACLED_ENTREPRENEUR :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( amalgamy > 0 )
amalgamy_squared = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' amalgamy squared ' )
model . AddMultiplicationEquality ( amalgamy_squared , [ amalgamy , amalgamy ] )
final_breaths = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' final breaths ' )
if data [ ' bone_market_fluctuations ' ] == Fluctuation . AMALGAMY :
model . AddApproximateExponentiationEquality ( final_breaths , amalgamy , 2.2 , MAXIMUM_ATTRIBUTE )
else :
model . Add ( final_breaths == amalgamy_squared )
value_remainder = model . NewIntVar ( 0 , 49 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 50 )
# Revenue
model . Add ( primary_revenue == value - value_remainder + 250 )
model . Add ( secondary_revenue == 50 * final_breaths )
# Difficulty Level
model . Add ( difficulty_level == 75 * implausibility )
# Exhaustion
derived_exhaustion = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' derived exhaustion ' )
model . AddDivisionEquality ( derived_exhaustion , amalgamy_squared , 100 )
model . Add ( exhaustion == derived_exhaustion + cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . AN_AUTHOR_OF_GOTHIC_TALES :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( antiquity > 0 )
model . Add ( menace > 0 )
antiquity_times_menace = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' antiquity times menace ' )
model . AddMultiplicationEquality ( antiquity_times_menace , [ antiquity , menace ] )
value_remainder = model . NewIntVar ( 0 , 49 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 50 )
# Revenue
model . Add ( primary_revenue == value - value_remainder + 250 )
model . Add ( secondary_revenue == 250 * antiquity_times_menace + 250 * ( menace if data [ ' bone_market_fluctuations ' ] == Fluctuation . ANTIQUITY else 0 ) )
# Difficulty Level
model . Add ( difficulty_level == 75 * implausibility )
# Exhaustion
derived_exhaustion = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' derived exhaustion ' )
model . AddDivisionEquality ( derived_exhaustion , antiquity_times_menace , 20 )
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) + derived_exhaustion )
elif data [ ' buyer ' ] == Buyer . A_ZAILOR_WITH_PARTICULAR_INTERESTS :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( antiquity > 0 )
model . Add ( amalgamy > 0 )
amalgamy_times_antiquity = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' amalgamy times antiquity ' )
model . AddMultiplicationEquality ( amalgamy_times_antiquity , [ amalgamy , antiquity ] )
value_remainder = model . NewIntVar ( 0 , 9 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 10 )
# Revenue
model . Add ( primary_revenue == value - value_remainder + 250 )
model . Add ( secondary_revenue == 250 * amalgamy_times_antiquity + 250 * ( amalgamy if data [ ' bone_market_fluctuations ' ] == Fluctuation . ANTIQUITY else antiquity if data [ ' bone_market_fluctuations ' ] == Fluctuation . AMALGAMY else 0 ) )
# Difficulty Level
model . Add ( difficulty_level == 75 * implausibility )
# Exhaustion
derived_exhaustion = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' derived exhaustion ' )
model . AddDivisionEquality ( derived_exhaustion , amalgamy_times_antiquity , 20 )
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) + derived_exhaustion )
elif data [ ' buyer ' ] == Buyer . A_RUBBERY_COLLECTOR :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( amalgamy > 0 )
model . Add ( menace > 0 )
amalgamy_times_menace = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' amalgamy times menace ' )
model . AddMultiplicationEquality ( amalgamy_times_menace , [ amalgamy , menace ] )
value_remainder = model . NewIntVar ( 0 , 49 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 50 )
# Revenue
model . Add ( primary_revenue == value - value_remainder + 250 )
model . Add ( secondary_revenue == 250 * amalgamy_times_menace + 250 * ( menace if data [ ' bone_market_fluctuations ' ] == Fluctuation . AMALGAMY else 0 ) )
# Difficulty Level
model . Add ( difficulty_level == 75 * implausibility )
# Exhaustion
derived_exhaustion = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' derived exhaustion ' )
model . AddDivisionEquality ( derived_exhaustion , amalgamy_times_menace , 20 )
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) + derived_exhaustion )
elif data [ ' buyer ' ] == Buyer . A_CONSTABLE :
model . AddLinearExpressionInDomain ( skeleton_in_progress , cp_model . Domain . FromFlatIntervals ( [ 110 , 119 ] ) )
value_remainder = model . NewIntVar ( 0 , 49 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 50 )
# Revenue
model . Add ( primary_revenue == value - value_remainder + 1000 )
model . Add ( secondary_revenue == 0 )
# Difficulty Level
model . Add ( difficulty_level == 50 * implausibility )
# Exhaustion
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . AN_ENTHUSIAST_IN_SKULLS :
model . Add ( skeleton_in_progress > = 100 )
model . Add ( skulls > = 2 )
extra_skulls = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' extra skulls ' )
model . Add ( extra_skulls == skulls - 1 )
vital_intelligence = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' vital intelligence ' )
model . AddApproximateExponentiationEquality ( vital_intelligence , extra_skulls , 1.8 , MAXIMUM_ATTRIBUTE )
# Revenue
model . Add ( primary_revenue == value )
model . Add ( secondary_revenue == 1250 * vital_intelligence )
# Difficulty Level
model . Add ( difficulty_level == 60 * implausibility )
# Exhaustion
derived_exhaustion = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' derived exhaustion ' )
model . AddDivisionEquality ( derived_exhaustion , vital_intelligence , 4 )
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) + derived_exhaustion )
elif data [ ' buyer ' ] == Buyer . A_DREARY_MIDNIGHTER :
model . AddLinearExpressionInDomain ( skeleton_in_progress , cp_model . Domain . FromFlatIntervals ( [ 110 , 299 ] ) )
model . Add ( amalgamy < = 0 )
model . Add ( counter_church < = 0 )
value_remainder = model . NewIntVar ( 0 , 2 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 3 )
# Revenue
model . Add ( primary_revenue == value - value_remainder + 300 )
model . Add ( secondary_revenue == 250 )
# Difficulty Level
model . Add ( difficulty_level == 100 * implausibility )
# Exhaustion
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
elif data [ ' buyer ' ] == Buyer . THE_DUMBWAITER_OF_BALMORAL :
model . AddLinearExpressionInDomain ( skeleton_in_progress , cp_model . Domain . FromFlatIntervals ( [ 180 , 189 ] ) )
model . Add ( value > = 250 )
value_remainder = model . NewIntVar ( 0 , 249 , ' value remainder ' )
model . AddModuloEquality ( value_remainder , value , 250 )
# Revenue
model . Add ( primary_revenue == value - value_remainder )
model . Add ( secondary_revenue == 0 )
# Difficulty Level
model . Add ( difficulty_level == 200 )
# Exhaustion
model . Add ( exhaustion == cp_model . LinearExpr . ScalProd ( actions . values ( ) , [ action . exhaustion for action in actions . keys ( ) ] ) )
# Maximize profit margin
net_profit = model . NewIntVar ( cp_model . INT32_MIN , cp_model . INT32_MAX , ' net profit ' )
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 ' )
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 ' )
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 ' )
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 ' )
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 )
model . Add ( profit_margin == absolute_profit_margin * - 1 ) . OnlyEnforceIf ( positive_net_profit . Not ( ) )
del multiplied_net_profit , absolute_multiplied_net_profit , absolute_profit_margin , positive_net_profit
model . Maximize ( profit_margin )
solver = cp_model . CpSolver ( )
solver . parameters . num_search_workers = os . cpu_count ( )
solver . parameters . log_search_progress = True
status = solver . StatusName ( solver . Solve ( model ) )
if status == " INFEASIBLE " :
raise RuntimeError ( " There is no satisfactory skeleton. " )
elif status == " FEASIBLE " :
print ( " WARNING: skeleton may be suboptimal. " )
elif status != " OPTIMAL " :
raise RuntimeError ( " Unknown status returned: {} . " . format ( status ) )
for action in actions . keys ( ) :
for _ in range ( int ( solver . Value ( actions [ action ] ) ) ) :
print ( action )
print ( " \n Profit: £ {:,.2f} " . format ( solver . Value ( net_profit ) / 100 ) )
print ( " Profit Margin: {:+,.2%} " . format ( solver . Value ( profit_margin ) / PROFIT_MARGIN_MULTIPLIER ) )
print ( " \n Total Revenue: £ {:,.2f} " . format ( solver . Value ( total_revenue ) / 100 ) )
print ( " Primary Revenue: £ {:,.2f} " . format ( solver . Value ( primary_revenue ) / 100 ) )
print ( " Secondary Revenue: £ {:,.2f} " . format ( solver . Value ( secondary_revenue ) / 100 ) )
print ( " \n Cost: £ {:,.2f} " . format ( solver . Value ( cost ) / 100 ) )
print ( " \n Value: £ {:,.2f} " . format ( solver . Value ( value ) / 100 ) )
print ( " Amalgamy: {:n} " . format ( solver . Value ( amalgamy ) ) )
print ( " Antiquity: {:n} " . format ( solver . Value ( antiquity ) ) )
print ( " Menace: {:n} " . format ( solver . Value ( menace ) ) )
print ( " Counter-Church: {:n} " . format ( solver . Value ( counter_church ) ) )
print ( " Implausibility: {:n} " . format ( solver . Value ( implausibility ) ) )
print ( " \n Exhaustion: {:n} " . format ( solver . Value ( exhaustion ) ) )
if __name__ == ' __main__ ' :
Solve ( )