Skip to content

Commit 0085118

Browse files
authored
Merge pull request #28 from SUJALGOYALL/feature/timetable_generator
feat(timetable): add user input, preferred slots, conflict checking, GA optimization
2 parents 0d988f2 + 900c226 commit 0085118

File tree

7 files changed

+345
-0
lines changed

7 files changed

+345
-0
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include "genetic_algorithm.h"
2+
#include <algorithm>
3+
#include <cstdlib>
4+
#include <ctime>
5+
#include <iostream>
6+
using namespace std;
7+
8+
Timetable GeneticAlgorithm::crossover(const Timetable& p1, const Timetable& p2) {
9+
Timetable child = p1;
10+
size_t half = p1.sessions.size() / 2;
11+
for (size_t i = half; i < p1.sessions.size(); ++i)
12+
child.sessions[i] = p2.sessions[i];
13+
return child;
14+
}
15+
16+
void GeneticAlgorithm::mutate(Timetable& t) {
17+
if (t.rooms.empty() || t.slotsPerDay <= 0 || t.numDays <= 0) return;
18+
19+
for (auto &s : t.sessions) {
20+
if ((rand() / (double)RAND_MAX) < mutationRate) {
21+
s.day = rand() % t.numDays;
22+
s.slot = rand() % t.slotsPerDay;
23+
s.room = t.rooms[rand() % t.rooms.size()].name;
24+
}
25+
}
26+
}
27+
28+
Timetable GeneticAlgorithm::run(const Timetable& base, bool verbose) {
29+
if (base.rooms.empty() || base.slotsPerDay <= 0 || base.numDays <= 0) return base;
30+
31+
vector<Timetable> population;
32+
for (int i = 0; i < populationSize; ++i) {
33+
Timetable t = base;
34+
t.generateRandom();
35+
population.push_back(t);
36+
}
37+
38+
Timetable best = population[0];
39+
int bestFitness = best.computeConflicts();
40+
41+
for (int gen = 0; gen < generations; ++gen) {
42+
sort(population.begin(), population.end(),
43+
[](const Timetable &a, const Timetable &b){ return a.computeConflicts() < b.computeConflicts(); });
44+
45+
if (population[0].computeConflicts() < bestFitness) {
46+
best = population[0];
47+
bestFitness = best.computeConflicts();
48+
}
49+
50+
if (bestFitness == 0) break;
51+
52+
vector<Timetable> newPop;
53+
for (int i = 0; i < populationSize/2; ++i) {
54+
Timetable c1 = crossover(population[i], population[rand() % populationSize]);
55+
Timetable c2 = crossover(population[rand() % populationSize], population[i]);
56+
mutate(c1);
57+
mutate(c2);
58+
newPop.push_back(c1);
59+
newPop.push_back(c2);
60+
}
61+
population = newPop;
62+
63+
if (verbose && gen % 10 == 0)
64+
cout << "Generation " << gen << " | Best Conflicts: " << bestFitness << "\n";
65+
}
66+
67+
return best;
68+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef GENETIC_ALGORITHM_H
2+
#define GENETIC_ALGORITHM_H
3+
4+
#include "timetable.h"
5+
6+
class GeneticAlgorithm {
7+
public:
8+
int populationSize;
9+
int generations;
10+
double mutationRate;
11+
12+
GeneticAlgorithm(int pop, int gen, double mut)
13+
: populationSize(pop), generations(gen), mutationRate(mut) {}
14+
15+
Timetable run(const Timetable& base, bool verbose = true);
16+
17+
private:
18+
Timetable crossover(const Timetable& p1, const Timetable& p2);
19+
void mutate(Timetable& t);
20+
};
21+
22+
#endif

Src/Time_table_Generator/main.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "timetable.h"
2+
#include "genetic_algorithm.h"
3+
#include "utils.h"
4+
#include <iostream>
5+
using namespace std;
6+
7+
int main() {
8+
srand(time(nullptr));
9+
cout << "\n=== C++ Timetable Generator (Genetic Algorithm) ===\n";
10+
11+
Timetable base = getUserInput();
12+
13+
int totalRequiredSessions = 0;
14+
for (auto &c : base.courses) totalRequiredSessions += c.sessionsPerWeek;
15+
int totalAvailableSlots = base.numDays * base.slotsPerDay * base.rooms.size();
16+
17+
if (totalRequiredSessions > totalAvailableSlots) {
18+
cerr << "\n❌ Scheduling impossible: Total required sessions ("
19+
<< totalRequiredSessions << ") exceed total available slots ("
20+
<< totalAvailableSlots << ").\n";
21+
cerr << "Try reducing sessions or increasing rooms/days/slots.\n";
22+
return 1;
23+
}
24+
25+
GeneticAlgorithm ga(30, 100, 0.1);
26+
Timetable optimized = ga.run(base, true);
27+
28+
cout << "\nFinal Best Timetable (Conflicts: " << optimized.computeConflicts() << ")\n";
29+
optimized.print();
30+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#include "timetable.h"
2+
#include <algorithm>
3+
#include <iostream>
4+
using namespace std;
5+
6+
void Timetable::generateRandom() {
7+
if (rooms.empty() || slotsPerDay <= 0 || numDays <= 0) return;
8+
9+
sessions.clear();
10+
srand(time(nullptr));
11+
for (auto &course : courses) {
12+
for (int i = 0; i < course.sessionsPerWeek; ++i) {
13+
Session s;
14+
s.courseName = course.name;
15+
s.instructor = course.instructor;
16+
s.room = rooms[rand() % rooms.size()].name;
17+
s.day = rand() % numDays;
18+
s.slot = rand() % slotsPerDay;
19+
sessions.push_back(s);
20+
}
21+
}
22+
}
23+
24+
int Timetable::computeConflicts() const {
25+
int conflicts = 0;
26+
for (size_t i = 0; i < sessions.size(); ++i) {
27+
for (size_t j = i + 1; j < sessions.size(); ++j) {
28+
const Session &a = sessions[i];
29+
const Session &b = sessions[j];
30+
if (a.day == b.day && a.slot == b.slot) {
31+
if (a.room == b.room) conflicts++;
32+
if (a.instructor == b.instructor) conflicts++;
33+
}
34+
}
35+
}
36+
for (auto &s : sessions) {
37+
auto it = find_if(courses.begin(), courses.end(),
38+
[&](const Course &c){ return c.name == s.courseName; });
39+
if (it != courses.end() && !it->allowedSlots.empty()) {
40+
if (find(it->allowedSlots.begin(), it->allowedSlots.end(), s.slot) == it->allowedSlots.end())
41+
conflicts++;
42+
}
43+
}
44+
return conflicts;
45+
}
46+
47+
void Timetable::print() const {
48+
vector<Session> sorted = sessions;
49+
sort(sorted.begin(), sorted.end(), [](const Session &a, const Session &b){
50+
if (a.day != b.day) return a.day < b.day;
51+
return a.slot < b.slot;
52+
});
53+
54+
cout << "\n===== Optimized Timetable =====\n";
55+
for (auto &s : sorted) {
56+
string slotLabel = (s.slot >= 0 && s.slot < (int)slotLabels.size() && !slotLabels[s.slot].empty())
57+
? slotLabels[s.slot]
58+
: ("Slot " + to_string(s.slot + 1));
59+
cout << "Day " << s.day+1 << ", " << slotLabel
60+
<< " | " << s.courseName
61+
<< " | Instructor: " << s.instructor
62+
<< " | Room: " << s.room << "\n";
63+
}
64+
cout << "===============================\n";
65+
}
1.35 MB
Binary file not shown.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#ifndef TIMETABLE_H
2+
#define TIMETABLE_H
3+
4+
#include <bits/stdc++.h>
5+
using namespace std;
6+
7+
struct Course {
8+
string name;
9+
string instructor;
10+
int sessionsPerWeek;
11+
vector<int> allowedSlots; // optional preferred slots (0-based)
12+
};
13+
14+
struct Room {
15+
string name;
16+
};
17+
18+
struct Session {
19+
string courseName;
20+
string instructor;
21+
string room;
22+
int day;
23+
int slot;
24+
};
25+
26+
class Timetable {
27+
public:
28+
int numDays;
29+
int slotsPerDay;
30+
vector<string> slotLabels;
31+
vector<Room> rooms;
32+
vector<Course> courses;
33+
vector<Session> sessions;
34+
35+
Timetable() {}
36+
Timetable(int d, int s, const vector<Room>& r, const vector<Course>& c,
37+
const vector<string>& labels)
38+
: numDays(d), slotsPerDay(s), rooms(r), courses(c), slotLabels(labels) {}
39+
40+
void generateRandom();
41+
int computeConflicts() const;
42+
void print() const;
43+
};
44+
45+
#endif

Src/Time_table_Generator/utils.h

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#ifndef UTILS_H
2+
#define UTILS_H
3+
4+
#include "timetable.h"
5+
#include <regex>
6+
#include <iostream>
7+
#include <string>
8+
9+
constexpr int MAX_DAYS = 7;
10+
constexpr int MAX_SLOTS = 24;
11+
constexpr int MAX_ROOMS = 50;
12+
constexpr int MAX_COURSES = 100;
13+
14+
// Robust numeric input
15+
inline int getSafeInt(const std::string &prompt, int minVal, int maxVal) {
16+
while (true) {
17+
std::cout << prompt;
18+
std::string line;
19+
std::getline(std::cin, line);
20+
21+
line.erase(0, line.find_first_not_of(" \t\n\r"));
22+
line.erase(line.find_last_not_of(" \t\n\r") + 1);
23+
24+
if (line.empty()) {
25+
std::cout << "Input cannot be empty! Please enter a number.\n";
26+
continue;
27+
}
28+
29+
bool allDigits = true;
30+
for (char c : line) {
31+
if (!isdigit(c)) { allDigits = false; break; }
32+
}
33+
if (!allDigits) {
34+
std::cout << "Invalid input! Enter a numeric integer.\n";
35+
continue;
36+
}
37+
38+
try {
39+
int val = std::stoi(line);
40+
if (val < minVal || val > maxVal) {
41+
std::cout << "Invalid input! Enter integer between " << minVal << " and " << maxVal << ".\n";
42+
continue;
43+
}
44+
return val;
45+
} catch (...) {
46+
std::cout << "Invalid input! Could not convert to number.\n";
47+
}
48+
}
49+
}
50+
51+
// Non-empty string input
52+
inline std::string getNonEmptyString(const std::string &prompt) {
53+
std::string s;
54+
while (true) {
55+
std::cout << prompt;
56+
std::getline(std::cin, s);
57+
if (!s.empty()) return s;
58+
std::cout << "Input cannot be empty! Please enter a valid value.\n";
59+
}
60+
}
61+
62+
// Validate slot label (HH:MM-HH:MM or single uppercase letter)
63+
inline bool isValidSlotLabel(const std::string &s) {
64+
std::regex timePattern(R"(\d{1,2}:\d{2}-\d{1,2}:\d{2})");
65+
std::regex letterPattern(R"([A-Z])");
66+
return std::regex_match(s, timePattern) || std::regex_match(s, letterPattern);
67+
}
68+
69+
inline std::string getSlotLabel(int i) {
70+
std::string label;
71+
while (true) {
72+
std::cout << "Slot " << i + 1 << " label (HH:MM-HH:MM or A-Z): ";
73+
std::getline(std::cin, label);
74+
if (label.empty()) label = "Slot " + std::to_string(i + 1);
75+
if (isValidSlotLabel(label)) break;
76+
std::cout << "Invalid format! Use HH:MM-HH:MM (09:00-10:00) or single letter (A-Z).\n";
77+
}
78+
return label;
79+
}
80+
81+
inline Timetable getUserInput() {
82+
int days = getSafeInt("Enter number of days in week (1-7): ", 1, MAX_DAYS);
83+
int slots = getSafeInt("Enter slots per day (1-24): ", 1, MAX_SLOTS);
84+
85+
std::vector<std::string> slotLabels(slots);
86+
std::cout << "\nEnter label or time for each slot:\n";
87+
for (int i = 0; i < slots; ++i) slotLabels[i] = getSlotLabel(i);
88+
89+
int numRooms = getSafeInt("\nEnter number of rooms (1-50): ", 1, MAX_ROOMS);
90+
std::vector<Room> rooms(numRooms);
91+
for (int i = 0; i < numRooms; ++i) rooms[i].name = getNonEmptyString("Room " + std::to_string(i + 1) + " name: ");
92+
93+
int numCourses = getSafeInt("\nEnter number of courses (1-100): ", 1, MAX_COURSES);
94+
std::vector<Course> courses(numCourses);
95+
96+
int totalAvailableSlots = days * slots * numRooms;
97+
98+
for (int i = 0; i < numCourses; ++i) {
99+
std::cout << "\nCourse " << i + 1 << " details:\n";
100+
courses[i].name = getNonEmptyString("Course name: ");
101+
courses[i].instructor = getNonEmptyString("Instructor name: ");
102+
courses[i].sessionsPerWeek = getSafeInt("Sessions per week: ", 1, totalAvailableSlots);
103+
104+
int prefCount = getSafeInt("Number of preferred slots (0 for none): ", 0, slots);
105+
courses[i].allowedSlots.clear();
106+
for (int j = 0; j < prefCount; ++j) {
107+
int slot = getSafeInt("Preferred slot index (1-" + std::to_string(slots) + "): ", 1, slots);
108+
courses[i].allowedSlots.push_back(slot - 1);
109+
}
110+
}
111+
112+
return Timetable(days, slots, rooms, courses, slotLabels);
113+
}
114+
115+
#endif

0 commit comments

Comments
 (0)