diff --git a/drools/pom.xml b/drools/pom.xml
index 631bc8c792..009ac8acec 100644
--- a/drools/pom.xml
+++ b/drools/pom.xml
@@ -48,12 +48,18 @@
poi-ooxml
${apache-poi-version}
+
+ org.optaplanner
+ optaplanner-core
+ ${opta-planner-version}
+
4.4.6
7.4.1.Final
3.13
+ 7.10.0.Final
diff --git a/drools/src/main/java/com/baeldung/drools/optaplanner/CourseSchedule.java b/drools/src/main/java/com/baeldung/drools/optaplanner/CourseSchedule.java
new file mode 100644
index 0000000000..7b2ba117a1
--- /dev/null
+++ b/drools/src/main/java/com/baeldung/drools/optaplanner/CourseSchedule.java
@@ -0,0 +1,63 @@
+package com.baeldung.drools.optaplanner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.optaplanner.core.api.domain.solution.PlanningEntityCollectionProperty;
+import org.optaplanner.core.api.domain.solution.PlanningScore;
+import org.optaplanner.core.api.domain.solution.PlanningSolution;
+import org.optaplanner.core.api.domain.solution.drools.ProblemFactCollectionProperty;
+import org.optaplanner.core.api.domain.valuerange.ValueRangeProvider;
+import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@PlanningSolution
+public class CourseSchedule {
+
+ Logger logger = LoggerFactory.getLogger("CourseSchedule");
+
+ private List roomList;
+ private List periodList;
+ private List lectureList;
+ private HardSoftScore score;
+
+ public CourseSchedule(){
+ roomList = new ArrayList<>();
+ periodList = new ArrayList<>();
+ lectureList = new ArrayList<>();
+ }
+
+ @ValueRangeProvider(id = "availableRooms")
+ @ProblemFactCollectionProperty
+ public List getRoomList() {
+ return roomList;
+ }
+
+ @ValueRangeProvider(id = "availablePeriods")
+ @ProblemFactCollectionProperty
+ public List getPeriodList() {
+ return periodList;
+ }
+
+ @PlanningEntityCollectionProperty
+ public List getLectureList() {
+ return lectureList;
+ }
+
+ @PlanningScore
+ public HardSoftScore getScore() {
+ return score;
+ }
+
+ public void setScore(HardSoftScore score) {
+ this.score = score;
+ }
+
+ public void printCourseSchedule() {
+ lectureList.stream()
+ .map(c -> "Lecture in Room " + c.getRoomNumber().toString() + " during Period " + c.getPeriod().toString())
+ .forEach(k -> logger.info(k));
+ }
+
+}
diff --git a/drools/src/main/java/com/baeldung/drools/optaplanner/Lecture.java b/drools/src/main/java/com/baeldung/drools/optaplanner/Lecture.java
new file mode 100644
index 0000000000..193cdb08b1
--- /dev/null
+++ b/drools/src/main/java/com/baeldung/drools/optaplanner/Lecture.java
@@ -0,0 +1,30 @@
+package com.baeldung.drools.optaplanner;
+
+import org.optaplanner.core.api.domain.entity.PlanningEntity;
+import org.optaplanner.core.api.domain.variable.PlanningVariable;
+
+@PlanningEntity
+public class Lecture {
+
+ private Integer roomNumber;
+ private Integer period;
+
+ @PlanningVariable(valueRangeProviderRefs = {"availablePeriods"})
+ public Integer getPeriod() {
+ return period;
+ }
+
+ @PlanningVariable(valueRangeProviderRefs = {"availableRooms"})
+ public Integer getRoomNumber() {
+ return roomNumber;
+ }
+
+ public void setPeriod(Integer period) {
+ this.period = period;
+ }
+
+ public void setRoomNumber(Integer roomNumber) {
+ this.roomNumber = roomNumber;
+ }
+
+}
diff --git a/drools/src/main/java/com/baeldung/drools/optaplanner/ScoreCalculator.java b/drools/src/main/java/com/baeldung/drools/optaplanner/ScoreCalculator.java
new file mode 100644
index 0000000000..86501cdccd
--- /dev/null
+++ b/drools/src/main/java/com/baeldung/drools/optaplanner/ScoreCalculator.java
@@ -0,0 +1,32 @@
+package com.baeldung.drools.optaplanner;
+
+import java.util.HashSet;
+
+import org.optaplanner.core.api.score.Score;
+import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
+import org.optaplanner.core.impl.score.director.easy.EasyScoreCalculator;
+
+public class ScoreCalculator implements EasyScoreCalculator {
+
+ @Override
+ public Score calculateScore(CourseSchedule courseSchedule) {
+ int hardScore = 0;
+ int softScore = 0;
+
+ HashSet occupiedRooms = new HashSet<>();
+ for (Lecture lecture : courseSchedule.getLectureList()) {
+ if(lecture.getPeriod() != null && lecture.getRoomNumber() != null) {
+ String roomInUse = lecture.getPeriod().toString() + ":" + lecture.getRoomNumber().toString();
+ if (occupiedRooms.contains(roomInUse)) {
+ hardScore += -1;
+ } else {
+ occupiedRooms.add(roomInUse);
+ }
+ } else {
+ hardScore += -1;
+ }
+ }
+
+ return HardSoftScore.valueOf(hardScore, softScore);
+ }
+}
diff --git a/drools/src/main/resources/courseSchedule.drl b/drools/src/main/resources/courseSchedule.drl
new file mode 100644
index 0000000000..35dd57df84
--- /dev/null
+++ b/drools/src/main/resources/courseSchedule.drl
@@ -0,0 +1,14 @@
+package com.baeldung.drools.optaplanner
+
+import com.baeldung.drools.optaplanner.Lecture;
+import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder;
+
+global HardSoftScoreHolder scoreHolder;
+
+rule "noNullRoomPeriod"
+ when
+ Lecture( roomNumber == null );
+ Lecture( period == null );
+ then
+ scoreHolder.addHardConstraintMatch(kcontext, -1);
+end
\ No newline at end of file
diff --git a/drools/src/main/resources/courseScheduleSolverConfigDrools.xml b/drools/src/main/resources/courseScheduleSolverConfigDrools.xml
new file mode 100644
index 0000000000..7cf95fdcd3
--- /dev/null
+++ b/drools/src/main/resources/courseScheduleSolverConfigDrools.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ courseSchedule.drl
+
+
+
+ 10
+
+
\ No newline at end of file
diff --git a/drools/src/main/resources/courseScheduleSolverConfiguration.xml b/drools/src/main/resources/courseScheduleSolverConfiguration.xml
new file mode 100644
index 0000000000..dfedb8f2fd
--- /dev/null
+++ b/drools/src/main/resources/courseScheduleSolverConfiguration.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ com.baeldung.drools.optaplanner.ScoreCalculator
+
+
+
+ 10
+
+
\ No newline at end of file
diff --git a/drools/src/test/java/com/baeldung/drools/optaplanner/OptaPlannerUnitTest.java b/drools/src/test/java/com/baeldung/drools/optaplanner/OptaPlannerUnitTest.java
new file mode 100644
index 0000000000..1880e30b86
--- /dev/null
+++ b/drools/src/test/java/com/baeldung/drools/optaplanner/OptaPlannerUnitTest.java
@@ -0,0 +1,49 @@
+package com.baeldung.drools.optaplanner;
+
+import java.util.Arrays;
+
+import org.junit.Assert;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.optaplanner.core.api.solver.Solver;
+import org.optaplanner.core.api.solver.SolverFactory;
+
+public class OptaPlannerUnitTest {
+
+ static CourseSchedule unsolvedCourseSchedule;
+
+ @BeforeAll
+ public static void setUp() {
+
+ unsolvedCourseSchedule = new CourseSchedule();
+
+ for(int i = 0; i < 10; i++){
+ unsolvedCourseSchedule.getLectureList().add(new Lecture());
+ }
+
+ unsolvedCourseSchedule.getPeriodList().addAll(Arrays.asList(new Integer[] { 1, 2, 3 }));
+ unsolvedCourseSchedule.getRoomList().addAll(Arrays.asList(new Integer[] { 1, 2 }));
+ }
+
+ @Test
+ public void test_whenCustomJavaSolver() {
+
+ SolverFactory solverFactory = SolverFactory.createFromXmlResource("courseScheduleSolverConfiguration.xml");
+ Solver solver = solverFactory.buildSolver();
+ CourseSchedule solvedCourseSchedule = solver.solve(unsolvedCourseSchedule);
+
+ Assert.assertNotNull(solvedCourseSchedule.getScore());
+ Assert.assertEquals(-4, solvedCourseSchedule.getScore().getHardScore());
+ }
+
+ @Test
+ public void test_whenDroolsSolver() {
+
+ SolverFactory solverFactory = SolverFactory.createFromXmlResource("courseScheduleSolverConfigDrools.xml");
+ Solver solver = solverFactory.buildSolver();
+ CourseSchedule solvedCourseSchedule = solver.solve(unsolvedCourseSchedule);
+
+ Assert.assertNotNull(solvedCourseSchedule.getScore());
+ Assert.assertEquals(0, solvedCourseSchedule.getScore().getHardScore());
+ }
+}