diff --git a/bonemarketsolver/data/appendages.py b/bonemarketsolver/data/appendages.py index 53c896c..9d9313e 100644 --- a/bonemarketsolver/data/appendages.py +++ b/bonemarketsolver/data/appendages.py @@ -19,6 +19,17 @@ class Appendage(Enum): amalgamy = 2 ) + # Cost from this scales with segments and is partially implemented separately + # Requires Torso Style 110 + # Requires initial tail slot + SEGMENTED_RIBCAGE = Action( + "Extend the tail end with another Segmented Ribcage", + cost = Cost.ACTION.value + Cost.SEGMENTED_RIBCAGE.value, + value = 250, + limbs_needed = 2, + segments = 1 + ) + ALBATROSS_WING = Action( "Put an Albatross Wing on your (Skeleton Type)", cost = Cost.ACTION.value + Cost.ALBATROSS_WING.value, diff --git a/bonemarketsolver/solve.py b/bonemarketsolver/solve.py index c27e203..514f93e 100644 --- a/bonemarketsolver/solve.py +++ b/bonemarketsolver/solve.py @@ -4,6 +4,7 @@ __all__ = ['Adjustment', 'Appendage', 'Buyer', 'Declaration', 'DiplomatFascinati __author__ = "Jeremy Saklad" from functools import partialmethod +from itertools import chain, repeat from os import cpu_count from ortools.sat.python import cp_model @@ -328,10 +329,139 @@ def Solve(shadowy_level, bone_market_fluctuations = None, zoological_mania = Non del add_joints, add_joints_amber_cost_multiple - cost = model.NewIntVar('cost', lb = 0, ub = maximum_cost) - model.Add(cost == cp_model.LinearExpr.WeightedSum(actions.values(), [int(action.value.cost) for action in actions.keys()]) + add_joints_amber_cost + sale_cost) + # Calculate cost of adding segments. + # This is a partial sum formula. + add_segments_brass_cost = model.NewIntVar('add segments brass cost', lb = 0) - del sale_cost, add_joints_amber_cost + add_segments = actions[Appendage.SEGMENTED_RIBCAGE] + + # Additional segments may be added once the torso and skulls are chosen, so the sum of their properties are the starting point. + base_segments = model.NewIntVar('base segments', lb = 0) + model.Add(base_segments == cp_model.LinearExpr.WeightedSum([value for (key, value) in actions.items() if isinstance(key, (Torso, Skull))], [action.value.segments for action in chain(Torso, Skull)])) + + first_term, *_ = model.NewIntermediateIntVar( + partialmethod(BoneMarketModel.AddMultiplicationEquality, + variables=( + 25, + *repeat(add_segments, 4), + ) + ), + 'add segments brass cost multiple first term' + ) + + second_term, *_ = model.NewIntermediateIntVar( + partialmethod(BoneMarketModel.AddMultiplicationEquality, + variables=( + 100, + *repeat(add_segments, 3), + base_segments, + ) + ), + 'add segments brass cost multiple second term' + ) + + third_term, *_ = model.NewIntermediateIntVar( + partialmethod(BoneMarketModel.AddMultiplicationEquality, + variables=( + 50, + *repeat(add_segments, 3), + ) + ), + 'add segments brass cost multiple third term' + ) + + fourth_term, *_ = model.NewIntermediateIntVar( + partialmethod(BoneMarketModel.AddMultiplicationEquality, + variables=( + 150, + *repeat(add_segments, 2), + *repeat(base_segments, 2), + ) + ), + 'add segments brass cost multiple fourth term' + ) + + fifth_term, *_ = model.NewIntermediateIntVar( + partialmethod(BoneMarketModel.AddMultiplicationEquality, + variables=( + 150, + *repeat(add_segments, 2), + base_segments, + ) + ), + 'add segments brass cost multiple fifth term' + ) + + sixth_term, *_ = model.NewIntermediateIntVar( + partialmethod(BoneMarketModel.AddMultiplicationEquality, + variables=( + 25, + *repeat(add_segments, 2), + ) + ), + 'add segments brass cost multiple sixth term' + ) + + seventh_term, *_ = model.NewIntermediateIntVar( + partialmethod(BoneMarketModel.AddMultiplicationEquality, + variables=( + 100, + add_segments, + *repeat(base_segments, 3), + ) + ), + 'add segments brass cost multiple seventh term' + ) + + eighth_term, *_ = model.NewIntermediateIntVar( + partialmethod(BoneMarketModel.AddMultiplicationEquality, + variables=( + 150, + add_segments, + *repeat(base_segments, 2), + ) + ), + 'add segments brass cost multiple eighth term' + ) + + ninth_term, *_ = model.NewIntermediateIntVar( + partialmethod(BoneMarketModel.AddMultiplicationEquality, + variables=( + 50, + add_segments, + base_segments, + ) + ), + 'add segments brass cost multiple ninth term' + ) + + add_segments_brass_cost_multiple, *_ = model.NewIntermediateIntVar( + partialmethod(BoneMarketModel.AddDivisionEquality, + num=first_term + second_term + third_term + fourth_term + fifth_term + sixth_term + seventh_term + eighth_term + ninth_term, + denom=2 + ), + 'add segments brass cost multiple' + ) + + del first_term, second_term, third_term, fourth_term, fifth_term, sixth_term, seventh_term, eighth_term, ninth_term + + add_segments_brass_cost, *_ = model.NewIntermediateIntVar( + partialmethod(BoneMarketModel.AddMultiplicationEquality, + variables=( + add_segments_brass_cost_multiple, + Cost.NEVERCOLD_BRASS.value, + ) + ), + 'add segments brass cost' + ) + + del add_segments, base_segments, add_segments_brass_cost_multiple + + + cost = model.NewIntVar('cost', lb = 0, ub = maximum_cost) + model.Add(cost == cp_model.LinearExpr.WeightedSum(actions.values(), [int(action.value.cost) for action in actions.keys()]) + add_joints_amber_cost + add_segments_brass_cost + sale_cost) + + del sale_cost, add_joints_amber_cost, add_segments_brass_cost # Type of skeleton @@ -451,6 +581,14 @@ def Solve(shadowy_level, bone_market_fluctuations = None, zoological_mania = Non .OnlyEnforceIf(model.BoolExpression(actions[Skull.SEGMENTED_RIBCAGE] > 0)) + # Appendage requirements + + model.AddIf(model.BoolExpression(actions[Appendage.SEGMENTED_RIBCAGE] > 0), + torso_style == 110, + cp_model.LinearExpr.WeightedSum([value for (key, value) in actions.items() if isinstance(key, (Torso, Skull))], [action.value.tails_needed for action in chain(Torso, Skull)]) >= 1, + ) + + # Declaration requirements model.AddIf(actions[Declaration.HUMANOID],