use crate::domain::{Plan, PlanConstraintStreams, Shift}; use solverforge::prelude::*; use solverforge::IncrementalConstraint; const SCORE_SCALE: i64 = 100_000; const STRUCTURAL_MINUTE_HARD_UNITS: i64 = 20; /// Penalizes overlapping time windows for the same employee. pub fn constraint() -> impl IncrementalConstraint { ConstraintFactory::::new() .shifts() .filter(|shift: &Shift| shift.employee_idx.is_some()) .join(joiner::equal(|shift: &Shift| shift.employee_idx)) .filter(|a: &Shift, b: &Shift| a.index < b.index && a.start < b.end && b.start < a.end) .penalize(hard_weight(|a: &Shift, b: &Shift| { let overlap_start = a.start.max(b.start); let overlap_end = a.end.min(b.end); let overlap_minutes = if overlap_start < overlap_end { (overlap_end - overlap_start).num_minutes() } else { 0 }; HardSoftDecimalScore::of_hard_scaled( overlap_minutes * STRUCTURAL_MINUTE_HARD_UNITS * SCORE_SCALE, ) })) .named("Overlapping shift") }