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.
"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);
}