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
# Bone Fragment
BONE_FRAGMENT = 1
# Cartographer's Hoard
CARTOGRAPHERS_HOARD = 31250
# Collection Note: There's a 'Package' in London
# Station VIII Lab
COLLECTION_NOTE = ACTION
# Volume of Collated Research
COLLATED_RESEARCH = 250
# Five-Pointed Ribcage
# Upwards
FIVE_POINTED_RIBCAGE = 9 * ACTION + CARTOGRAPHERS_HOARD
# Headless Skeleton
# These are accumulated while acquiring other qualities.
HEADLESS_SKELETON = 0
# Nevercold Brass Sliver
NEVERCOLD_BRASS = 1
# Knob of Scintillack
SCINTILLACK = 250
# Searing Enigma
SEARING_ENIGMA = 6250
# 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
# 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
# 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
# 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
# Nodule of Warm Amber
WARM_AMBER = 10
# 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-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 ) :
BRASS_SKULL = Action ( " Affix a Bright Brass Skull to your (Skeleton Type) " , cost = 6450 + Value . ACTION . value , value = 6500 , skulls_needed = - 1 , skulls = 1 , implausibility = 2 )
# No consistent source
# EYELESS_SKULL = Action("Affix an Eyeless Skull to your (Skeleton Type)", cost = cp_model.INT32_MAX, value = 3000, skulls_needed = -1, skulls = 1, menace = 2)
# Feast of the Exceptional Rose, 200 Inklings of Identity, action to send and receive it
# ENGRAVED_SKULL = Action("Affix a Custom-Engraved Skull to your (Skeleton Type)", cost = 2000 + Value.ACTION.value*2, value = 10000, skulls_needed = -1, skulls = 1, exhaustion = 2)
HORNED_SKULL = Action ( " Affix a Horned Skull to your (Skeleton Type) " , cost = 1050 + Value . ACTION . value * 2 , value = 1250 , skulls_needed = - 1 , skulls = 1 , antiquity = 1 , menace = 2 )
SABRE_TOOTHED_SKULL = Action ( " Affix a Sabre-toothed Skull to your (Skeleton Type) " , cost = 6150 + Value . ACTION . value * 2 , value = 6250 , skulls_needed = - 1 , skulls = 1 , antiquity = 1 , menace = 1 )
# Upwards
PENTAGRAMMIC_SKULL = Action ( " Affix a Pentagrammic Skull to your (Skeleton Type) " , cost = Value . ACTION . value * 10 , value = 1250 , skulls_needed = - 1 , skulls = 1 , amalgamy = 2 , menace = 1 )
PLATED_SKULL = Action ( " Affix a Plated Skull to your (Skeleton Type) " , cost = 2250 + Value . ACTION . value * 2 , value = 2500 , skulls_needed = - 1 , skulls = 1 , menace = 2 )
# Flute Street, including travel due to quality cap
RUBBERY_SKULL = Action ( " Affix a Rubbery Skull to your (Skeleton Type) " , cost = Value . ACTION . value * 26 , value = 600 , skulls_needed = - 1 , skulls = 1 , amalgamy = 1 )
# OWN_SKULL = Action("Duplicate your own skull and affix it here", cost = 1000 + Value.ACTION.value, value = -250, skulls_needed = -1, skulls = 1)
BAPTIST_SKULL = Action ( " Duplicate the skull of John the Baptist, if you can call that a skull " , cost = 1000 + Value . ACTION . value , value = 1500 , skulls_needed = - 1 , skulls = 1 , counter_church = 2 )
# Persephone, 6 actions (Favours: the Docks) for 2 Esteem of the Guild
CORAL_SKULL = Action ( " Affix a Skull in Coral to your (Skeleton Type) " , cost = Value . ACTION . value * 25 / 3 , value = 1750 , skulls_needed = - 1 , skulls = 1 , amalgamy = 2 )
VAKE_SKULL = Action ( " Duplicate the Vake ' s skull and use it to decorate your (Skeleton Type) " , cost = 6000 + Value . ACTION . value , value = 6500 , skulls_needed = - 1 , skulls = 1 , menace = 3 )
# VICTIM_SKULL = Action("Cap this with a victim’ s skull", cost = Value.ACTION.value, value = 250, skulls_needed = -1, skulls = 1)
# Balmoral Woods (also gives Thorned Ribcage)
DOUBLED_SKULL = Action ( " Affix a Doubled Skull to your (Skeleton Type) " , cost = 2000 + Value . ACTION . value * 14 , value = 6250 , skulls_needed = - 1 , skulls = 2 , amalgamy = 1 , antiquity = 2 )
STYGIAN_IVORY = Action ( " Use a Carved Ball of Stygian Ivory to cap off your (Skeleton Type) " , cost = 250 + Value . ACTION . value , value = 250 , skulls_needed = - 1 )
def __str__ ( self ) :
return str ( self . value )
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-09 15:29:56 +00:00
data [ ' actions ' ] = [ torso . value for torso in Torso ] + [ skull . value for skull in Skull ] + [
2021-06-08 22:28:15 +00:00
# 2 pincers at once
2021-06-08 23:55:56 +00:00
Action ( " Apply a Crustacean Pincer to your (Skeleton Type) " , cost = 25 + Value . ACTION . value * 1.5 , limbs_needed = - 1 , arms = 1 , menace = 1 ) ,
2021-06-08 22:28:15 +00:00
# Accumulated while trying to get other things
2021-06-08 23:55:56 +00:00
Action ( " Apply a Knotted Humerus to your (Skeleton Type) " , cost = Value . ACTION . value , value = 150 , limbs_needed = - 1 , arms = 1 , amalgamy = 1 ) ,
2021-06-08 22:28:15 +00:00
# Ealing Gardens, 5 actions (Favours: Bohemians) for 2
2021-06-08 23:55:56 +00:00
Action ( " Apply an Ivory Humerus to your (Skeleton Type) " , cost = Value . ACTION . value * 3.5 , value = 1500 , limbs_needed = - 1 , arms = 1 ) ,
2021-06-08 22:28:15 +00:00
# Accumulated while trying to get other things
2021-06-08 23:55:56 +00:00
Action ( " Join a Human Arm to your (Skeleton Type) " , cost = Value . ACTION . value , value = 250 , limbs_needed = - 1 , arms = 1 , menace = - 1 ) ,
2021-06-08 22:28:15 +00:00
# Anning and Daughters
2021-06-08 23:55:56 +00:00
Action ( " Apply a Fossilised Forelimb to your (Skeleton Type) " , cost = 2500 + Value . ACTION . value , value = 2750 , limbs_needed = - 1 , arms = 1 , antiquity = 2 ) ,
2021-06-08 22:28:15 +00:00
# 2 wings at once
2021-06-08 23:55:56 +00:00
Action ( " Add the Wing of a Young Terror Bird to your (Skeleton Type) " , cost = 175 + Value . ACTION . value * 1.5 , value = 250 , limbs_needed = - 1 , wings = 1 , antiquity = 1 , menace = 1 ) ,
2021-06-08 22:28:15 +00:00
# 2 wings at once
2021-06-08 23:55:56 +00:00
Action ( " Put an Albatross Wing on your (Skeleton Type) " , cost = 1125 + Value . ACTION . value * 1.5 , value = 1250 , limbs_needed = - 1 , wings = 1 , amalgamy = 1 ) ,
2021-06-08 22:28:15 +00:00
# 2 wings at once
2021-06-08 23:55:56 +00:00
Action ( " Add a Bat Wing to your (Skeleton Type) " , cost = 60 + Value . ACTION . value * 1.5 , value = 1 , limbs_needed = - 1 , wings = 1 , menace = - 1 ) ,
2021-06-08 22:28:15 +00:00
# Dumbwaiter of Balmoral, 25 at a time
2021-06-08 23:55:56 +00:00
Action ( " Apply the Femur of a Surface Deer to your (Skeleton Type) " , cost = Value . ACTION . value * 1.04 , value = 10 , limbs_needed = - 1 , legs = 1 , menace = - 1 ) ,
2021-06-08 22:28:15 +00:00
# Accumulated while trying to get other things
2021-06-08 23:55:56 +00:00
Action ( " Apply an Unidentified Thigh Bone to your (Skeleton Type) " , cost = Value . ACTION . value , value = 100 , limbs_needed = - 1 , legs = 1 ) ,
2021-06-08 22:28:15 +00:00
# Brawling, 12 at a time
2021-06-08 23:55:56 +00:00
Action ( " Apply a Jurassic Thigh Bone to your (Skeleton Type) " , cost = Value . ACTION . value * ( 11 / 6 ) , value = 300 , limbs_needed = - 1 , legs = 1 , antiquity = 1 ) ,
2021-06-08 22:28:15 +00:00
# Jericho Locks, 5 actions (Favours: the Church) for 2
# Counter-Church theology from this scales with torso style and is implemented separately
2021-06-08 23:55:56 +00:00
Action ( " Affix Saint Fiacre ' s Thigh Relic to your (Skeleton Type) " , cost = Value . ACTION . value * 3.5 , value = 1250 , limbs_needed = - 1 , legs = 1 ) ,
2021-06-08 22:28:15 +00:00
# Palaeontological Discoveries, Plain of Thirsty Grasses
2021-06-08 23:55:56 +00:00
Action ( " Affix the Helical Thighbone to your (Skeleton Type) " , cost = Value . ACTION . value + Value . SURVEY . value * ( 70 / 9 ) , value = 300 , limbs_needed = - 1 , legs = 1 , amalgamy = 2 ) ,
2021-06-08 22:28:15 +00:00
# Parabolan Orange-Apples, Hedonist, 3cp/action
2021-06-08 23:55:56 +00:00
Action ( " Apply an Ivory Femur to your (Skeleton Type) " , cost = 900 + Value . ACTION . value * 15.5 , value = 6500 , limbs_needed = - 1 , legs = 1 ) ,
2021-06-08 22:28:15 +00:00
# Hunt and dissect Pinewood Shark, 40 at a time
2021-06-08 23:55:56 +00:00
Action ( " Put Fins on your (Skeleton Type) " , cost = Value . ACTION . value * ( 51 / 40 ) , value = 50 , limbs_needed = - 1 , fins = 1 ) ,
2021-06-08 22:28:15 +00:00
# Combination of 10 Fins
2021-06-08 23:55:56 +00:00
Action ( " Attach the Amber-Crusted Fin to your (Skeleton Type) " , cost = Value . ACTION . value * ( 15 / 4 ) , value = 1500 , limbs_needed = - 1 , fins = 1 , amalgamy = 1 , menace = 1 ) ,
2021-06-08 22:28:15 +00:00
# Helicon House, 3 at a time
2021-06-08 23:55:56 +00:00
Action ( " Put a Withered Tentacle on your (Skeleton Type) " , cost = 50 / 3 + Value . ACTION . value * 4 / 3 , value = 250 , limbs_needed = - 1 , tentacles = 1 , antiquity = - 1 ) ,
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
# Cost from this scales with limbs and is partially implemented separately
2021-06-08 23:55:56 +00:00
Action ( " Add four more joints to your skeleton " , cost = 1250 + Value . ACTION . value , limbs_needed = 4 , amalgamy = 2 ) ,
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 ' )
model . AddGeneralMultiplicationEquality ( add_joints_amber_cost_multiple_first_term , 250 , base_joints , base_joints , add_joints )
add_joints_amber_cost_multiple_second_term = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' add joints amber cost multiple second term ' )
model . AddGeneralMultiplicationEquality ( add_joints_amber_cost_multiple_second_term , 1000 , base_joints , add_joints , add_joints )
add_joints_amber_cost_multiple_third_term = model . NewIntVar ( 0 , cp_model . INT32_MAX , ' add joints amber cost multiple third term ' )
model . AddGeneralMultiplicationEquality ( add_joints_amber_cost_multiple_third_term , 1000 , base_joints , add_joints )
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 ' )
model . AddGeneralMultiplicationEquality ( add_joints_amber_cost_multiple_fourth_term_numerator_first_term , 4000 , 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 + 2000 * add_joints )
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 ' )
model . AddGeneralMultiplicationEquality ( add_joints_amber_cost_multiple_fifth_term , 2000 , add_joints , add_joints )
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
model . AddMultiplicationEquality ( add_joints_amber_cost , [ add_joints , add_joints_amber_cost_multiple ] )
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 ( )