Make buyers part of model

Buyers are now part of the model, rather than being fixed. This means
that the model can choose from among all buyers and determine the most
profitable option.

Accomplishing this meant replacing the if-else control flow with a large
quantity of half-reified constraints.
This commit is contained in:
Jeremy Saklad 2021-06-13 17:37:39 -05:00
parent 52a509bd19
commit d571167742
Signed by: Jeremy Saklad
GPG Key ID: 9CA2149583EDBF84
1 changed files with 356 additions and 294 deletions

View File

@ -1172,12 +1172,23 @@ def Solve():
for embellishment in Embellishment:
actions[embellishment] = model.NewIntVar(0, cp_model.INT32_MAX, embellishment.value.name)
# Buyer
for buyer in Buyer:
actions[buyer] = model.NewBoolVar(buyer.value.name)
# 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)
# One buyer
model.Add(cp_model.LinearExpr.Sum([value for (key, value) in actions.items() if isinstance(key, Buyer)]) == 1)
# Set buyer
if BUYER is not None:
model.Add(actions[BUYER] == 1)
# Value calculation
original_value = model.NewIntVar(0, cp_model.INT32_MAX, 'original value')
model.Add(original_value == cp_model.LinearExpr.ScalProd(actions.values(), [action.value.value for action in actions.keys()]))
@ -1265,7 +1276,6 @@ def Solve():
# Profit intermediate variables
value_remainder = model.NewIntVar(0, cp_model.INT32_MAX, 'value remainder')
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')
@ -1537,309 +1547,361 @@ def Solve():
model.Add(cp_model.LinearExpr.ScalProd(actions.values(), [action.value.tails_needed for action in actions.keys()]) == 0).OnlyEnforceIf(actions[Appendage.SKIP_TAILS].Not())
model.Add(cp_model.LinearExpr.ScalProd(actions.values(), [action.value.tails_needed for action in actions.keys()]) >= 0).OnlyEnforceIf(actions[Appendage.SKIP_TAILS])
if 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)
# A Palaeontologist with Hoarding Propensities
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.A_PALAEONTOLOGIST_WITH_HOARDING_PROPENSITIES])
# Difficulty Level
model.Add(difficulty_level == 40*implausibility)
model.Add(primary_revenue == value + 5).OnlyEnforceIf(actions[Buyer.A_PALAEONTOLOGIST_WITH_HOARDING_PROPENSITIES])
model.Add(secondary_revenue == 500).OnlyEnforceIf(actions[Buyer.A_PALAEONTOLOGIST_WITH_HOARDING_PROPENSITIES])
# Added Exhaustion
model.Add(added_exhaustion == 0)
elif BUYER == Buyer.A_NAIVE_COLLECTOR:
model.Add(skeleton_in_progress >= 100)
model.Add(difficulty_level == 40*implausibility).OnlyEnforceIf(actions[Buyer.A_PALAEONTOLOGIST_WITH_HOARDING_PROPENSITIES])
model.AddModuloEquality(value_remainder, value, 250)
model.Add(added_exhaustion == 0).OnlyEnforceIf(actions[Buyer.A_PALAEONTOLOGIST_WITH_HOARDING_PROPENSITIES])
# Revenue
model.Add(primary_revenue == value - value_remainder)
model.Add(secondary_revenue == 0)
# Difficulty Level
model.Add(difficulty_level == 25*implausibility)
# A Naive Collector
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.A_NAIVE_COLLECTOR])
# Added Exhaustion
model.Add(added_exhaustion == 0)
elif BUYER == Buyer.A_FAMILIAR_BOHEMIAN_SCULPTRESS:
model.Add(skeleton_in_progress >= 100)
model.Add(antiquity <= 0)
value_remainder = model.NewIntVar(0, 249, '{}: {}'.format(Buyer.A_NAIVE_COLLECTOR.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 250)
model.AddModuloEquality(value_remainder, value, 250)
model.Add(primary_revenue == value - value_remainder).OnlyEnforceIf(actions[Buyer.A_NAIVE_COLLECTOR])
model.Add(secondary_revenue == 0).OnlyEnforceIf(actions[Buyer.A_NAIVE_COLLECTOR])
# Revenue
model.Add(primary_revenue == value - value_remainder + 1000)
model.Add(secondary_revenue == 250*counter_church)
model.Add(difficulty_level == 25*implausibility).OnlyEnforceIf(actions[Buyer.A_NAIVE_COLLECTOR])
# Difficulty Level
model.Add(difficulty_level == 50*implausibility)
model.Add(added_exhaustion == 0).OnlyEnforceIf(actions[Buyer.A_NAIVE_COLLECTOR])
# Added Exhaustion
model.Add(added_exhaustion == 0)
elif BUYER == Buyer.A_PEDAGOGICALLY_INCLINED_GRANDMOTHER:
model.Add(skeleton_in_progress >= 100)
model.Add(menace <= 0)
del value_remainder
model.AddModuloEquality(value_remainder, value, 50)
# Revenue
model.Add(primary_revenue == value - value_remainder + 1000)
model.Add(secondary_revenue == 0)
# A Familiar Bohemian Sculptress
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.A_FAMILIAR_BOHEMIAN_SCULPTRESS])
model.Add(antiquity <= 0).OnlyEnforceIf(actions[Buyer.A_FAMILIAR_BOHEMIAN_SCULPTRESS])
# Difficulty Level
model.Add(difficulty_level == 50*implausibility)
value_remainder = model.NewIntVar(0, 249, '{}: {}'.format(Buyer.A_FAMILIAR_BOHEMIAN_SCULPTRESS.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 250)
# Added Exhaustion
model.Add(added_exhaustion == 0)
elif BUYER == Buyer.A_THEOLOGIAN_OF_THE_OLD_SCHOOL:
model.Add(skeleton_in_progress >= 100)
model.Add(amalgamy <= 0)
model.Add(primary_revenue == value - value_remainder + 1000).OnlyEnforceIf(actions[Buyer.A_FAMILIAR_BOHEMIAN_SCULPTRESS])
model.Add(secondary_revenue == 250*counter_church).OnlyEnforceIf(actions[Buyer.A_FAMILIAR_BOHEMIAN_SCULPTRESS])
model.AddModuloEquality(value_remainder, value, 250)
model.Add(difficulty_level == 50*implausibility).OnlyEnforceIf(actions[Buyer.A_FAMILIAR_BOHEMIAN_SCULPTRESS])
# Revenue
model.Add(primary_revenue == value - value_remainder + 1000)
model.Add(secondary_revenue == 0)
model.Add(added_exhaustion == 0).OnlyEnforceIf(actions[Buyer.A_FAMILIAR_BOHEMIAN_SCULPTRESS])
# Difficulty Level
model.Add(difficulty_level == 50*implausibility)
del value_remainder
# Added Exhaustion
model.Add(added_exhaustion == 0)
elif BUYER == Buyer.AN_ENTHUSIAST_OF_THE_ANCIENT_WORLD:
model.Add(skeleton_in_progress >= 100)
model.Add(antiquity > 0)
model.AddModuloEquality(value_remainder, value, 50)
# A Pedagogically Inclined Grandmother
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.A_PEDAGOGICALLY_INCLINED_GRANDMOTHER])
model.Add(menace <= 0).OnlyEnforceIf(actions[Buyer.A_PEDAGOGICALLY_INCLINED_GRANDMOTHER])
# Revenue
model.Add(primary_revenue == value - value_remainder)
model.Add(secondary_revenue == 250*antiquity + (250 if BONE_MARKET_FLUCTUATIONS == Fluctuation.ANTIQUITY else 0))
value_remainder = model.NewIntVar(0, 49, '{}: {}'.format(Buyer.A_PEDAGOGICALLY_INCLINED_GRANDMOTHER.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 50)
# Difficulty Level
model.Add(difficulty_level == 45*implausibility)
model.Add(primary_revenue == value - value_remainder + 1000).OnlyEnforceIf(actions[Buyer.A_PEDAGOGICALLY_INCLINED_GRANDMOTHER])
model.Add(secondary_revenue == 0).OnlyEnforceIf(actions[Buyer.A_PEDAGOGICALLY_INCLINED_GRANDMOTHER])
# Added Exhaustion
model.Add(added_exhaustion == 0)
elif BUYER == Buyer.MRS_PLENTY:
model.Add(skeleton_in_progress >= 100)
model.Add(menace > 0)
model.Add(difficulty_level == 50*implausibility).OnlyEnforceIf(actions[Buyer.A_PEDAGOGICALLY_INCLINED_GRANDMOTHER])
model.AddModuloEquality(value_remainder, value, 50)
model.Add(added_exhaustion == 0).OnlyEnforceIf(actions[Buyer.A_PEDAGOGICALLY_INCLINED_GRANDMOTHER])
# Revenue
model.Add(primary_revenue == value - value_remainder)
model.Add(secondary_revenue == 250*menace)
del value_remainder
# Difficulty Level
model.Add(difficulty_level == 45*implausibility)
# Added Exhaustion
model.Add(added_exhaustion == 0)
elif BUYER == Buyer.A_TENTACLED_SERVANT:
model.Add(skeleton_in_progress >= 100)
model.Add(amalgamy > 0)
# A Theologian of the Old School
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.A_THEOLOGIAN_OF_THE_OLD_SCHOOL])
model.Add(amalgamy <= 0).OnlyEnforceIf(actions[Buyer.A_THEOLOGIAN_OF_THE_OLD_SCHOOL])
model.AddModuloEquality(value_remainder, value, 50)
value_remainder = model.NewIntVar(0, 249, '{}: {}'.format(Buyer.A_THEOLOGIAN_OF_THE_OLD_SCHOOL.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 250)
# Revenue
model.Add(primary_revenue == value - value_remainder + 250)
model.Add(secondary_revenue == 250*amalgamy + (250 if BONE_MARKET_FLUCTUATIONS == Fluctuation.AMALGAMY else 0))
model.Add(primary_revenue == value - value_remainder + 1000).OnlyEnforceIf(actions[Buyer.A_THEOLOGIAN_OF_THE_OLD_SCHOOL])
model.Add(secondary_revenue == 0).OnlyEnforceIf(actions[Buyer.A_THEOLOGIAN_OF_THE_OLD_SCHOOL])
# Difficulty Level
model.Add(difficulty_level == 45*implausibility)
model.Add(difficulty_level == 50*implausibility).OnlyEnforceIf(actions[Buyer.A_THEOLOGIAN_OF_THE_OLD_SCHOOL])
# Added Exhaustion
model.Add(added_exhaustion == 0)
elif BUYER == Buyer.AN_INVESTMENT_MINDED_AMBASSADOR:
model.Add(skeleton_in_progress >= 100)
model.Add(antiquity > 0)
model.Add(added_exhaustion == 0).OnlyEnforceIf(actions[Buyer.A_THEOLOGIAN_OF_THE_OLD_SCHOOL])
antiquity_squared = model.NewIntVar(0, cp_model.INT32_MAX, 'antiquity squared')
model.AddMultiplicationEquality(antiquity_squared, [antiquity, antiquity])
del value_remainder
tailfeathers = model.NewIntVar(0, cp_model.INT32_MAX, 'tailfeathers')
if BONE_MARKET_FLUCTUATIONS == Fluctuation.ANTIQUITY:
model.AddApproximateExponentiationEquality(tailfeathers, antiquity, 2.2, MAXIMUM_ATTRIBUTE)
else:
model.Add(tailfeathers == antiquity_squared)
model.AddModuloEquality(value_remainder, value, 50)
extra_value = model.NewIntermediateBoolVar('extra value', value_remainder, cp_model.Domain.FromFlatIntervals([0, cp_model.INT_MAX]))
# An Enthusiast of the Ancient World
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_OF_THE_ANCIENT_WORLD])
model.Add(antiquity > 0).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_OF_THE_ANCIENT_WORLD])
# Revenue
model.Add(primary_revenue == value + 50*extra_value + 250)
model.Add(secondary_revenue == 250*tailfeathers)
value_remainder = model.NewIntVar(0, 49, '{}: {}'.format(Buyer.AN_ENTHUSIAST_OF_THE_ANCIENT_WORLD.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 50)
# Difficulty Level
model.Add(difficulty_level == 75*implausibility)
model.Add(primary_revenue == value - value_remainder).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_OF_THE_ANCIENT_WORLD])
model.Add(secondary_revenue == 250*antiquity + (250 if BONE_MARKET_FLUCTUATIONS == Fluctuation.ANTIQUITY else 0)).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_OF_THE_ANCIENT_WORLD])
# Added Exhaustion
model.AddDivisionEquality(added_exhaustion, antiquity_squared, 20)
elif BUYER == Buyer.A_TELLER_OF_TERRORS:
model.Add(skeleton_in_progress >= 100)
model.Add(menace > 0)
model.Add(difficulty_level == 45*implausibility).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_OF_THE_ANCIENT_WORLD])
menace_squared = model.NewIntVar(0, cp_model.INT32_MAX, 'menace squared')
model.AddMultiplicationEquality(menace_squared, [menace, menace])
model.Add(added_exhaustion == 0).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_OF_THE_ANCIENT_WORLD])
model.AddModuloEquality(value_remainder, value, 10)
del value_remainder
# Revenue
model.Add(primary_revenue == value - value_remainder + 50)
model.Add(secondary_revenue == 50*menace_squared)
# Difficulty Level
model.Add(difficulty_level == 75*implausibility)
# Mrs Plenty
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.MRS_PLENTY])
model.Add(menace > 0).OnlyEnforceIf(actions[Buyer.MRS_PLENTY])
# Added Exhaustion
model.AddDivisionEquality(added_exhaustion, menace_squared, 100)
elif BUYER == Buyer.A_TENTACLED_ENTREPRENEUR:
model.Add(skeleton_in_progress >= 100)
model.Add(amalgamy > 0)
value_remainder = model.NewIntVar(0, 49, '{}: {}'.format(Buyer.MRS_PLENTY.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 50)
amalgamy_squared = model.NewIntVar(0, cp_model.INT32_MAX, 'amalgamy squared')
model.AddMultiplicationEquality(amalgamy_squared, [amalgamy, amalgamy])
model.Add(primary_revenue == value - value_remainder).OnlyEnforceIf(actions[Buyer.MRS_PLENTY])
model.Add(secondary_revenue == 250*menace).OnlyEnforceIf(actions[Buyer.MRS_PLENTY])
final_breaths = model.NewIntVar(0, cp_model.INT32_MAX, 'final breaths')
if BONE_MARKET_FLUCTUATIONS == Fluctuation.AMALGAMY:
model.AddApproximateExponentiationEquality(final_breaths, amalgamy, 2.2, MAXIMUM_ATTRIBUTE)
else:
model.Add(final_breaths == amalgamy_squared)
model.Add(difficulty_level == 45*implausibility).OnlyEnforceIf(actions[Buyer.MRS_PLENTY])
model.AddModuloEquality(value_remainder, value, 50)
model.Add(added_exhaustion == 0).OnlyEnforceIf(actions[Buyer.MRS_PLENTY])
# Revenue
model.Add(primary_revenue == value - value_remainder + 250)
model.Add(secondary_revenue == 50*final_breaths)
del value_remainder
# Difficulty Level
model.Add(difficulty_level == 75*implausibility)
# Added Exhaustion
model.AddDivisionEquality(added_exhaustion, amalgamy_squared, 100)
elif BUYER == Buyer.AN_AUTHOR_OF_GOTHIC_TALES:
model.Add(skeleton_in_progress >= 100)
model.Add(antiquity > 0)
model.Add(menace > 0)
# A Tentacled Servant
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.A_TENTACLED_SERVANT])
model.Add(amalgamy > 0).OnlyEnforceIf(actions[Buyer.A_TENTACLED_SERVANT])
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, '{}: {}'.format(Buyer.A_TENTACLED_SERVANT.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 50)
model.AddModuloEquality(value_remainder, value, 50)
model.Add(primary_revenue == value - value_remainder + 250).OnlyEnforceIf(actions[Buyer.A_TENTACLED_SERVANT])
model.Add(secondary_revenue == 250*amalgamy + (250 if BONE_MARKET_FLUCTUATIONS == Fluctuation.AMALGAMY else 0)).OnlyEnforceIf(actions[Buyer.A_TENTACLED_SERVANT])
# Revenue
model.Add(primary_revenue == value - value_remainder + 250)
model.Add(secondary_revenue == 250*antiquity_times_menace + 250*(menace if BONE_MARKET_FLUCTUATIONS == Fluctuation.ANTIQUITY else 0))
model.Add(difficulty_level == 45*implausibility).OnlyEnforceIf(actions[Buyer.A_TENTACLED_SERVANT])
# Difficulty Level
model.Add(difficulty_level == 75*implausibility)
model.Add(added_exhaustion == 0).OnlyEnforceIf(actions[Buyer.A_TENTACLED_SERVANT])
# Added Exhaustion
model.AddDivisionEquality(added_exhaustion, antiquity_times_menace, 20)
elif BUYER == Buyer.A_ZAILOR_WITH_PARTICULAR_INTERESTS:
model.Add(skeleton_in_progress >= 100)
model.Add(antiquity > 0)
model.Add(amalgamy > 0)
del value_remainder
amalgamy_times_antiquity = model.NewIntVar(0, cp_model.INT32_MAX, 'amalgamy times antiquity')
model.AddMultiplicationEquality(amalgamy_times_antiquity, [amalgamy, antiquity])
model.AddModuloEquality(value_remainder, value, 10)
# An Investment-Minded Ambassador
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.AN_INVESTMENT_MINDED_AMBASSADOR])
model.Add(antiquity > 0).OnlyEnforceIf(actions[Buyer.AN_INVESTMENT_MINDED_AMBASSADOR])
# Revenue
model.Add(primary_revenue == value - value_remainder + 250)
model.Add(secondary_revenue == 250*amalgamy_times_antiquity + 250*(amalgamy if BONE_MARKET_FLUCTUATIONS == Fluctuation.ANTIQUITY else antiquity if BONE_MARKET_FLUCTUATIONS == Fluctuation.AMALGAMY else 0))
antiquity_squared = model.NewIntVar(0, cp_model.INT32_MAX, '{}: {}'.format(Buyer.AN_INVESTMENT_MINDED_AMBASSADOR.name, 'antiquity squared'))
model.AddMultiplicationEquality(antiquity_squared, [antiquity, antiquity])
# Difficulty Level
model.Add(difficulty_level == 75*implausibility)
tailfeathers = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, '{}: {}'.format(Buyer.AN_INVESTMENT_MINDED_AMBASSADOR.name, 'tailfeathers'))
if BONE_MARKET_FLUCTUATIONS == Fluctuation.ANTIQUITY:
model.AddApproximateExponentiationEquality(tailfeathers, antiquity, 2.2, MAXIMUM_ATTRIBUTE)
else:
model.Add(tailfeathers == antiquity_squared).OnlyEnforceIf(actions[Buyer.AN_INVESTMENT_MINDED_AMBASSADOR])
# Added Exhaustion
model.AddDivisionEquality(added_exhaustion, amalgamy_times_antiquity, 20)
elif BUYER == Buyer.A_RUBBERY_COLLECTOR:
model.Add(skeleton_in_progress >= 100)
model.Add(amalgamy > 0)
model.Add(menace > 0)
value_remainder = model.NewIntVar(0, 49, '{}: {}'.format(Buyer.AN_INVESTMENT_MINDED_AMBASSADOR.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 50)
extra_value = model.NewIntermediateBoolVar('{}: {}'.format(Buyer.AN_INVESTMENT_MINDED_AMBASSADOR.name, 'extra value'), value_remainder, cp_model.Domain.FromFlatIntervals([0, cp_model.INT_MAX]))
amalgamy_times_menace = model.NewIntVar(0, cp_model.INT32_MAX, 'amalgamy times menace')
model.AddMultiplicationEquality(amalgamy_times_menace, [amalgamy, menace])
model.Add(primary_revenue == value + 50*extra_value + 250).OnlyEnforceIf(actions[Buyer.AN_INVESTMENT_MINDED_AMBASSADOR])
model.Add(secondary_revenue == 250*tailfeathers).OnlyEnforceIf(actions[Buyer.AN_INVESTMENT_MINDED_AMBASSADOR])
model.AddModuloEquality(value_remainder, value, 50)
model.Add(difficulty_level == 75*implausibility).OnlyEnforceIf(actions[Buyer.AN_INVESTMENT_MINDED_AMBASSADOR])
# Revenue
model.Add(primary_revenue == value - value_remainder + 250)
model.Add(secondary_revenue == 250*amalgamy_times_menace + 250*(menace if BONE_MARKET_FLUCTUATIONS == Fluctuation.AMALGAMY else 0))
# The indirection is necessary for applying an enforcement literal
derived_exhaustion = model.NewIntVar(0, cp_model.INT32_MAX, '{}: {}'.format(Buyer.AN_INVESTMENT_MINDED_AMBASSADOR.name, 'derived exhaustion'))
model.AddDivisionEquality(derived_exhaustion, antiquity_squared, 20)
model.Add(added_exhaustion == derived_exhaustion).OnlyEnforceIf(actions[Buyer.AN_INVESTMENT_MINDED_AMBASSADOR])
# Difficulty Level
model.Add(difficulty_level == 75*implausibility)
del antiquity_squared, tailfeathers, value_remainder, extra_value, derived_exhaustion
# Added Exhaustion
model.AddDivisionEquality(added_exhaustion, amalgamy_times_menace, 20)
elif BUYER == Buyer.A_CONSTABLE:
model.AddLinearExpressionInDomain(skeleton_in_progress, cp_model.Domain.FromFlatIntervals([110, 119]))
model.AddModuloEquality(value_remainder, value, 50)
# A Teller of Terrors
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.A_TELLER_OF_TERRORS])
model.Add(menace > 0).OnlyEnforceIf(actions[Buyer.A_TELLER_OF_TERRORS])
# Revenue
model.Add(primary_revenue == value - value_remainder + 1000)
model.Add(secondary_revenue == 0)
menace_squared = model.NewIntVar(0, cp_model.INT32_MAX, '{}: {}'.format(Buyer.A_TELLER_OF_TERRORS.name, 'menace squared'))
model.AddMultiplicationEquality(menace_squared, [menace, menace])
# Difficulty Level
model.Add(difficulty_level == 50*implausibility)
value_remainder = model.NewIntVar(0, 9, '{}: {}'.format(Buyer.A_TELLER_OF_TERRORS.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 10)
# Added Exhaustion
model.Add(added_exhaustion == 0)
elif BUYER == Buyer.AN_ENTHUSIAST_IN_SKULLS:
model.Add(skeleton_in_progress >= 100)
model.Add(skulls >= 2)
model.Add(primary_revenue == value - value_remainder + 50).OnlyEnforceIf(actions[Buyer.A_TELLER_OF_TERRORS])
model.Add(secondary_revenue == 50*menace_squared).OnlyEnforceIf(actions[Buyer.A_TELLER_OF_TERRORS])
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)
model.Add(difficulty_level == 75*implausibility).OnlyEnforceIf(actions[Buyer.A_TELLER_OF_TERRORS])
# Revenue
model.Add(primary_revenue == value)
model.Add(secondary_revenue == 1250*vital_intelligence)
# The indirection is necessary for applying an enforcement literal
derived_exhaustion = model.NewIntVar(0, cp_model.INT32_MAX, '{}: {}'.format(Buyer.A_TELLER_OF_TERRORS.name, 'derived exhaustion'))
model.AddDivisionEquality(derived_exhaustion, menace_squared, 100)
model.Add(added_exhaustion == derived_exhaustion).OnlyEnforceIf(actions[Buyer.A_TELLER_OF_TERRORS])
# Difficulty Level
model.Add(difficulty_level == 60*implausibility)
del menace_squared, value_remainder, derived_exhaustion
# Added Exhaustion
model.AddDivisionEquality(added_exhaustion, vital_intelligence, 4)
elif 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)
model.AddModuloEquality(value_remainder, value, 3)
# A Tentacled Entrepreneur
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.A_TENTACLED_ENTREPRENEUR])
model.Add(amalgamy > 0).OnlyEnforceIf(actions[Buyer.A_TENTACLED_ENTREPRENEUR])
# Revenue
model.Add(primary_revenue == value - value_remainder + 300)
model.Add(secondary_revenue == 250)
amalgamy_squared = model.NewIntVar(0, cp_model.INT32_MAX, '{}: {}'.format(Buyer.A_TENTACLED_ENTREPRENEUR.name, 'amalgamy squared'))
model.AddMultiplicationEquality(amalgamy_squared, [amalgamy, amalgamy])
# Difficulty Level
model.Add(difficulty_level == 100*implausibility)
final_breaths = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, '{}: {}'.format(Buyer.A_TENTACLED_ENTREPRENEUR.name, 'final breaths'))
if BONE_MARKET_FLUCTUATIONS == Fluctuation.AMALGAMY:
model.AddApproximateExponentiationEquality(final_breaths, amalgamy, 2.2, MAXIMUM_ATTRIBUTE)
else:
model.Add(final_breaths == amalgamy_squared).OnlyEnforceIf(actions[Buyer.A_TENTACLED_ENTREPRENEUR])
# Added Exhaustion
model.Add(added_exhaustion == 0)
elif 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, 49, '{}: {}'.format(Buyer.A_TENTACLED_ENTREPRENEUR.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 50)
model.AddModuloEquality(value_remainder, value, 250)
model.Add(primary_revenue == value - value_remainder + 250).OnlyEnforceIf(actions[Buyer.A_TENTACLED_ENTREPRENEUR])
model.Add(secondary_revenue == 50*final_breaths).OnlyEnforceIf(actions[Buyer.A_TENTACLED_ENTREPRENEUR])
# Revenue
model.Add(primary_revenue == value - value_remainder)
model.Add(secondary_revenue == 0)
model.Add(difficulty_level == 75*implausibility).OnlyEnforceIf(actions[Buyer.A_TENTACLED_ENTREPRENEUR])
# Difficulty Level
model.Add(difficulty_level == 200)
# The indirection is necessary for applying an enforcement literal
derived_exhaustion = model.NewIntVar(0, cp_model.INT32_MAX, '{}: {}'.format(Buyer.A_TENTACLED_ENTREPRENEUR.name, 'derived exhaustion'))
model.AddDivisionEquality(derived_exhaustion, amalgamy_squared, 100)
model.Add(added_exhaustion == derived_exhaustion).OnlyEnforceIf(actions[Buyer.A_TENTACLED_ENTREPRENEUR])
# Added Exhaustion
model.Add(added_exhaustion == 0)
del amalgamy_squared, final_breaths, value_remainder, derived_exhaustion
# An Author of Gothic Tales
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.AN_AUTHOR_OF_GOTHIC_TALES])
model.Add(antiquity > 0).OnlyEnforceIf(actions[Buyer.AN_AUTHOR_OF_GOTHIC_TALES])
model.Add(menace > 0).OnlyEnforceIf(actions[Buyer.AN_AUTHOR_OF_GOTHIC_TALES])
antiquity_times_menace = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, '{}: {}'.format(Buyer.AN_AUTHOR_OF_GOTHIC_TALES.name, 'antiquity times menace'))
model.AddMultiplicationEquality(antiquity_times_menace, [antiquity, menace])
value_remainder = model.NewIntVar(0, 49, '{}: {}'.format(Buyer.AN_AUTHOR_OF_GOTHIC_TALES.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 50)
model.Add(primary_revenue == value - value_remainder + 250).OnlyEnforceIf(actions[Buyer.AN_AUTHOR_OF_GOTHIC_TALES])
model.Add(secondary_revenue == 250*antiquity_times_menace + 250*(menace if BONE_MARKET_FLUCTUATIONS == Fluctuation.ANTIQUITY else 0)).OnlyEnforceIf(actions[Buyer.AN_AUTHOR_OF_GOTHIC_TALES])
model.Add(difficulty_level == 75*implausibility).OnlyEnforceIf(actions[Buyer.AN_AUTHOR_OF_GOTHIC_TALES])
# The indirection is necessary for applying an enforcement literal
derived_exhaustion = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, '{}: {}'.format(Buyer.AN_AUTHOR_OF_GOTHIC_TALES.name, 'derived exhaustion'))
model.AddDivisionEquality(derived_exhaustion, antiquity_times_menace, 20)
model.Add(added_exhaustion == derived_exhaustion).OnlyEnforceIf(actions[Buyer.AN_AUTHOR_OF_GOTHIC_TALES])
del antiquity_times_menace, value_remainder, derived_exhaustion
# A Zailor with Particular Interests
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.A_ZAILOR_WITH_PARTICULAR_INTERESTS])
model.Add(antiquity > 0).OnlyEnforceIf(actions[Buyer.A_ZAILOR_WITH_PARTICULAR_INTERESTS])
model.Add(amalgamy > 0).OnlyEnforceIf(actions[Buyer.A_ZAILOR_WITH_PARTICULAR_INTERESTS])
amalgamy_times_antiquity = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, '{}: {}'.format(Buyer.A_ZAILOR_WITH_PARTICULAR_INTERESTS.name, 'amalgamy times antiquity'))
model.AddMultiplicationEquality(amalgamy_times_antiquity, [amalgamy, antiquity])
value_remainder = model.NewIntVar(0, 9, '{}: {}'.format(Buyer.A_ZAILOR_WITH_PARTICULAR_INTERESTS.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 10)
model.Add(primary_revenue == value - value_remainder + 250).OnlyEnforceIf(actions[Buyer.A_ZAILOR_WITH_PARTICULAR_INTERESTS])
model.Add(secondary_revenue == 250*amalgamy_times_antiquity + 250*(amalgamy if BONE_MARKET_FLUCTUATIONS == Fluctuation.ANTIQUITY else antiquity if BONE_MARKET_FLUCTUATIONS == Fluctuation.AMALGAMY else 0)).OnlyEnforceIf(actions[Buyer.A_ZAILOR_WITH_PARTICULAR_INTERESTS])
model.Add(difficulty_level == 75*implausibility).OnlyEnforceIf(actions[Buyer.A_ZAILOR_WITH_PARTICULAR_INTERESTS])
# The indirection is necessary for applying an enforcement literal
derived_exhaustion = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, '{}: {}'.format(Buyer.A_ZAILOR_WITH_PARTICULAR_INTERESTS.name, 'derived exhaustion'))
model.AddDivisionEquality(derived_exhaustion, amalgamy_times_antiquity, 20)
model.Add(added_exhaustion == derived_exhaustion).OnlyEnforceIf(actions[Buyer.A_ZAILOR_WITH_PARTICULAR_INTERESTS])
del amalgamy_times_antiquity, value_remainder, derived_exhaustion
# A Rubbery Collector
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.A_RUBBERY_COLLECTOR])
model.Add(amalgamy > 0).OnlyEnforceIf(actions[Buyer.A_RUBBERY_COLLECTOR])
model.Add(menace > 0).OnlyEnforceIf(actions[Buyer.A_RUBBERY_COLLECTOR])
amalgamy_times_menace = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, '{}: {}'.format(Buyer.A_RUBBERY_COLLECTOR.name, 'amalgamy times menace'))
model.AddMultiplicationEquality(amalgamy_times_menace, [amalgamy, menace])
value_remainder = model.NewIntVar(0, 49, '{}: {}'.format(Buyer.A_RUBBERY_COLLECTOR.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 50)
model.Add(primary_revenue == value - value_remainder + 250).OnlyEnforceIf(actions[Buyer.A_RUBBERY_COLLECTOR])
model.Add(secondary_revenue == 250*amalgamy_times_menace + 250*(menace if BONE_MARKET_FLUCTUATIONS == Fluctuation.AMALGAMY else 0)).OnlyEnforceIf(actions[Buyer.A_RUBBERY_COLLECTOR])
model.Add(difficulty_level == 75*implausibility).OnlyEnforceIf(actions[Buyer.A_RUBBERY_COLLECTOR])
# The indirection is necessary for applying an enforcement literal
derived_exhaustion = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, '{}: {}'.format(Buyer.A_RUBBERY_COLLECTOR.name, 'derived exhaustion'))
model.AddDivisionEquality(derived_exhaustion, amalgamy_times_menace, 20)
model.Add(added_exhaustion == derived_exhaustion).OnlyEnforceIf(actions[Buyer.A_RUBBERY_COLLECTOR])
del amalgamy_times_menace, value_remainder, derived_exhaustion
# A Constable
model.AddLinearExpressionInDomain(skeleton_in_progress, cp_model.Domain.FromFlatIntervals([110, 119])).OnlyEnforceIf(actions[Buyer.A_CONSTABLE])
value_remainder = model.NewIntVar(0, 49, '{}: {}'.format(Buyer.A_CONSTABLE.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 50)
model.Add(primary_revenue == value - value_remainder + 1000).OnlyEnforceIf(actions[Buyer.A_CONSTABLE])
model.Add(secondary_revenue == 0).OnlyEnforceIf(actions[Buyer.A_CONSTABLE])
model.Add(difficulty_level == 50*implausibility).OnlyEnforceIf(actions[Buyer.A_CONSTABLE])
model.Add(added_exhaustion == 0).OnlyEnforceIf(actions[Buyer.A_CONSTABLE])
del value_remainder
# An Enthusiast in Skulls
model.Add(skeleton_in_progress >= 100).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_IN_SKULLS])
model.Add(skulls >= 2).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_IN_SKULLS])
extra_skulls = model.NewIntVar(0, cp_model.INT32_MAX, '{}: {}'.format(Buyer.AN_ENTHUSIAST_IN_SKULLS.name, 'extra skulls'))
model.Add(extra_skulls == skulls - 1).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_IN_SKULLS])
vital_intelligence = model.NewIntVar(cp_model.INT32_MIN, cp_model.INT32_MAX, '{}: {}'.format(Buyer.AN_ENTHUSIAST_IN_SKULLS.name, 'vital intelligence'))
model.AddApproximateExponentiationEquality(vital_intelligence, extra_skulls, 1.8, MAXIMUM_ATTRIBUTE)
model.Add(primary_revenue == value).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_IN_SKULLS])
model.Add(secondary_revenue == 1250*vital_intelligence).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_IN_SKULLS])
model.Add(difficulty_level == 60*implausibility).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_IN_SKULLS])
# The indirection is necessary for applying an enforcement literal
derived_exhaustion = model.NewIntVar(0, cp_model.INT32_MAX, '{}: {}'.format(Buyer.AN_ENTHUSIAST_IN_SKULLS.name, 'derived exhaustion'))
model.AddDivisionEquality(derived_exhaustion, vital_intelligence, 4)
model.Add(added_exhaustion == derived_exhaustion).OnlyEnforceIf(actions[Buyer.AN_ENTHUSIAST_IN_SKULLS])
del extra_skulls, vital_intelligence, derived_exhaustion
# A Dreary Midnighter
model.AddLinearExpressionInDomain(skeleton_in_progress, cp_model.Domain.FromFlatIntervals([110, 299])).OnlyEnforceIf(actions[Buyer.A_DREARY_MIDNIGHTER])
model.Add(amalgamy <= 0).OnlyEnforceIf(actions[Buyer.A_DREARY_MIDNIGHTER])
model.Add(counter_church <= 0).OnlyEnforceIf(actions[Buyer.A_DREARY_MIDNIGHTER])
value_remainder = model.NewIntVar(0, 2, '{}: {}'.format(Buyer.A_DREARY_MIDNIGHTER.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 3)
model.Add(primary_revenue == value - value_remainder + 300).OnlyEnforceIf(actions[Buyer.A_DREARY_MIDNIGHTER])
model.Add(secondary_revenue == 250).OnlyEnforceIf(actions[Buyer.A_DREARY_MIDNIGHTER])
model.Add(difficulty_level == 100*implausibility).OnlyEnforceIf(actions[Buyer.A_DREARY_MIDNIGHTER])
model.Add(added_exhaustion == 0).OnlyEnforceIf(actions[Buyer.A_DREARY_MIDNIGHTER])
del value_remainder
# The Dumbwaiter of Balmoral
model.AddLinearExpressionInDomain(skeleton_in_progress, cp_model.Domain.FromFlatIntervals([180, 189])).OnlyEnforceIf(actions[Buyer.THE_DUMBWAITER_OF_BALMORAL])
model.Add(value >= 250).OnlyEnforceIf(actions[Buyer.THE_DUMBWAITER_OF_BALMORAL])
value_remainder = model.NewIntVar(0, 249, '{}: {}'.format(Buyer.THE_DUMBWAITER_OF_BALMORAL.name, 'value remainder'))
model.AddModuloEquality(value_remainder, value, 250)
model.Add(primary_revenue == value - value_remainder).OnlyEnforceIf(actions[Buyer.THE_DUMBWAITER_OF_BALMORAL])
model.Add(secondary_revenue == 0).OnlyEnforceIf(actions[Buyer.THE_DUMBWAITER_OF_BALMORAL])
model.Add(difficulty_level == 200).OnlyEnforceIf(actions[Buyer.THE_DUMBWAITER_OF_BALMORAL])
model.Add(added_exhaustion == 0).OnlyEnforceIf(actions[Buyer.THE_DUMBWAITER_OF_BALMORAL])
del value_remainder
# Maximize profit margin