blackopsrepl's picture
Upload 31 files
e40294e verified
from solverforge_legacy.solver.score import (
constraint_provider,
ConstraintFactory,
HardSoftDecimalScore,
)
from .domain import Trolley
REQUIRED_NUMBER_OF_BUCKETS = "Required number of buckets"
MINIMIZE_ORDER_SPLIT = "Minimize order split by trolley"
MINIMIZE_DISTANCE = "Minimize total distance"
@constraint_provider
def define_constraints(factory: ConstraintFactory):
return [
# Hard constraints
required_number_of_buckets(factory),
# Soft constraints
minimize_order_split_by_trolley(factory),
minimize_total_distance(factory),
]
def required_number_of_buckets(factory: ConstraintFactory):
"""
Hard: Ensure trolley has enough buckets for all orders.
"""
return (
factory.for_each(Trolley)
.filter(lambda trolley: trolley.calculate_excess_buckets() > 0)
.penalize(
HardSoftDecimalScore.ONE_HARD,
lambda trolley: trolley.calculate_excess_buckets()
)
.as_constraint(REQUIRED_NUMBER_OF_BUCKETS)
)
def minimize_order_split_by_trolley(factory: ConstraintFactory):
"""
Soft: Orders should ideally be on the same trolley.
"""
return (
factory.for_each(Trolley)
.filter(lambda trolley: len(trolley.steps) > 0)
.penalize(
HardSoftDecimalScore.ONE_SOFT,
lambda trolley: trolley.calculate_order_split_penalty()
)
.as_constraint(MINIMIZE_ORDER_SPLIT)
)
def minimize_total_distance(factory: ConstraintFactory):
"""
Soft: Minimize total distance traveled by all trolleys.
Aggregated at Trolley level (like vehicle-routing) for performance.
"""
return (
factory.for_each(Trolley)
.penalize(
HardSoftDecimalScore.ONE_SOFT,
lambda trolley: trolley.calculate_total_distance()
)
.as_constraint(MINIMIZE_DISTANCE)
)