2021-06-18 20:28:56 +00:00
""" Use constraint programming to devise the optimal skeleton at the Bone Market in Fallen London. """
2021-08-13 13:39:04 +00:00
__all__ = [ ' Adjustment ' , ' Appendage ' , ' Buyer ' , ' Declaration ' , ' DiplomatFascination ' , ' Embellishment ' , ' Fluctuation ' , ' OccasionalBuyer ' , ' Skull ' , ' Solve ' , ' Torso ' ]
2021-06-18 20:28:56 +00:00
__author__ = " Jeremy Saklad "
2021-10-15 22:46:13 +00:00
from functools import partialmethod
2022-04-19 15:02:03 +00:00
from os import cpu_count
2021-06-08 22:28:15 +00:00
from ortools . sat . python import cp_model
2021-08-03 19:48:42 +00:00
from . data . adjustments import Adjustment
from . data . appendages import Appendage
from . data . buyers import Buyer
from . data . costs import Cost
from . data . declarations import Declaration
from . data . diplomat_fascinations import DiplomatFascination
from . data . embellishments import Embellishment
from . data . fluctuations import Fluctuation
from . data . occasional_buyers import OccasionalBuyer
from . data . skulls import Skull
from . data . torsos import Torso
2021-09-22 22:06:44 +00:00
from . objects . bone_market_model import BoneMarketModel
2021-08-03 17:02:06 +00:00
2021-06-08 22:28:15 +00:00
# This multiplier is applied to the profit margin to avoid losing precision due to rounding.
2021-10-16 02:32:55 +00:00
PROFIT_MARGIN_MULTIPLIER = 10000
2021-06-08 22:28:15 +00:00
# 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
2022-04-19 15:02:03 +00:00
def Solve ( shadowy_level , bone_market_fluctuations = None , zoological_mania = None , occasional_buyer = None , diplomat_fascination = None , desired_buyers = [ ] , maximum_cost = cp_model . INT32_MAX , maximum_exhaustion = cp_model . INT32_MAX , time_limit = float ( ' inf ' ) , workers = cpu_count ( ) , blacklist = [ ] , stdscr = None ) :
2021-09-22 22:06:44 +00:00
model = BoneMarketModel ( )
2021-06-08 22:28:15 +00:00
actions = { }
2021-06-13 17:37:15 +00:00
# Torso
for torso in Torso :
actions [ torso ] = model . NewBoolVar ( torso . value . name )
# Skull
for skull in Skull :
2021-09-22 22:06:44 +00:00
actions [ skull ] = model . NewIntVar ( skull . value . name , lb = 0 )
2021-06-13 17:37:15 +00:00
# Appendage
for appendage in Appendage :
if appendage == Appendage . SKIP_TAILS :
actions [ appendage ] = model . NewBoolVar ( appendage . value . name )
2021-06-08 22:28:15 +00:00
else :
2021-09-22 22:06:44 +00:00
actions [ appendage ] = model . NewIntVar ( appendage . value . name , lb = 0 )
2021-06-08 22:28:15 +00:00
2021-06-13 17:37:15 +00:00
# Adjustment
for adjustment in Adjustment :
2021-09-22 22:06:44 +00:00
actions [ adjustment ] = model . NewIntVar ( adjustment . value . name , lb = 0 )
2021-06-08 22:28:15 +00:00
2021-06-13 17:37:15 +00:00
# Declaration
2021-06-08 22:28:15 +00:00
for declaration in Declaration :
2021-06-13 17:37:15 +00:00
actions [ declaration ] = model . NewBoolVar ( declaration . value . name )
2021-06-13 19:27:53 +00:00
# Embellishment
for embellishment in Embellishment :
2021-09-22 22:06:44 +00:00
actions [ embellishment ] = model . NewIntVar ( embellishment . value . name , lb = 0 )
2021-06-13 19:27:53 +00:00
2021-06-16 20:30:48 +00:00
2021-06-13 22:37:39 +00:00
# Buyer
for buyer in Buyer :
actions [ buyer ] = model . NewBoolVar ( buyer . value . name )
2021-06-16 20:30:48 +00:00
# Mark unavailable buyers
model . AddAssumptions ( [
actions [ buyer ] . Not ( )
for unavailable_buyer in OccasionalBuyer if unavailable_buyer != occasional_buyer
2021-06-16 22:28:16 +00:00
for buyer in unavailable_buyer . value if buyer not in desired_buyers
2021-06-16 20:30:48 +00:00
] )
2021-06-25 23:37:41 +00:00
model . AddAssumptions ( [
actions [ outmoded_fascination . value ] . Not ( )
for outmoded_fascination in DiplomatFascination if outmoded_fascination != diplomat_fascination and outmoded_fascination . value not in desired_buyers
] )
2021-06-16 20:30:48 +00:00
2021-06-16 22:28:16 +00:00
# Restrict to desired buyers
if desired_buyers :
model . Add ( cp_model . LinearExpr . Sum ( [ actions [ desired_buyer ] for desired_buyer in desired_buyers ] ) == 1 )
2021-08-13 13:39:04 +00:00
# Blacklist
model . Add ( cp_model . LinearExpr . Sum ( [ actions [ forbidden ] for forbidden in blacklist ] ) == 0 )
2021-06-16 20:30:48 +00:00
2021-06-13 17:37:15 +00:00
# One torso
model . Add ( cp_model . LinearExpr . Sum ( [ value for ( key , value ) in actions . items ( ) if isinstance ( key , Torso ) ] ) == 1 )
# One declaration
model . Add ( cp_model . LinearExpr . Sum ( [ value for ( key , value ) in actions . items ( ) if isinstance ( key , Declaration ) ] ) == 1 )
2021-06-08 22:28:15 +00:00
2021-06-13 22:37:39 +00:00
# One buyer
model . Add ( cp_model . LinearExpr . Sum ( [ value for ( key , value ) in actions . items ( ) if isinstance ( key , Buyer ) ] ) == 1 )
2021-06-08 22:28:15 +00:00
# Value calculation
2021-09-22 22:06:44 +00:00
value = model . NewIntVar ( ' value ' , lb = 0 )
2021-09-01 17:03:53 +00:00
2021-09-22 22:06:44 +00:00
base_value = model . NewIntVar ( ' base value ' , lb = 0 )
2022-04-11 18:38:25 +00:00
model . Add ( base_value == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . value for action in actions . keys ( ) ] ) )
2021-09-01 17:03:53 +00:00
# Calculate value from Vake skulls
# This is a partial sum formula.
2021-09-22 22:06:44 +00:00
vake_skull_value = model . NewIntVar ( ' vake skull value ' )
2021-09-01 17:03:53 +00:00
vake_skulls = actions [ Skull . VAKE_SKULL ]
2021-09-22 22:06:44 +00:00
vake_skulls_squared = model . NewIntVar ( ' vake skulls squared ' , lb = 0 )
model . AddMultiplicationEquality ( vake_skulls_squared , ( vake_skulls , vake_skulls ) )
2021-09-01 17:03:53 +00:00
model . Add ( vake_skull_value == - 250 * vake_skulls_squared + 6750 * vake_skulls )
del vake_skulls , vake_skulls_squared
2021-09-10 19:58:56 +00:00
model . Add ( value == base_value + vake_skull_value )
2021-09-01 17:03:53 +00:00
2021-09-10 19:58:56 +00:00
del base_value , vake_skull_value
2021-06-24 13:45:59 +00:00
2021-09-01 17:03:53 +00:00
# Zoological Mania
2021-09-22 22:06:44 +00:00
zoological_mania_bonus = model . NewIntVar ( ' zoological mania bonus ' , lb = 0 )
2021-07-11 20:45:04 +00:00
if zoological_mania :
2021-09-10 19:58:56 +00:00
multiplier = 15 if zoological_mania in [ Declaration . FISH , Declaration . INSECT , Declaration . SPIDER ] else 10
2021-06-08 22:28:15 +00:00
2021-09-22 22:06:44 +00:00
potential_zoological_mania_bonus = model . NewIntVar ( ' potential zoological mania bonus ' , lb = 0 )
multiplied_value = model . NewIntVar ( ' multiplied value ' , lb = 0 )
2021-09-10 19:58:56 +00:00
model . Add ( multiplied_value == multiplier * value )
model . AddDivisionEquality ( potential_zoological_mania_bonus , multiplied_value , 100 )
model . Add ( zoological_mania_bonus == potential_zoological_mania_bonus ) . OnlyEnforceIf ( actions [ zoological_mania ] )
model . Add ( zoological_mania_bonus == 0 ) . OnlyEnforceIf ( actions [ zoological_mania ] . Not ( ) )
2021-07-11 20:45:04 +00:00
2021-09-10 19:58:56 +00:00
del multiplier , potential_zoological_mania_bonus , multiplied_value
2021-07-11 20:45:04 +00:00
else :
2021-09-10 19:58:56 +00:00
model . Add ( zoological_mania_bonus == 0 )
2021-06-08 22:28:15 +00:00
# Torso Style calculation
2021-06-18 18:49:44 +00:00
torso_style = model . NewIntVarFromDomain ( cp_model . Domain . FromValues ( [ torso . value . torso_style for torso in Torso ] ) , ' torso style ' )
2021-06-13 17:37:15 +00:00
for torso , torso_variable in { key : value for ( key , value ) in actions . items ( ) if isinstance ( key , Torso ) } . items ( ) :
model . Add ( torso_style == torso . value . torso_style ) . OnlyEnforceIf ( torso_variable )
2021-06-08 22:28:15 +00:00
# Skulls calculation
2021-09-22 22:06:44 +00:00
skulls = model . NewIntVar ( ' skulls ' , lb = 0 )
2022-04-11 18:38:25 +00:00
model . Add ( skulls == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . skulls for action in actions . keys ( ) ] ) )
2021-06-08 22:28:15 +00:00
# Arms calculation
2021-09-22 22:06:44 +00:00
arms = model . NewIntVar ( ' arms ' , lb = 0 )
2022-04-11 18:38:25 +00:00
model . Add ( arms == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . arms for action in actions . keys ( ) ] ) )
2021-06-08 22:28:15 +00:00
# Legs calculation
2021-09-22 22:06:44 +00:00
legs = model . NewIntVar ( ' legs ' , lb = 0 )
2022-04-11 18:38:25 +00:00
model . Add ( legs == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . legs for action in actions . keys ( ) ] ) )
2021-06-08 22:28:15 +00:00
# Tails calculation
2021-09-22 22:06:44 +00:00
tails = model . NewIntVar ( ' tails ' , lb = 0 )
2022-04-11 18:38:25 +00:00
model . Add ( tails == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . tails for action in actions . keys ( ) ] ) )
2021-06-08 22:28:15 +00:00
# Wings calculation
2021-09-22 22:06:44 +00:00
wings = model . NewIntVar ( ' wings ' , lb = 0 )
2022-04-11 18:38:25 +00:00
model . Add ( wings == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . wings for action in actions . keys ( ) ] ) )
2021-06-08 22:28:15 +00:00
# Fins calculation
2021-09-22 22:06:44 +00:00
fins = model . NewIntVar ( ' fins ' , lb = 0 )
2022-04-11 18:38:25 +00:00
model . Add ( fins == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . fins for action in actions . keys ( ) ] ) )
2021-06-08 22:28:15 +00:00
2022-05-26 12:45:36 +00:00
# Segments calculation
segments = model . NewIntVar ( ' segments ' , lb = 0 )
model . Add ( segments == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . segments for action in actions . keys ( ) ] ) )
2021-06-08 22:28:15 +00:00
# Tentacles calculation
2021-09-22 22:06:44 +00:00
tentacles = model . NewIntVar ( ' tentacles ' , lb = 0 )
2022-04-11 18:38:25 +00:00
model . Add ( tentacles == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . tentacles for action in actions . keys ( ) ] ) )
2021-06-08 22:28:15 +00:00
# Amalgamy calculation
2021-10-15 22:12:20 +00:00
amalgamy = model . NewIntVar ( ' amalgamy ' , lb = 0 )
unbound_amalgamy = model . NewIntVar ( ' unbound amalgamy ' )
2022-04-11 18:38:25 +00:00
model . Add ( unbound_amalgamy == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . amalgamy for action in actions . keys ( ) ] ) )
2021-10-15 22:12:20 +00:00
model . AddMaxEquality ( amalgamy , ( unbound_amalgamy , 0 ) )
del unbound_amalgamy
2021-06-08 22:28:15 +00:00
# Antiquity calculation
2021-10-15 22:12:20 +00:00
antiquity = model . NewIntVar ( ' antiquity ' , lb = 0 )
unbound_antiquity = model . NewIntVar ( ' unbound antiquity ' )
2022-04-11 18:38:25 +00:00
model . Add ( unbound_antiquity == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . antiquity for action in actions . keys ( ) ] ) )
2021-10-15 22:12:20 +00:00
model . AddMaxEquality ( antiquity , ( unbound_antiquity , 0 ) )
del unbound_antiquity
2021-06-08 22:28:15 +00:00
2021-09-01 19:04:18 +00:00
2021-06-08 22:28:15 +00:00
# Menace calculation
2021-10-15 22:12:20 +00:00
menace = model . NewIntVar ( ' menace ' , lb = 0 )
unbound_menace = model . NewIntVar ( ' unbound menace ' )
2021-09-01 19:04:18 +00:00
2021-09-22 22:06:44 +00:00
constant_base_menace = model . NewIntVar ( ' constant base menace ' )
2022-04-11 18:38:25 +00:00
model . Add ( constant_base_menace == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . menace for action in actions . keys ( ) ] ) )
2021-09-01 19:04:18 +00:00
# Calculate menace from Vake skulls
vake_skull_bonus_menace = model . NewIntVarFromDomain ( cp_model . Domain . FromValues ( [ 0 , 2 , 3 ] ) , ' vake skull bonus menace ' )
2021-09-22 22:06:44 +00:00
vake_skulls_times_two = model . NewIntVar ( ' vake skulls times two ' , lb = 0 )
model . AddMultiplicationEquality ( vake_skulls_times_two , ( 2 , actions [ Skull . VAKE_SKULL ] ) )
2021-09-01 19:04:18 +00:00
model . AddMinEquality ( vake_skull_bonus_menace , [ vake_skulls_times_two , 3 ] )
del vake_skulls_times_two
2021-10-15 22:12:20 +00:00
model . Add ( unbound_menace == constant_base_menace + vake_skull_bonus_menace )
model . AddMaxEquality ( menace , ( unbound_menace , 0 ) )
del unbound_menace , constant_base_menace , vake_skull_bonus_menace
2021-06-08 22:28:15 +00:00
2021-09-01 18:21:47 +00:00
2021-06-08 22:28:15 +00:00
# Implausibility calculation
2021-09-22 22:06:44 +00:00
implausibility = model . NewIntVar ( ' implausibility ' )
2021-09-01 18:21:47 +00:00
2021-09-22 22:06:44 +00:00
constant_base_implausibility = model . NewIntVar ( ' implausibility ' )
2022-04-11 18:38:25 +00:00
model . Add ( constant_base_implausibility == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . implausibility for action in actions . keys ( ) ] ) )
2021-09-01 18:21:47 +00:00
# Calculate implausibility from Vake skulls
# This is a partial sum formula.
2021-09-22 22:06:44 +00:00
vake_skull_implausibility = model . NewIntVar ( ' vake skull implausibility ' , lb = 0 )
2021-09-01 18:21:47 +00:00
2021-09-22 22:06:44 +00:00
vake_skull_implausibility_numerator = model . NewIntVar ( ' vake skull implausibility numerator ' , lb = 0 )
2021-09-01 18:21:47 +00:00
vake_skulls = actions [ Skull . VAKE_SKULL ]
2021-09-22 22:06:44 +00:00
vake_skull_implausibility_numerator_second_term = model . NewIntVar ( ' vake skull implausibility numerator second term ' , lb = 0 )
model . AddMultiplicationEquality ( vake_skull_implausibility_numerator_second_term , ( vake_skulls , vake_skulls ) )
2021-09-01 18:21:47 +00:00
2021-09-22 22:06:44 +00:00
vake_skull_implausibility_numerator_third_term = model . NewIntVar ( ' vake skull implausibility numerator third term ' , lb = 0 , ub = 1 )
2021-09-01 18:21:47 +00:00
model . AddModuloEquality ( vake_skull_implausibility_numerator_third_term , vake_skulls , 2 )
model . Add ( vake_skull_implausibility_numerator == - 2 * vake_skulls + vake_skull_implausibility_numerator_second_term + vake_skull_implausibility_numerator_third_term )
del vake_skulls , vake_skull_implausibility_numerator_second_term , vake_skull_implausibility_numerator_third_term
model . AddDivisionEquality ( vake_skull_implausibility , vake_skull_implausibility_numerator , 4 )
del vake_skull_implausibility_numerator
model . Add ( implausibility == constant_base_implausibility + vake_skull_implausibility )
del constant_base_implausibility , vake_skull_implausibility
2021-06-08 22:28:15 +00:00
2021-06-13 18:23:23 +00:00
2021-06-08 22:28:15 +00:00
# Counter-church calculation
# Calculate amount of Counter-church from Holy Relics of the Thigh of Saint Fiacre
2021-06-13 17:37:15 +00:00
holy_relic = actions [ Appendage . FIACRE_THIGH ]
2021-09-22 22:06:44 +00:00
torso_style_divided_by_ten = model . NewIntVar ( ' torso style divided by ten ' , lb = 0 )
2021-06-08 22:28:15 +00:00
model . AddDivisionEquality ( torso_style_divided_by_ten , torso_style , 10 )
2021-09-22 22:06:44 +00:00
holy_relic_counter_church = model . NewIntVar ( ' holy relic counter-church ' , lb = 0 )
model . AddMultiplicationEquality ( holy_relic_counter_church , ( holy_relic , torso_style_divided_by_ten ) )
2021-06-08 22:28:15 +00:00
2021-10-15 22:12:20 +00:00
counter_church = model . NewIntVar ( ' counter-church ' , lb = 0 )
2022-04-11 18:38:25 +00:00
model . Add ( counter_church == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . counter_church for action in actions . keys ( ) ] ) + holy_relic_counter_church )
2021-06-08 22:28:15 +00:00
del holy_relic , torso_style_divided_by_ten , holy_relic_counter_church
2021-06-13 18:23:23 +00:00
2021-06-08 22:28:15 +00:00
# Exhaustion calculation
2021-09-22 22:06:44 +00:00
exhaustion = model . NewIntVar ( ' exhaustion ' , lb = 0 , ub = maximum_exhaustion )
2021-06-08 22:28:15 +00:00
2021-06-13 18:23:23 +00:00
# Exhaustion added by certain buyers
2021-09-22 22:06:44 +00:00
added_exhaustion = model . NewIntVar ( ' added exhaustion ' , lb = 0 , ub = maximum_exhaustion )
2022-04-11 18:38:25 +00:00
model . Add ( exhaustion == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . exhaustion for action in actions . keys ( ) ] ) + added_exhaustion )
2021-06-13 18:23:23 +00:00
2021-06-08 22:28:15 +00:00
# Profit intermediate variables
2021-09-22 22:06:44 +00:00
primary_revenue = model . NewIntVar ( ' primary revenue ' , lb = 0 )
secondary_revenue = model . NewIntVar ( ' secondary revenue ' , lb = 0 )
total_revenue = model . NewIntVar ( ' total revenue ' , lb = 0 )
2021-06-08 22:28:15 +00:00
model . Add ( total_revenue == cp_model . LinearExpr . Sum ( [ primary_revenue , secondary_revenue ] ) )
# Cost
# Calculate value of actions needed to sell the skeleton.
2021-09-22 22:06:44 +00:00
difficulty_level = model . NewIntVar ( ' difficulty level ' )
2021-06-08 22:28:15 +00:00
2021-09-22 22:06:44 +00:00
non_zero_difficulty_level = model . NewIntVar ( ' non-zero difficulty level ' , lb = 1 )
2021-06-08 22:28:15 +00:00
model . AddMaxEquality ( non_zero_difficulty_level , [ difficulty_level , 1 ] )
2021-09-22 22:06:44 +00:00
sale_actions_times_action_value = model . NewIntVar ( ' sale actions times action value ' , lb = 0 )
2021-06-19 12:32:19 +00:00
model . AddDivisionEquality ( sale_actions_times_action_value , model . NewConstant ( round ( DIFFICULTY_SCALER * shadowy_level * Cost . ACTION . value ) ) , non_zero_difficulty_level )
2021-09-22 22:06:44 +00:00
abstract_sale_cost = model . NewIntVar ( ' abstract sale cost ' , lb = 0 )
2021-06-13 17:43:52 +00:00
model . AddDivisionEquality ( abstract_sale_cost , Cost . ACTION . value * * 2 , sale_actions_times_action_value )
2021-09-22 22:06:44 +00:00
sale_cost = model . NewIntVar ( ' sale cost ' , lb = 0 )
2021-06-13 17:43:52 +00:00
model . AddMaxEquality ( sale_cost , [ abstract_sale_cost , Cost . 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.
2021-09-22 22:06:44 +00:00
add_joints_amber_cost = model . NewIntVar ( ' add joints amber cost ' , lb = 0 )
2021-06-08 22:28:15 +00:00
2021-06-13 17:37:15 +00:00
add_joints = actions [ Appendage . ADD_JOINTS ]
2021-06-08 22:28:15 +00:00
2021-09-22 22:06:44 +00:00
base_joints = model . NewIntVar ( ' base joints ' , lb = 0 )
2022-04-11 18:38:25 +00:00
model . Add ( base_joints == cp_model . LinearExpr . WeightedSum ( [ value for ( key , value ) in actions . items ( ) if isinstance ( key , Torso ) ] , [ torso . value . limbs_needed + torso . value . arms + torso . value . legs + torso . value . wings + torso . value . fins + torso . value . tentacles for torso in Torso ] ) )
2021-06-08 22:28:15 +00:00
2021-09-22 22:06:44 +00:00
add_joints_amber_cost_multiple = model . NewIntVar ( ' add joints amber cost multiple ' , lb = 0 )
2021-06-08 22:28:15 +00:00
2021-09-22 22:06:44 +00:00
add_joints_amber_cost_multiple_first_term = model . NewIntVar ( ' add joints amber cost multiple first term ' , lb = 0 )
model . AddMultiplicationEquality ( add_joints_amber_cost_multiple_first_term , ( 25 , base_joints , base_joints , add_joints ) )
2021-06-08 22:28:15 +00:00
2021-09-22 22:06:44 +00:00
add_joints_amber_cost_multiple_second_term = model . NewIntVar ( ' add joints amber cost multiple second term ' , lb = 0 )
model . AddMultiplicationEquality ( add_joints_amber_cost_multiple_second_term , ( 100 , base_joints , add_joints , add_joints ) )
2021-06-08 22:28:15 +00:00
2021-09-22 22:06:44 +00:00
add_joints_amber_cost_multiple_third_term = model . NewIntVar ( ' add joints amber cost multiple third term ' , lb = 0 )
model . AddMultiplicationEquality ( add_joints_amber_cost_multiple_third_term , ( 100 , base_joints , add_joints ) )
2021-06-08 22:28:15 +00:00
2021-09-22 22:06:44 +00:00
add_joints_amber_cost_multiple_fourth_term = model . NewIntVar ( ' add joints amber cost multiple fourth term ' , lb = 0 )
add_joints_amber_cost_multiple_fourth_term_numerator = model . NewIntVar ( ' add joints amber cost multiple fourth term numerator ' , lb = 0 )
add_joints_amber_cost_multiple_fourth_term_numerator_first_term = model . NewIntVar ( ' add joints amber cost multiple fourth term numerator first term ' , lb = 0 )
model . AddMultiplicationEquality ( add_joints_amber_cost_multiple_fourth_term_numerator_first_term , ( 400 , add_joints , add_joints , add_joints ) )
2021-06-11 23:04:21 +00:00
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
2021-09-22 22:06:44 +00:00
add_joints_amber_cost_multiple_fifth_term = model . NewIntVar ( ' add joints amber cost multiple fifth term ' , lb = 0 )
model . AddMultiplicationEquality ( 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-09-22 22:06:44 +00:00
model . AddMultiplicationEquality ( add_joints_amber_cost , ( add_joints_amber_cost_multiple , Cost . WARM_AMBER . value ) )
2021-06-08 22:28:15 +00:00
del add_joints , add_joints_amber_cost_multiple
2021-09-22 22:06:44 +00:00
cost = model . NewIntVar ( ' cost ' , lb = 0 , ub = maximum_cost )
2022-04-11 18:38:25 +00:00
model . Add ( cost == cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ int ( action . value . cost ) for action in actions . keys ( ) ] ) + add_joints_amber_cost + sale_cost )
2021-06-08 22:28:15 +00:00
del sale_cost , add_joints_amber_cost
# Type of skeleton
2021-09-22 22:06:44 +00:00
skeleton_in_progress = model . NewIntVar ( ' skeleton in progress ' , lb = 0 )
2021-06-08 22:28:15 +00:00
# Chimera
model . Add ( skeleton_in_progress == 100 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . CHIMERA ] )
2021-06-08 22:28:15 +00:00
# Humanoid
model . Add ( skeleton_in_progress == 110 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . HUMANOID ] ) \
2021-10-15 22:12:20 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity == 0 ) )
2021-06-08 22:28:15 +00:00
# Ancient Humanoid (UNCERTAIN)
model . Add ( skeleton_in_progress == 111 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . HUMANOID ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( cp_model . BoundedLinearExpression ( antiquity , ( 1 , 5 ) ) ) )
2021-06-08 22:28:15 +00:00
# Neanderthal
model . Add ( skeleton_in_progress == 112 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . HUMANOID ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity > = 6 ) )
2021-06-08 22:28:15 +00:00
# Ape (UNCERTAIN)
model . Add ( skeleton_in_progress == 120 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . APE ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity < = 1 ) )
2021-06-08 22:28:15 +00:00
# Primordial Ape (UNCERTAIN)
model . Add ( skeleton_in_progress == 121 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . APE ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity > = 2 ) )
2021-06-08 22:28:15 +00:00
# Monkey
model . Add ( skeleton_in_progress == 125 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . MONKEY ] ) \
2021-10-15 22:12:20 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity == 0 ) )
2021-06-08 22:28:15 +00:00
# Catarrhine Monkey (UNCERTAIN)
model . Add ( skeleton_in_progress == 126 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . MONKEY ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( cp_model . BoundedLinearExpression ( antiquity , ( 1 , 8 ) ) ) )
2021-06-08 22:28:15 +00:00
# Catarrhine Monkey
model . Add ( skeleton_in_progress == 128 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . MONKEY ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity > = 9 ) )
2021-06-08 22:28:15 +00:00
# Crocodile
model . Add ( skeleton_in_progress == 160 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . REPTILE ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity < = 1 ) )
2021-06-08 22:28:15 +00:00
# Dinosaur
model . Add ( skeleton_in_progress == 161 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . REPTILE ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( cp_model . BoundedLinearExpression ( antiquity , ( 2 , 4 ) ) ) )
2021-06-08 22:28:15 +00:00
# Mesosaur (UNCERTAIN)
model . Add ( skeleton_in_progress == 162 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . REPTILE ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity > = 5 ) )
2021-06-08 22:28:15 +00:00
# Toad
model . Add ( skeleton_in_progress == 170 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . AMPHIBIAN ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity < = 1 ) )
2021-06-08 22:28:15 +00:00
# Primordial Amphibian
model . Add ( skeleton_in_progress == 171 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . AMPHIBIAN ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( cp_model . BoundedLinearExpression ( antiquity , ( 2 , 4 ) ) ) )
2021-06-08 22:28:15 +00:00
# Temnospondyl
model . Add ( skeleton_in_progress == 172 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . AMPHIBIAN ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity > = 5 ) )
2021-06-08 22:28:15 +00:00
# Owl
model . Add ( skeleton_in_progress == 180 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . BIRD ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity < = 1 ) )
2021-06-08 22:28:15 +00:00
# Archaeopteryx
model . Add ( skeleton_in_progress == 181 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . BIRD ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( cp_model . BoundedLinearExpression ( antiquity , ( 2 , 4 ) ) ) )
2021-06-08 22:28:15 +00:00
# Ornithomimosaur (UNCERTAIN)
model . Add ( skeleton_in_progress == 182 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . BIRD ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity > = 5 ) )
2021-06-08 22:28:15 +00:00
# Lamprey
model . Add ( skeleton_in_progress == 190 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . FISH ] ) \
2021-10-15 22:12:20 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity == 0 ) )
2021-06-08 22:28:15 +00:00
# Coelacanth (UNCERTAIN)
model . Add ( skeleton_in_progress == 191 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . FISH ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity > = 1 ) )
2021-06-08 22:28:15 +00:00
# Spider (UNCERTAIN)
model . Add ( skeleton_in_progress == 200 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . SPIDER ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity < = 1 ) )
2021-06-08 22:28:15 +00:00
# Primordial Orb-Weaver (UNCERTAIN)
model . Add ( skeleton_in_progress == 201 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . SPIDER ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( cp_model . BoundedLinearExpression ( antiquity , ( 2 , 7 ) ) ) )
2021-06-08 22:28:15 +00:00
# Trigonotarbid
model . Add ( skeleton_in_progress == 203 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . SPIDER ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity > = 8 ) )
2021-06-08 22:28:15 +00:00
# Beetle (UNCERTAIN)
model . Add ( skeleton_in_progress == 210 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . INSECT ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity < = 1 ) )
2021-06-08 22:28:15 +00:00
# Primordial Beetle (UNCERTAIN)
model . Add ( skeleton_in_progress == 211 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . INSECT ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( cp_model . BoundedLinearExpression ( antiquity , ( 2 , 6 ) ) ) )
2021-06-08 22:28:15 +00:00
# Rhyniognatha
model . Add ( skeleton_in_progress == 212 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . INSECT ] ) \
2021-09-25 03:04:54 +00:00
. OnlyEnforceIf ( model . BoolExpression ( antiquity > = 7 ) )
2021-06-08 22:28:15 +00:00
# Curator
model . Add ( skeleton_in_progress == 300 ) \
2021-06-13 17:37:15 +00:00
. OnlyEnforceIf ( actions [ Declaration . CURATOR ] )
2021-06-08 22:28:15 +00:00
2022-05-26 13:32:04 +00:00
# Skull requirements
model . Add ( torso_style == 110 ) \
. OnlyEnforceIf ( model . BoolExpression ( actions [ Skull . SEGMENTED_RIBCAGE ] > 0 ) )
2021-09-27 21:01:41 +00:00
# Declaration requirements
model . AddIf ( actions [ Declaration . HUMANOID ] ,
( part == 0 for part in ( tails , fins , wings ) ) ,
skulls == 1 ,
( part == 2 for part in ( legs , arms ) ) ,
cp_model . BoundedLinearExpression ( torso_style , ( 10 , 20 ) ) ,
)
model . AddIf ( actions [ Declaration . APE ] ,
( part == 0 for part in ( legs , tails , fins , wings ) ) ,
skulls == 1 ,
arms == 4 ,
cp_model . BoundedLinearExpression ( torso_style , ( 10 , 20 ) ) ,
)
model . AddIf ( actions [ Declaration . MONKEY ] ,
( part == 0 for part in ( legs , fins , wings ) ) ,
( part == 1 for part in ( skulls , tails ) ) ,
arms == 4 ,
cp_model . BoundedLinearExpression ( torso_style , ( 10 , 20 ) ) ,
)
model . AddIf ( actions [ Declaration . BIRD ] ,
( part == 0 for part in ( arms , fins ) ) ,
tails < 2 ,
( part == 2 for part in ( legs , wings ) ) ,
torso_style > = 20 ,
)
model . AddIf ( actions [ Declaration . CURATOR ] ,
( part == 0 for part in ( fins , tails ) ) ,
skulls == 1 ,
( part == 2 for part in ( arms , legs , wings ) ) ,
)
model . AddIf ( actions [ Declaration . REPTILE ] ,
( part == 0 for part in ( fins , wings , arms ) ) ,
( part == 1 for part in ( tails , skulls ) ) ,
legs < 5 ,
torso_style > = 20 ,
)
model . AddIf ( actions [ Declaration . AMPHIBIAN ] ,
( part == 0 for part in ( tails , fins , wings , arms ) ) ,
skulls == 1 ,
legs == 4 ,
torso_style > = 20 ,
)
model . AddIf ( actions [ Declaration . FISH ] ,
( part == 0 for part in ( arms , legs , wings ) ) ,
tails < = 1 ,
skulls == 1 ,
fins > = 2 ,
torso_style > = 20 ,
)
model . AddIf ( actions [ Declaration . INSECT ] ,
( part == 0 for part in ( arms , fins , tails ) ) ,
skulls == 1 ,
wings < 5 ,
legs == 6 ,
torso_style > = 20 ,
)
model . AddIf ( actions [ Declaration . SPIDER ] ,
( part == 0 for part in ( skulls , arms , wings , fins ) ) ,
tails < = 1 ,
legs == 8 ,
torso_style > = 20 ,
)
2021-06-13 17:37:15 +00:00
# Skeleton must have no unfilled skulls
2022-04-11 18:38:25 +00:00
model . Add ( cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . skulls_needed for action in actions . keys ( ) ] ) == 0 )
2021-06-13 17:37:15 +00:00
# Skeleton must have no unfilled limbs
2022-04-11 18:38:25 +00:00
model . Add ( cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . limbs_needed for action in actions . keys ( ) ] ) == 0 )
2021-06-08 22:28:15 +00:00
2021-06-13 17:37:15 +00:00
# Skeleton must have no unfilled tails, unless they were skipped
2022-04-11 18:38:25 +00:00
model . Add ( cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . tails_needed for action in actions . keys ( ) ] ) == 0 ) . OnlyEnforceIf ( actions [ Appendage . SKIP_TAILS ] . Not ( ) )
model . Add ( cp_model . LinearExpr . WeightedSum ( actions . values ( ) , [ action . value . tails_needed for action in actions . keys ( ) ] ) > 0 ) . OnlyEnforceIf ( actions [ Appendage . SKIP_TAILS ] )
2021-06-08 22:28:15 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . A_PALAEONTOLOGIST_WITH_HOARDING_PROPENSITIES ] ,
skeleton_in_progress > = 100 ,
primary_revenue == value + zoological_mania_bonus + 5 ,
secondary_revenue == 500 ,
difficulty_level == 40 * implausibility ,
added_exhaustion == 0 ,
)
2021-06-13 22:37:39 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . A_NAIVE_COLLECTOR ] ,
skeleton_in_progress > = 100 ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue ,
( 250 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 250 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
secondary_revenue == 0 ,
difficulty_level == 25 * implausibility ,
added_exhaustion == 0 ,
)
2021-06-13 22:37:39 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . A_FAMILIAR_BOHEMIAN_SCULPTRESS ] ,
skeleton_in_progress > = 100 ,
antiquity == 0 ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 1000 ,
( 250 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 250 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
secondary_revenue == 250 * counter_church ,
difficulty_level == 50 * implausibility ,
added_exhaustion == 0 ,
)
2021-09-10 19:58:56 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . A_PEDAGOGICALLY_INCLINED_GRANDMOTHER ] ,
skeleton_in_progress > = 100 ,
menace == 0 ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 1000 ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 50 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
secondary_revenue == 0 ,
difficulty_level == 50 * implausibility ,
added_exhaustion == 0 ,
)
2021-06-08 22:28:15 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . A_THEOLOGIAN_OF_THE_OLD_SCHOOL ] ,
skeleton_in_progress > = 100 ,
amalgamy == 0 ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 1000 ,
( 250 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 250 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
secondary_revenue == 0 ,
difficulty_level == 50 * implausibility ,
added_exhaustion == 0 ,
)
2021-06-08 22:28:15 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . AN_ENTHUSIAST_OF_THE_ANCIENT_WORLD ] ,
skeleton_in_progress > = 100 ,
antiquity > 0 ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 50 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
secondary_revenue == 250 * ( antiquity + ( 1 if bone_market_fluctuations == Fluctuation . ANTIQUITY else 0 ) ) ,
difficulty_level == 45 * implausibility ,
added_exhaustion == 0 ,
)
2021-06-08 22:28:15 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . MRS_PLENTY ] ,
skeleton_in_progress > = 100 ,
menace > 0 ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 50 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
secondary_revenue == 250 * menace ,
difficulty_level == 45 * implausibility ,
added_exhaustion == 0 ,
)
2021-06-08 22:28:15 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . A_TENTACLED_SERVANT ] ,
skeleton_in_progress > = 100 ,
amalgamy > 0 ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 250 ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 50 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
secondary_revenue == 250 * ( amalgamy + ( 1 if bone_market_fluctuations == Fluctuation . AMALGAMY else 0 ) ) ,
difficulty_level == 45 * implausibility ,
added_exhaustion == 0 ,
)
2021-06-08 22:28:15 +00:00
2021-10-20 03:20:02 +00:00
model . AddIf ( actions [ Buyer . AN_INVESTMENT_MINDED_AMBASSADOR ] ,
skeleton_in_progress > = 100 ,
antiquity > 0 ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 250 ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 50 ) ) ,
) ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
secondary_revenue ,
(
250 ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
num = partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
variables = (
4 ,
partialmethod ( BoneMarketModel . AddApproximateExponentiationEquality ,
var = antiquity ,
exp = 2.1 ,
upto = MAXIMUM_ATTRIBUTE ,
) ,
) if bone_market_fluctuations == Fluctuation . ANTIQUITY else
(
4 ,
antiquity ,
antiquity ,
) ,
) ,
denom = 5 ,
) ,
) ,
) ,
difficulty_level == 75 * implausibility ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
added_exhaustion ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality , variables = ( antiquity , antiquity ) ) ,
25 ,
) ,
)
2021-06-08 22:28:15 +00:00
2021-10-20 03:20:02 +00:00
model . AddIf ( actions [ Buyer . A_TELLER_OF_TERRORS ] ,
skeleton_in_progress > = 100 ,
menace > 0 ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 250 ,
( 10 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 10 ) ) ,
) ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
secondary_revenue ,
(
200 ,
partialmethod ( BoneMarketModel . AddApproximateExponentiationEquality , var = menace , exp = 2.1 , upto = MAXIMUM_ATTRIBUTE ) ,
) if bone_market_fluctuations == Fluctuation . MENACE else
(
200 ,
menace ,
menace ,
) ,
) ,
difficulty_level == 75 * implausibility ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
added_exhaustion ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality , variables = ( menace , menace ) ) ,
25 ,
) ,
)
2021-06-13 22:37:39 +00:00
2021-10-20 03:20:02 +00:00
model . AddIf ( actions [ Buyer . A_TENTACLED_ENTREPRENEUR ] ,
skeleton_in_progress > = 100 ,
amalgamy > 0 ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 250 ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 50 ) ) ,
) ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
secondary_revenue ,
(
200 ,
partialmethod ( BoneMarketModel . AddApproximateExponentiationEquality , var = amalgamy , exp = 2.1 , upto = MAXIMUM_ATTRIBUTE ) ,
) if bone_market_fluctuations == Fluctuation . AMALGAMY else
(
200 ,
amalgamy ,
amalgamy ,
) ,
) ,
difficulty_level == 75 * implausibility ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
added_exhaustion ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality , variables = ( amalgamy , amalgamy ) ) ,
25 ,
) ,
)
2021-06-13 22:37:39 +00:00
2021-10-20 03:20:02 +00:00
model . AddIf ( actions [ Buyer . AN_AUTHOR_OF_GOTHIC_TALES ] ,
skeleton_in_progress > = 100 ,
antiquity > 0 ,
menace > 0 ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 250 ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 50 ) ) ,
) ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
secondary_revenue ,
(
250 ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
num = partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
variables = ( 2 * antiquity + 1 , menace )
) ,
denom = 2 ,
) ,
) if bone_market_fluctuations == Fluctuation . MENACE else
(
250 ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
num = partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
variables = ( antiquity , 2 * menace + 1 )
) ,
denom = 2 ,
) ,
) if bone_market_fluctuations == Fluctuation . ANTIQUITY else
(
250 ,
antiquity ,
menace ,
) ,
) ,
difficulty_level == 75 * implausibility ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
added_exhaustion ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality , variables = ( antiquity , menace ) ) ,
20 ,
) ,
)
2021-06-13 22:37:39 +00:00
2021-10-20 03:20:02 +00:00
model . AddIf ( actions [ Buyer . A_ZAILOR_WITH_PARTICULAR_INTERESTS ] ,
skeleton_in_progress > = 100 ,
antiquity > 0 ,
amalgamy > 0 ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 250 ,
( 10 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 10 ) ) ,
) ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
secondary_revenue ,
(
250 ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
num = partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
variables = ( 2 * antiquity + 1 , amalgamy )
) ,
denom = 2 ,
) ,
) if bone_market_fluctuations == Fluctuation . AMALGAMY else
(
250 ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
num = partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
variables = ( antiquity , 2 * amalgamy + 1 )
) ,
denom = 2 ,
) ,
) if bone_market_fluctuations == Fluctuation . ANTIQUITY else
(
250 ,
antiquity ,
amalgamy ,
) ,
) ,
difficulty_level == 75 * implausibility ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
added_exhaustion ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality , variables = ( antiquity , amalgamy ) ) ,
20 ,
) ,
)
2021-06-13 22:37:39 +00:00
2021-10-20 03:20:02 +00:00
model . AddIf ( actions [ Buyer . A_RUBBERY_COLLECTOR ] ,
skeleton_in_progress > = 100 ,
amalgamy > 0 ,
menace > 0 ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 250 ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 50 ) ) ,
) ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
secondary_revenue ,
(
250 ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
num = partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
variables = ( 2 * amalgamy + 1 , menace )
) ,
denom = 2 ,
) ,
) if bone_market_fluctuations == Fluctuation . MENACE else
(
250 ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
num = partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
variables = ( amalgamy , 2 * menace + 1 )
) ,
denom = 2 ,
) ,
) if bone_market_fluctuations == Fluctuation . AMALGAMY else
(
250 ,
amalgamy ,
menace ,
) ,
) ,
difficulty_level == 75 * implausibility ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
added_exhaustion ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality , variables = ( amalgamy , menace ) ) ,
20 ,
) ,
)
2021-06-13 22:37:39 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . A_CONSTABLE ] ,
cp_model . BoundedLinearExpression ( skeleton_in_progress , ( 110 , 119 ) ) ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 1000 ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value , denom = 50 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
secondary_revenue == 0 ,
difficulty_level == 50 * implausibility ,
added_exhaustion == 0 ,
)
2021-06-13 22:37:39 +00:00
2021-10-20 03:20:02 +00:00
model . AddIf ( actions [ Buyer . AN_ENTHUSIAST_IN_SKULLS ] ,
skeleton_in_progress > = 100 ,
skulls > = 2 ,
primary_revenue == value + zoological_mania_bonus ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
secondary_revenue ,
(
1250 ,
partialmethod ( BoneMarketModel . AddApproximateExponentiationEquality , var = skulls - 1 , exp = 1.8 , upto = MAXIMUM_ATTRIBUTE ) ,
) ,
) ,
difficulty_level == 60 * implausibility ,
partialmethod ( BoneMarketModel . AddDivisionEquality , added_exhaustion , secondary_revenue , 5000 ) ,
)
2021-06-13 22:37:39 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . A_DREARY_MIDNIGHTER ] ,
cp_model . BoundedLinearExpression ( skeleton_in_progress , ( 110 , 299 ) ) ,
amalgamy == 0 ,
counter_church == 0 ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 300 ,
( 3 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 3 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
secondary_revenue == 250 ,
difficulty_level == 100 * implausibility ,
added_exhaustion == 0 ,
)
2021-06-15 12:10:20 +00:00
2021-10-15 22:46:13 +00:00
{
model . AddIf ( actions [ getattr ( Buyer , ' A_COLOURFUL_PHANTASIST_ ' + style ) ] ,
skeleton_in_progress > = 100 ,
implausibility > = 2 ,
attribute > = 4 ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 100 ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 50 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality , secondary_revenue - 250 , ( 250 , attribute , implausibility ) ) ,
difficulty_level == 0 ,
partialmethod ( BoneMarketModel . AddDivisionEquality , added_exhaustion , secondary_revenue , 5000 ) ,
) for style , attribute in (
( ' BAZAARINE ' , amalgamy ) ,
( ' NOCTURNAL ' , menace ) ,
( ' CELESTIAL ' , antiquity ) ,
)
}
2021-06-15 12:10:20 +00:00
2021-10-20 03:20:02 +00:00
model . AddIf ( actions [ Buyer . AN_INGENUOUS_MALACOLOGIST ] ,
tentacles > = 4 ,
skeleton_in_progress > = 100 ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 250 ,
( 250 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value , denom = 250 ) ) ,
) ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
secondary_revenue ,
(
250 ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
num = partialmethod ( BoneMarketModel . AddApproximateExponentiationEquality , var = tentacles , exp = 2.2 , upto = MAXIMUM_ATTRIBUTE ) ,
denom = 5 ,
) ,
) ,
) ,
difficulty_level == 60 * implausibility ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
added_exhaustion ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality , variables = ( tentacles , tentacles ) ) ,
100 ,
) ,
)
2021-06-15 12:10:20 +00:00
2021-10-20 03:20:02 +00:00
model . AddIf ( actions [ Buyer . AN_ENTERPRISING_BOOT_SALESMAN ] ,
menace == 0 ,
amalgamy == 0 ,
skeleton_in_progress > = 100 ,
legs > = 4 ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value + zoological_mania_bonus , denom = 50 ) ) ,
) ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
secondary_revenue ,
( 50 , partialmethod ( BoneMarketModel . AddApproximateExponentiationEquality , var = legs , exp = 2.2 , upto = MAXIMUM_ATTRIBUTE ) ) ,
) ,
difficulty_level == 0 ,
partialmethod ( BoneMarketModel . AddDivisionEquality ,
added_exhaustion ,
partialmethod ( BoneMarketModel . AddMultiplicationEquality , variables = ( legs , legs ) ) ,
100 ,
) ,
)
2021-07-06 19:18:07 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . THE_DUMBWAITER_OF_BALMORAL ] ,
cp_model . BoundedLinearExpression ( skeleton_in_progress , ( 180 , 189 ) ) ,
value > = 250 ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue ,
( 250 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value , denom = 250 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
secondary_revenue == 0 ,
difficulty_level == 200 ,
added_exhaustion == 0 ,
)
2021-06-25 23:37:41 +00:00
2021-10-15 22:46:13 +00:00
model . AddIf ( actions [ Buyer . THE_CARPENTERS_GRANDDAUGHTER ] ,
skeleton_in_progress > = 100 ,
value + zoological_mania_bonus > = 30000 ,
primary_revenue == 31250 ,
secondary_revenue == 0 ,
difficulty_level == 100 * implausibility ,
added_exhaustion == 0 ,
)
2021-06-25 23:37:41 +00:00
2021-10-16 15:28:07 +00:00
# The Trifling Diplomat
2021-10-15 22:46:13 +00:00
{
2021-10-16 15:28:07 +00:00
model . AddIf ( actions [ getattr ( DiplomatFascination , str ( attribute ) . upper ( ) ) . value ] ,
2021-10-15 22:46:13 +00:00
skeleton_in_progress > = 100 ,
attribute > = 5 ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 50 ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value , denom = 50 ) ) ,
) ,
2021-10-15 22:46:13 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality , secondary_revenue , ( 50 , attribute , attribute ) ) ,
difficulty_level == 0 ,
partialmethod ( BoneMarketModel . AddDivisionEquality , added_exhaustion , secondary_revenue , 5000 ) ,
) for attribute in (
amalgamy ,
antiquity ,
2021-10-16 02:39:41 +00:00
menace ,
2021-10-15 22:46:13 +00:00
)
}
2021-10-16 18:11:51 +00:00
{
2021-10-16 15:28:07 +00:00
model . AddIf ( actions [ getattr ( DiplomatFascination , fascination ) . value ] ,
* criteria ,
2021-10-16 20:11:12 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
primary_revenue - 50 ,
( 50 , partialmethod ( BoneMarketModel . AddDivisionEquality , num = value , denom = 50 ) ) ,
) ,
2021-10-16 18:11:51 +00:00
partialmethod ( BoneMarketModel . AddMultiplicationEquality ,
secondary_revenue ,
2021-10-16 20:11:12 +00:00
(
50 ,
partialmethod ( BoneMarketModel . AddApproximateExponentiationEquality ,
var = partialmethod ( BoneMarketModel . AddDivisionEquality , num = amalgamy + antiquity + menace , denom = 3 ) ,
exp = 2.2 ,
upto = MAXIMUM_ATTRIBUTE ,
) ,
) ,
2021-10-16 18:11:51 +00:00
) ,
2021-10-16 15:28:07 +00:00
difficulty_level == 0 ,
partialmethod ( BoneMarketModel . AddDivisionEquality , added_exhaustion , secondary_revenue , 5000 ) ,
2021-10-16 18:11:51 +00:00
) for fascination , criteria in (
2021-10-28 05:19:39 +00:00
( ' AMPHIBIAN ' , ( cp_model . BoundedLinearExpression ( skeleton_in_progress , ( 170 , 179 ) ) , ) ) ,
2021-10-16 18:11:51 +00:00
( ' BIRD ' , ( cp_model . BoundedLinearExpression ( skeleton_in_progress , ( 180 , 189 ) ) , ) ) ,
( ' FISH ' , ( cp_model . BoundedLinearExpression ( skeleton_in_progress , ( 190 , 199 ) ) , ) ) ,
( ' INSECT ' , ( cp_model . BoundedLinearExpression ( skeleton_in_progress , ( 210 , 219 ) ) , ) ) ,
2021-11-02 20:26:08 +00:00
( ' LEGS ' , ( skeleton_in_progress > = 100 , legs > = 10 ) ) ,
2021-10-16 18:11:51 +00:00
( ' REPTILE ' , ( cp_model . BoundedLinearExpression ( skeleton_in_progress , ( 160 , 169 ) ) , ) ) ,
( ' SKULLS ' , ( skeleton_in_progress > = 100 , skulls > = 5 ) ) ,
)
}
2021-08-11 23:35:15 +00:00
2021-06-08 22:28:15 +00:00
# Maximize profit margin
2021-09-22 22:06:44 +00:00
net_profit = model . NewIntVar ( ' net profit ' )
2021-06-08 22:28:15 +00:00
model . Add ( net_profit == total_revenue - cost )
# This is necessary to preserve some degree of precision after dividing
2021-09-22 22:06:44 +00:00
multiplied_net_profit = model . NewIntVar ( ' multiplied net profit ' , lb = cp_model . INT32_MIN * PROFIT_MARGIN_MULTIPLIER , ub = cp_model . INT32_MAX * PROFIT_MARGIN_MULTIPLIER )
model . AddMultiplicationEquality ( multiplied_net_profit , ( net_profit , PROFIT_MARGIN_MULTIPLIER ) )
2021-06-08 22:28:15 +00:00
2021-09-22 22:06:44 +00:00
absolute_multiplied_net_profit = model . NewIntVar ( ' absolute multiplied net profit ' , lb = 0 , ub = cp_model . INT32_MAX * PROFIT_MARGIN_MULTIPLIER )
2021-06-08 22:28:15 +00:00
model . AddAbsEquality ( absolute_multiplied_net_profit , multiplied_net_profit )
2021-09-22 22:06:44 +00:00
absolute_profit_margin = model . NewIntVar ( ' absolute profit margin ' , lb = cp_model . INT32_MIN * PROFIT_MARGIN_MULTIPLIER , ub = cp_model . INT32_MAX * PROFIT_MARGIN_MULTIPLIER )
2021-06-08 22:28:15 +00:00
model . AddDivisionEquality ( absolute_profit_margin , absolute_multiplied_net_profit , total_revenue )
2021-09-22 22:06:44 +00:00
profit_margin = model . NewIntVar ( ' profit margin ' , lb = cp_model . INT32_MIN * PROFIT_MARGIN_MULTIPLIER , ub = cp_model . INT32_MAX * PROFIT_MARGIN_MULTIPLIER )
2021-06-08 22:28:15 +00:00
2021-09-25 03:04:54 +00:00
positive_net_profit = model . BoolExpression ( net_profit > = 0 )
2021-06-08 22:28:15 +00:00
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
2021-06-13 22:37:39 +00:00
2021-06-08 22:28:15 +00:00
model . Maximize ( profit_margin )
2021-06-15 19:13:07 +00:00
class SkeletonPrinter ( cp_model . CpSolverSolutionCallback ) :
2021-06-18 19:44:30 +00:00
""" A class that prints the steps that comprise a skeleton as well as relevant attributes. """
2021-09-19 00:09:13 +00:00
__slots__ = ' this ' , ' __solution_count '
2021-06-15 19:13:07 +00:00
def __init__ ( self ) :
cp_model . CpSolverSolutionCallback . __init__ ( self )
self . __solution_count = 0
def PrintableSolution ( self , solver = None ) :
2021-06-18 19:44:30 +00:00
""" Print the latest solution of a provided solver. """
2021-06-15 19:13:07 +00:00
output = " "
# Allows use as a callback
if solver is None :
solver = self
for action in actions . keys ( ) :
for _ in range ( int ( solver . Value ( actions [ action ] ) ) ) :
output + = str ( action ) + " \n "
2021-09-19 00:55:22 +00:00
output + = f """
Profit : £ { solver . Value ( net_profit ) / 100 : , .2 f }
Profit Margin : { solver . Value ( profit_margin ) / PROFIT_MARGIN_MULTIPLIER : + , .2 % }
2021-06-15 19:13:07 +00:00
2021-09-19 00:55:22 +00:00
Total Revenue : £ { solver . Value ( total_revenue ) / 100 : , .2 f }
Primary Revenue : £ { solver . Value ( primary_revenue ) / 100 : , .2 f }
Secondary Revenue : £ { solver . Value ( secondary_revenue ) / 100 : , .2 f }
2021-06-15 19:13:07 +00:00
2021-09-19 00:55:22 +00:00
Cost : £ { solver . Value ( cost ) / 100 : , .2 f }
2021-06-15 19:13:07 +00:00
2021-09-19 00:55:22 +00:00
Value : £ { solver . Value ( value ) / 100 : , .2 f }
Amalgamy : { solver . Value ( amalgamy ) : n }
Antiquity : { solver . Value ( antiquity ) : n }
Menace : { solver . Value ( menace ) : n }
Counter - Church : { solver . Value ( counter_church ) : n }
Implausibility : { solver . Value ( implausibility ) : n }
2021-06-15 19:13:07 +00:00
2021-09-19 00:55:22 +00:00
Exhaustion : { solver . Value ( exhaustion ) : n } """
2021-06-15 19:13:07 +00:00
return output
def OnSolutionCallback ( self ) :
self . __solution_count + = 1
2021-06-15 21:50:28 +00:00
# Prints current solution to window
stdscr . clear ( )
stdscr . addstr ( self . PrintableSolution ( ) )
2021-09-19 00:55:22 +00:00
stdscr . addstr ( stdscr . getmaxyx ( ) [ 0 ] - 1 , 0 , f " Skeleton # { self . __solution_count : n } " )
2021-06-15 21:50:28 +00:00
stdscr . refresh ( )
2021-06-15 19:13:07 +00:00
def SolutionCount ( self ) :
return self . __solution_count
printer = SkeletonPrinter ( )
2021-06-08 22:28:15 +00:00
solver = cp_model . CpSolver ( )
2021-10-16 02:32:55 +00:00
if workers :
2022-04-19 15:02:03 +00:00
solver . parameters . num_workers = workers
2021-06-16 16:32:05 +00:00
solver . parameters . max_time_in_seconds = time_limit
2021-06-08 22:28:15 +00:00
2021-06-15 21:50:28 +00:00
# There's no window in verbose mode
if stdscr is None :
solver . parameters . log_search_progress = True
solver . Solve ( model )
else :
2021-10-16 02:32:55 +00:00
solver . Solve ( model , printer )
2021-06-15 21:50:28 +00:00
status = solver . StatusName ( )
2021-06-18 18:48:20 +00:00
if status == ' INFEASIBLE ' :
2021-06-08 22:28:15 +00:00
raise RuntimeError ( " There is no satisfactory skeleton. " )
2021-06-18 18:48:20 +00:00
elif status == ' FEASIBLE ' :
2021-06-08 22:28:15 +00:00
print ( " WARNING: skeleton may be suboptimal. " )
2021-06-18 18:48:20 +00:00
elif status != ' OPTIMAL ' :
2021-09-19 00:55:22 +00:00
raise RuntimeError ( f " Unknown status returned: { status } . " )
2021-06-08 22:28:15 +00:00
2021-06-15 21:50:28 +00:00
return printer . PrintableSolution ( solver )