Search code examples
optaplanner

Optaplanner 8 - School timetabling constraintFactory avoid earlier empty timeslots in the same day


I'm learning about Optaplanner and have had great success creating new constraints if a lesson is involved. However, I'm struggling to work out how to create a constraintFactory if there is no lesson assigned to a timeslot.

I would fill lessons from the morning and have not empty slots if possible with .reward("no empty earlier time slots", HardSoftScore.ONE_SOFT). Something like:

import org.acme.kotlin.schooltimetabling.domain.Timeslot

...

    fun noEmptyEarlierTimeslots(constraintFactory: ConstraintFactory): Constraint {
        // try to fill the slots from the start and have not empty slots later in the day on the same day
        return constraintFactory
                .from(Timeslot::class.java)
                .join(Timeslot::class.java)
                .filter { slot1: Timeslot, slot2: Timeslot -> slot1.startTime.toLocalDate() === slot2.startTime.toLocalDate() }
                    slot1.startTime < slo2.startTime && ifNotExists(slot1::Lesson) && ifExists(slot2::Lesson)
                .reward("no empty earlier time slots", HardSoftScore.ONE_SOFT)
    }

[ I changed the startTime and endTime to localDateTime - hence the toLocalDate() ]

Any help will be greatly appreciated.


Solution

  • "if there is no lesson assigned to a timeslot."

    I think .ifNotExists() might work for that. Basically something like from(Timeslot.class).ifNotExists(Lesson.class, equal(identity(), Lesson::getTimeslot)...

    "I would fill lessons from the morning and have not empty slots if possible"

    Take a look at this constraint in the example:

    Constraint teacherTimeEfficiency(ConstraintFactory constraintFactory) {
        // A teacher prefers to teach sequential lessons and dislikes gaps between lessons.
        return constraintFactory
                .from(Lesson.class)
                .join(Lesson.class, Joiners.equal(Lesson::getTeacher),
                        Joiners.equal((lesson) -> lesson.getTimeslot().getDayOfWeek()))
                .filter((lesson1, lesson2) -> {
                    Duration between = Duration.between(lesson1.getTimeslot().getEndTime(),
                            lesson2.getTimeslot().getStartTime());
                    return !between.isNegative() && between.compareTo(Duration.ofMinutes(30)) <= 0;
                })
                .reward("Teacher time efficiency", HardSoftScore.ONE_SOFT);
    }