diff --git a/optaplanner/pom.xml b/optaplanner/pom.xml
new file mode 100644
index 0000000000..22abbedc8c
--- /dev/null
+++ b/optaplanner/pom.xml
@@ -0,0 +1,27 @@
+
+
+
+ parent-modules
+ com.baeldung
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+
+ optaplanner
+
+
+
+ org.optaplanner
+ optaplanner-core
+ 7.9.0.Final
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/optaplanner/src/main/java/com/baeldung/optaplanner/CourseSchedule.java b/optaplanner/src/main/java/com/baeldung/optaplanner/CourseSchedule.java
new file mode 100644
index 0000000000..8a820ab56e
--- /dev/null
+++ b/optaplanner/src/main/java/com/baeldung/optaplanner/CourseSchedule.java
@@ -0,0 +1,63 @@
+package com.baeldung.optaplanner;
+
+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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@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/optaplanner/src/main/java/com/baeldung/optaplanner/Lecture.java b/optaplanner/src/main/java/com/baeldung/optaplanner/Lecture.java
new file mode 100644
index 0000000000..48af4e89d1
--- /dev/null
+++ b/optaplanner/src/main/java/com/baeldung/optaplanner/Lecture.java
@@ -0,0 +1,30 @@
+package com.baeldung.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/optaplanner/src/main/java/com/baeldung/optaplanner/ScoreCalculator.java b/optaplanner/src/main/java/com/baeldung/optaplanner/ScoreCalculator.java
new file mode 100644
index 0000000000..11e5e4a5be
--- /dev/null
+++ b/optaplanner/src/main/java/com/baeldung/optaplanner/ScoreCalculator.java
@@ -0,0 +1,32 @@
+package com.baeldung.optaplanner;
+
+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;
+
+import java.util.HashSet;
+
+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/optaplanner/src/main/resources/courseSchedule.drl b/optaplanner/src/main/resources/courseSchedule.drl
new file mode 100644
index 0000000000..9372a24976
--- /dev/null
+++ b/optaplanner/src/main/resources/courseSchedule.drl
@@ -0,0 +1,14 @@
+package com.baeldung.optaplanner
+
+import com.baeldung.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/optaplanner/src/main/resources/courseScheduleSolverConfigDrools.xml b/optaplanner/src/main/resources/courseScheduleSolverConfigDrools.xml
new file mode 100644
index 0000000000..7cf95fdcd3
--- /dev/null
+++ b/optaplanner/src/main/resources/courseScheduleSolverConfigDrools.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ courseSchedule.drl
+
+
+
+ 10
+
+
\ No newline at end of file
diff --git a/optaplanner/src/main/resources/courseScheduleSolverConfiguration.xml b/optaplanner/src/main/resources/courseScheduleSolverConfiguration.xml
new file mode 100644
index 0000000000..0ad2264dcd
--- /dev/null
+++ b/optaplanner/src/main/resources/courseScheduleSolverConfiguration.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ com.baeldung.optaplanner.ScoreCalculator
+
+
+
+ 10
+
+
\ No newline at end of file
diff --git a/optaplanner/src/main/resources/logback.xml b/optaplanner/src/main/resources/logback.xml
new file mode 100644
index 0000000000..c109aa19e2
--- /dev/null
+++ b/optaplanner/src/main/resources/logback.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/optaplanner/src/test/java/com/baeldung/optaplanner/test/OptaPlannerUnitTest.java b/optaplanner/src/test/java/com/baeldung/optaplanner/test/OptaPlannerUnitTest.java
new file mode 100644
index 0000000000..2847a233a5
--- /dev/null
+++ b/optaplanner/src/test/java/com/baeldung/optaplanner/test/OptaPlannerUnitTest.java
@@ -0,0 +1,50 @@
+package com.baeldung.optaplanner.test;
+
+import com.baeldung.optaplanner.CourseSchedule;
+import com.baeldung.optaplanner.Lecture;
+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;
+import java.util.Arrays;
+
+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());
+ }
+}
diff --git a/pom.xml b/pom.xml
index 7bed9c5cc0..583ab8a957 100644
--- a/pom.xml
+++ b/pom.xml
@@ -592,6 +592,7 @@
spring-webflux-amqp
antlr
maven-archetype
+ optaplanner
apache-meecrowave
spring-reactive-kotlin
jnosql
@@ -1295,4 +1296,4 @@
3.8
-
+
\ No newline at end of file