Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions src/main/java/com/unitime/algorthm/Scheduler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.unitime.algorthm;

import java.util.ArrayList;
import java.util.List;

import com.unitime.feature.Course;

public class Scheduler {
//List to store all valid timetable combinations
private List<List<Course>> allSchedules;

/**
* Creates all possible schedules that fit your rules (time & credit limit)
*
* @param mandatoryList List of courses that MUST be included
* @param optionList List of optional courses that CAN be combined
* @param goalCredit Maximum total credit allowed
* @return List<List<Course>> All valid schedule combinations found
*/
public List<List<Course>> schedule(List<Course> mandatoryList, List<Course> optionList, int goalCredit) {

allSchedules = new ArrayList<>();

List<Course> currentSchedule = new ArrayList<>();

int currentCredit = 0;

// 1. Add mandatory courses to the schedule first
for (Course m : mandatoryList) {
currentSchedule.add(m);
currentCredit += m.getCredit();
}

// 2. Start the recursive search to find combinations
findCombinations(0, currentSchedule, currentCredit, optionList, goalCredit);

return allSchedules;
}
/**
* Recursively finds all valid course combinations (Backtracking)
* @param index Start index for searching in optionList (prevents duplicates)
* @param currentSchedule List of courses currently selected in this recursion
* @param currentCredit Total credit hours accumulated in the current schedule
*/
private void findCombinations(int index, List<Course> currentSchedule, int currentCredit, List<Course> optionList, int goalCredit) {
allSchedules.add(new ArrayList<>(currentSchedule));

// Loop through remaining options
// Start from 'index' to prevent duplicates (e.g., A+B and B+A)
for (int i = index; i < optionList.size(); i++) {
Course candidate = optionList.get(i);

// [Skip Rule 1] Stop if adding the course exceeds the max credit allowed
if (currentCredit + candidate.getCredit() > goalCredit) {
continue;
}

// [Skip Rule 2] Stop if the course overlaps with the current schedule
if (isTimeOverlap(currentSchedule, candidate)) {
continue;
}

// 1. CHOOSE: Add the course
currentSchedule.add(candidate);

// 2. RECURSE: Go deeper to find the next course (start from i + 1)
findCombinations(i + 1, currentSchedule, currentCredit + candidate.getCredit(), optionList, goalCredit);

// 3. BACKTRACK: Remove the course to try the next option in the loop
currentSchedule.remove(currentSchedule.size() - 1);
}
}

private boolean isTimeOverlap(List<Course> currentTable, Course newCourse) {
for (Course existing : currentTable) {
if (existing.getDay() != newCourse.getDay()) {
continue;
}
if (existing.getStartTime() < newCourse.getEndTime() &&
existing.getEndTime() > newCourse.getStartTime()) {
return true;
}
}
return false;
}
}
71 changes: 71 additions & 0 deletions src/test/java/com/unitime/algorthm/SchedulerTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.unitime.algorthm;

import static org.junit.Assert.*;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import com.unitime.feature.Course;

public class SchedulerTest {
// Check if schedule is generated when basic constraints are met
@Test
public void testBasicSchedule() {
List<Course> mandatory = new ArrayList<>();
mandatory.add(new Course("OSS", 3, 0, 600, 720, "Mon 10:00-12:00"));

List<Course> option = new ArrayList<>();
option.add(new Course("CS", 3, 1, 600, 720, "Tue 10:00-12:00"));

Scheduler scheduler = new Scheduler();
List<List<Course>> result = scheduler.schedule(mandatory, option, 10);

assertTrue(result.size() > 0);
}

// Check if credit limit is respected
@Test
public void testTimeConflict() {
List<Course> mandatory = new ArrayList<>();
mandatory.add(new Course("OSS", 3, 0, 600, 720, "Mon 10:00-12:00"));

List<Course> option = new ArrayList<>();
option.add(new Course("Tennis", 2, 0, 660, 750, "Mon 11:00-12:30"));

Scheduler scheduler = new Scheduler();
List<List<Course>> result = scheduler.schedule(mandatory, option, 10);

List<Course> firstSchedule = result.get(0);
boolean hasConflict = false;
for(Course c : firstSchedule) {
if(c.getName().contains("Tennis")) {
hasConflict = true;
}
}

assertFalse(hasConflict);
}

// Check if credit limit is respected
@Test
public void testCreditLimit() {
List<Course> mandatory = new ArrayList<>();
mandatory.add(new Course("OSS", 3, 0, 600, 720, "Mon 10:00"));

List<Course> option = new ArrayList<>();
option.add(new Course("DS)", 3, 1, 600, 720, "Tue"));
option.add(new Course("CS", 3, 2, 600, 720, "Wed"));
option.add(new Course("BPM", 3, 3, 600, 720, "Thu"));

int goalCredit = 5;

Scheduler scheduler = new Scheduler();
List<List<Course>> result = scheduler.schedule(mandatory, option, goalCredit);

int totalCredit = 0;
for(Course c : result.get(0)) {
totalCredit += c.getCredit();
}

assertTrue(totalCredit <= goalCredit);
}
}