format with baeldung style

This commit is contained in:
Geoffrey De Smet 2023-11-24 21:40:29 +01:00
parent 3df51484f2
commit 07a6999e56
4 changed files with 48 additions and 62 deletions

View File

@ -16,7 +16,8 @@ public class Shift {
private Employee employee; private Employee employee;
// A no-arg constructor is required for @PlanningEntity annotated classes // A no-arg constructor is required for @PlanningEntity annotated classes
public Shift() {} public Shift() {
}
public Shift(LocalDateTime start, LocalDateTime end, String requiredSkill) { public Shift(LocalDateTime start, LocalDateTime end, String requiredSkill) {
this(start, end, requiredSkill, null); this(start, end, requiredSkill, null);

View File

@ -5,24 +5,24 @@ import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ai.timefold.solver.core.api.solver.Solver; import ai.timefold.solver.core.api.solver.Solver;
import ai.timefold.solver.core.api.solver.SolverFactory; import ai.timefold.solver.core.api.solver.SolverFactory;
import ai.timefold.solver.core.config.solver.SolverConfig; import ai.timefold.solver.core.config.solver.SolverConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ShiftScheduleApp { public class ShiftScheduleApp {
private static final Logger logger = LoggerFactory.getLogger(ShiftScheduleApp.class); private static final Logger logger = LoggerFactory.getLogger(ShiftScheduleApp.class);
public static void main(String[] args) { public static void main(String[] args) {
SolverFactory<ShiftSchedule> solverFactory = SolverFactory.create(new SolverConfig() SolverFactory<ShiftSchedule> solverFactory = SolverFactory.create(new SolverConfig().withSolutionClass(ShiftSchedule.class)
.withSolutionClass(ShiftSchedule.class) .withEntityClasses(Shift.class)
.withEntityClasses(Shift.class) .withConstraintProviderClass(ShiftScheduleConstraintProvider.class)
.withConstraintProviderClass(ShiftScheduleConstraintProvider.class) // The solver runs only for 5 seconds on this small dataset.
// The solver runs only for 5 seconds on this small dataset. // It's recommended to run for at least 5 minutes ("5m") otherwise.
// It's recommended to run for at least 5 minutes ("5m") otherwise. .withTerminationSpentLimit(Duration.ofSeconds(5)));
.withTerminationSpentLimit(Duration.ofSeconds(5)));
Solver<ShiftSchedule> solver = solverFactory.buildSolver(); Solver<ShiftSchedule> solver = solverFactory.buildSolver();
ShiftSchedule problem = loadProblem(); ShiftSchedule problem = loadProblem();
@ -33,25 +33,21 @@ public class ShiftScheduleApp {
private static ShiftSchedule loadProblem() { private static ShiftSchedule loadProblem() {
LocalDate monday = LocalDate.of(2030, 4, 1); LocalDate monday = LocalDate.of(2030, 4, 1);
LocalDate tuesday = LocalDate.of(2030, 4, 2); LocalDate tuesday = LocalDate.of(2030, 4, 2);
return new ShiftSchedule(List.of( return new ShiftSchedule(
new Employee("Ann", Set.of("Bartender")), List.of(new Employee("Ann", Set.of("Bartender")), new Employee("Beth", Set.of("Waiter", "Bartender")), new Employee("Carl", Set.of("Waiter"))),
new Employee("Beth", Set.of("Waiter", "Bartender")), List.of(new Shift(monday.atTime(6, 0), monday.atTime(14, 0), "Waiter"), new Shift(monday.atTime(9, 0), monday.atTime(17, 0), "Bartender"),
new Employee("Carl", Set.of("Waiter")) new Shift(monday.atTime(14, 0), monday.atTime(22, 0), "Bartender"), new Shift(tuesday.atTime(6, 0), tuesday.atTime(14, 0), "Waiter"),
), List.of( new Shift(tuesday.atTime(14, 0), tuesday.atTime(22, 0), "Bartender")));
new Shift(monday.atTime(6, 0), monday.atTime(14, 0), "Waiter"),
new Shift(monday.atTime(9, 0), monday.atTime(17, 0), "Bartender"),
new Shift(monday.atTime(14, 0), monday.atTime(22, 0), "Bartender"),
new Shift(tuesday.atTime(6, 0), tuesday.atTime(14, 0), "Waiter"),
new Shift(tuesday.atTime(14, 0), tuesday.atTime(22, 0), "Bartender")
));
} }
private static void printSolution(ShiftSchedule solution) { private static void printSolution(ShiftSchedule solution) {
logger.info("Shift assignments"); logger.info("Shift assignments");
for (Shift shift : solution.getShifts()) { for (Shift shift : solution.getShifts()) {
logger.info(" " + shift.getStart().toLocalDate() logger.info(" " + shift.getStart()
+ " " + shift.getStart().toLocalTime() + " - " + shift.getEnd().toLocalTime() .toLocalDate() + " " + shift.getStart()
+ ": " + shift.getEmployee().getName()); .toLocalTime() + " - " + shift.getEnd()
.toLocalTime() + ": " + shift.getEmployee()
.getName());
} }
} }

View File

@ -1,37 +1,35 @@
package com.baeldung.timefoldsolver; package com.baeldung.timefoldsolver;
import static ai.timefold.solver.core.api.score.stream.Joiners.equal;
import ai.timefold.solver.core.api.score.buildin.hardsoft.HardSoftScore; import ai.timefold.solver.core.api.score.buildin.hardsoft.HardSoftScore;
import ai.timefold.solver.core.api.score.stream.Constraint; import ai.timefold.solver.core.api.score.stream.Constraint;
import ai.timefold.solver.core.api.score.stream.ConstraintFactory; import ai.timefold.solver.core.api.score.stream.ConstraintFactory;
import ai.timefold.solver.core.api.score.stream.ConstraintProvider; import ai.timefold.solver.core.api.score.stream.ConstraintProvider;
import static ai.timefold.solver.core.api.score.stream.Joiners.equal;
public class ShiftScheduleConstraintProvider implements ConstraintProvider { public class ShiftScheduleConstraintProvider implements ConstraintProvider {
@Override @Override
public Constraint[] defineConstraints(ConstraintFactory constraintFactory) { public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
return new Constraint[] { return new Constraint[] { atMostOneShiftPerDay(constraintFactory), requiredSkill(constraintFactory) };
atMostOneShiftPerDay(constraintFactory),
requiredSkill(constraintFactory)
};
} }
public Constraint atMostOneShiftPerDay(ConstraintFactory constraintFactory) { public Constraint atMostOneShiftPerDay(ConstraintFactory constraintFactory) {
return constraintFactory.forEach(Shift.class) return constraintFactory.forEach(Shift.class)
.join(Shift.class, .join(Shift.class, equal(shift -> shift.getStart()
equal(shift -> shift.getStart().toLocalDate()), .toLocalDate()), equal(Shift::getEmployee))
equal(Shift::getEmployee)) .filter((shift1, shift2) -> shift1 != shift2)
.filter((shift1, shift2) -> shift1 != shift2) .penalize(HardSoftScore.ONE_HARD)
.penalize(HardSoftScore.ONE_HARD) .asConstraint("At most one shift per day");
.asConstraint("At most one shift per day");
} }
public Constraint requiredSkill(ConstraintFactory constraintFactory) { public Constraint requiredSkill(ConstraintFactory constraintFactory) {
return constraintFactory.forEach(Shift.class) return constraintFactory.forEach(Shift.class)
.filter(shift -> !shift.getEmployee().getSkills().contains(shift.getRequiredSkill())) .filter(shift -> !shift.getEmployee()
.penalize(HardSoftScore.ONE_HARD) .getSkills()
.asConstraint("Required skill"); .contains(shift.getRequiredSkill()))
.penalize(HardSoftScore.ONE_HARD)
.asConstraint("Required skill");
} }
} }

View File

@ -3,49 +3,40 @@ package com.baeldung.timefoldsolver;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Set; import java.util.Set;
import ai.timefold.solver.test.api.score.stream.ConstraintVerifier;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import ai.timefold.solver.test.api.score.stream.ConstraintVerifier;
class ShiftScheduleConstraintProviderTest { class ShiftScheduleConstraintProviderTest {
private static final LocalDate MONDAY = LocalDate.of(2030, 4, 1); private static final LocalDate MONDAY = LocalDate.of(2030, 4, 1);
private static final LocalDate TUESDAY = LocalDate.of(2030, 4, 2); private static final LocalDate TUESDAY = LocalDate.of(2030, 4, 2);
ConstraintVerifier<ShiftScheduleConstraintProvider, ShiftSchedule> constraintVerifier = ConstraintVerifier.build( ConstraintVerifier<ShiftScheduleConstraintProvider, ShiftSchedule> constraintVerifier = ConstraintVerifier.build(new ShiftScheduleConstraintProvider(),
new ShiftScheduleConstraintProvider(), ShiftSchedule.class, Shift.class); ShiftSchedule.class, Shift.class);
@Test @Test
void atMostOneShiftPerDay() { void atMostOneShiftPerDay() {
Employee ann = new Employee("Ann", null); Employee ann = new Employee("Ann", null);
constraintVerifier.verifyThat(ShiftScheduleConstraintProvider::atMostOneShiftPerDay) constraintVerifier.verifyThat(ShiftScheduleConstraintProvider::atMostOneShiftPerDay)
.given( .given(ann, new Shift(MONDAY.atTime(6, 0), MONDAY.atTime(14, 0), null, ann), new Shift(MONDAY.atTime(14, 0), MONDAY.atTime(22, 0), null, ann))
ann, // Penalizes by 2 because both {shiftA, shiftB} and {shiftB, shiftA} match.
new Shift(MONDAY.atTime(6, 0), MONDAY.atTime(14, 0), null, ann), // To avoid that, use forEachUniquePair() in the constraint instead.
new Shift(MONDAY.atTime(14, 0), MONDAY.atTime(22, 0), null, ann)) .penalizesBy(2);
// Penalizes by 2 because both {shiftA, shiftB} and {shiftB, shiftA} match.
// To avoid that, use forEachUniquePair() in the constraint instead.
.penalizesBy(2);
constraintVerifier.verifyThat(ShiftScheduleConstraintProvider::atMostOneShiftPerDay) constraintVerifier.verifyThat(ShiftScheduleConstraintProvider::atMostOneShiftPerDay)
.given( .given(ann, new Shift(MONDAY.atTime(6, 0), MONDAY.atTime(14, 0), null, ann), new Shift(TUESDAY.atTime(14, 0), TUESDAY.atTime(22, 0), null, ann))
ann, .penalizesBy(0);
new Shift(MONDAY.atTime(6, 0), MONDAY.atTime(14, 0), null, ann),
new Shift(TUESDAY.atTime(14, 0), TUESDAY.atTime(22, 0), null, ann))
.penalizesBy(0);
} }
@Test @Test
void requiredSkill() { void requiredSkill() {
Employee ann = new Employee("Ann", Set.of("Waiter")); Employee ann = new Employee("Ann", Set.of("Waiter"));
constraintVerifier.verifyThat(ShiftScheduleConstraintProvider::requiredSkill) constraintVerifier.verifyThat(ShiftScheduleConstraintProvider::requiredSkill)
.given( .given(ann, new Shift(MONDAY.atTime(6, 0), MONDAY.atTime(14, 0), "Cook", ann))
ann, .penalizesBy(1);
new Shift(MONDAY.atTime(6, 0), MONDAY.atTime(14, 0), "Cook", ann))
.penalizesBy(1);
constraintVerifier.verifyThat(ShiftScheduleConstraintProvider::requiredSkill) constraintVerifier.verifyThat(ShiftScheduleConstraintProvider::requiredSkill)
.given( .given(ann, new Shift(MONDAY.atTime(6, 0), MONDAY.atTime(14, 0), "Waiter", ann))
ann, .penalizesBy(0);
new Shift(MONDAY.atTime(6, 0), MONDAY.atTime(14, 0), "Waiter", ann))
.penalizesBy(0);
} }
} }