0

I am writing a staff scheduler for my office. There are at least two shifts per day (7 days a week) and I want the optimizer to make sure no one staff member works drastically more weekend shifts than another.

I have a simple working program that assigns one Staff to each Shift. The program structure is as follows:

  • SchedulingSolution is the @PlanningSolution
  • SchedulingSolution contains a List<Shift> which is the @PlanningEntityCollectionProperty
  • Shift is the @PlanningEntity
  • Shift contains a Staff which is the @PlanningVariable
  • SchedulingSolution contains a @ValueRangeProvider which returns our staff roster as a List<Staff>.

This working solution schedules all staff equally but does not consider weekends and weekdays. To delineate weekdays from weekends, I have replaced the List<Shift> in SchedulingSolution with a List<Day>. Every Day contains its own List<Shift> representing the shifts that occur on that day. Now when I want to compute the number of weekends a staff member has worked, I can find all Day objects that represent weekends and count only the Shifts contained in those days.

Unfortunately, this places the List<Shift> which is the @PlanningEntityCollectionProperty in a class that is not a @PlanningSolution. The @PlanningEntityCollectionProperty annotation is now ignored and the program fails.

Have I missed an obvious way to restructure my program, or is my only option to keep my original program structure and modify my shift objects to record which day they occur on?

Thanks in advance. I'll try to pass the help forward if I can.

Yeti
  • 1,108
  • 19
  • 28
Anthony
  • 341
  • 2
  • 11

1 Answers1

2

I would restructure your Planning Solution.

Why don't you do:

  • Keep the List<Shift> in the Planning Solution
  • Discard the List<Day> in every shift
  • Add a field day in Shift, possibly even an indicator whether that day is a weekend using an enum.

You can then easily calculate the number of weekend days worked by each staff with Drools as so:

(Absolutely untested!!!)

rule "balancedStaffWeekend"
    when
        $staff: Staff()
        accumulate(
            Shift(
                staff == $staff
                day == Day.WEEKEND);
                $count: count()
            );
        )
    then
        scoreHolder.addSoftConstraintMatch(kcontext, -Math.pow(count, 2));
end

Penalising the solution by counts raised to the power of two balances the number of weekends.

Philip Harding
  • 392
  • 1
  • 10
  • Thanks! Looking back at the examples, it appears that it is necessary to have the PlanningSolution keep track of the PlanningEntityCollectionProperty directly with no intermediate classes. I have done just as you suggested and each Shift now references a Workday which has a Date and a DayType from an Enum. I'm still learning Drools but I mocked up an EasyScoreCalculator that works just fine. Thank you for helping me learn this new and valuable skill! – Anthony Jun 07 '18 at 13:01