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
68 changes: 68 additions & 0 deletions Src/Time_table_Generator/genetic_algorithm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include "genetic_algorithm.h"
#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;

Timetable GeneticAlgorithm::crossover(const Timetable& p1, const Timetable& p2) {
Timetable child = p1;
size_t half = p1.sessions.size() / 2;
for (size_t i = half; i < p1.sessions.size(); ++i)
child.sessions[i] = p2.sessions[i];
return child;
}

void GeneticAlgorithm::mutate(Timetable& t) {
if (t.rooms.empty() || t.slotsPerDay <= 0 || t.numDays <= 0) return;

for (auto &s : t.sessions) {
if ((rand() / (double)RAND_MAX) < mutationRate) {
s.day = rand() % t.numDays;
s.slot = rand() % t.slotsPerDay;
s.room = t.rooms[rand() % t.rooms.size()].name;
}
}
}

Timetable GeneticAlgorithm::run(const Timetable& base, bool verbose) {
if (base.rooms.empty() || base.slotsPerDay <= 0 || base.numDays <= 0) return base;

vector<Timetable> population;
for (int i = 0; i < populationSize; ++i) {
Timetable t = base;
t.generateRandom();
population.push_back(t);
}

Timetable best = population[0];
int bestFitness = best.computeConflicts();

for (int gen = 0; gen < generations; ++gen) {
sort(population.begin(), population.end(),
[](const Timetable &a, const Timetable &b){ return a.computeConflicts() < b.computeConflicts(); });

if (population[0].computeConflicts() < bestFitness) {
best = population[0];
bestFitness = best.computeConflicts();
}

if (bestFitness == 0) break;

vector<Timetable> newPop;
for (int i = 0; i < populationSize/2; ++i) {
Timetable c1 = crossover(population[i], population[rand() % populationSize]);
Timetable c2 = crossover(population[rand() % populationSize], population[i]);
mutate(c1);
mutate(c2);
newPop.push_back(c1);
newPop.push_back(c2);
}
population = newPop;

if (verbose && gen % 10 == 0)
cout << "Generation " << gen << " | Best Conflicts: " << bestFitness << "\n";
}

return best;
}
22 changes: 22 additions & 0 deletions Src/Time_table_Generator/genetic_algorithm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef GENETIC_ALGORITHM_H
#define GENETIC_ALGORITHM_H

#include "timetable.h"

class GeneticAlgorithm {
public:
int populationSize;
int generations;
double mutationRate;

GeneticAlgorithm(int pop, int gen, double mut)
: populationSize(pop), generations(gen), mutationRate(mut) {}

Timetable run(const Timetable& base, bool verbose = true);

private:
Timetable crossover(const Timetable& p1, const Timetable& p2);
void mutate(Timetable& t);
};

#endif
30 changes: 30 additions & 0 deletions Src/Time_table_Generator/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "timetable.h"
#include "genetic_algorithm.h"
#include "utils.h"
#include <iostream>
using namespace std;

int main() {
srand(time(nullptr));
cout << "\n=== C++ Timetable Generator (Genetic Algorithm) ===\n";

Timetable base = getUserInput();

int totalRequiredSessions = 0;
for (auto &c : base.courses) totalRequiredSessions += c.sessionsPerWeek;
int totalAvailableSlots = base.numDays * base.slotsPerDay * base.rooms.size();

if (totalRequiredSessions > totalAvailableSlots) {
cerr << "\n❌ Scheduling impossible: Total required sessions ("
<< totalRequiredSessions << ") exceed total available slots ("
<< totalAvailableSlots << ").\n";
cerr << "Try reducing sessions or increasing rooms/days/slots.\n";
return 1;
}

GeneticAlgorithm ga(30, 100, 0.1);
Timetable optimized = ga.run(base, true);

cout << "\nFinal Best Timetable (Conflicts: " << optimized.computeConflicts() << ")\n";
optimized.print();
}
65 changes: 65 additions & 0 deletions Src/Time_table_Generator/timetable.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "timetable.h"
#include <algorithm>
#include <iostream>
using namespace std;

void Timetable::generateRandom() {
if (rooms.empty() || slotsPerDay <= 0 || numDays <= 0) return;

sessions.clear();
srand(time(nullptr));
for (auto &course : courses) {
for (int i = 0; i < course.sessionsPerWeek; ++i) {
Session s;
s.courseName = course.name;
s.instructor = course.instructor;
s.room = rooms[rand() % rooms.size()].name;
s.day = rand() % numDays;
s.slot = rand() % slotsPerDay;
sessions.push_back(s);
}
}
}

int Timetable::computeConflicts() const {
int conflicts = 0;
for (size_t i = 0; i < sessions.size(); ++i) {
for (size_t j = i + 1; j < sessions.size(); ++j) {
const Session &a = sessions[i];
const Session &b = sessions[j];
if (a.day == b.day && a.slot == b.slot) {
if (a.room == b.room) conflicts++;
if (a.instructor == b.instructor) conflicts++;
}
}
}
for (auto &s : sessions) {
auto it = find_if(courses.begin(), courses.end(),
[&](const Course &c){ return c.name == s.courseName; });
if (it != courses.end() && !it->allowedSlots.empty()) {
if (find(it->allowedSlots.begin(), it->allowedSlots.end(), s.slot) == it->allowedSlots.end())
conflicts++;
}
}
return conflicts;
}

void Timetable::print() const {
vector<Session> sorted = sessions;
sort(sorted.begin(), sorted.end(), [](const Session &a, const Session &b){
if (a.day != b.day) return a.day < b.day;
return a.slot < b.slot;
});

cout << "\n===== Optimized Timetable =====\n";
for (auto &s : sorted) {
string slotLabel = (s.slot >= 0 && s.slot < (int)slotLabels.size() && !slotLabels[s.slot].empty())
? slotLabels[s.slot]
: ("Slot " + to_string(s.slot + 1));
cout << "Day " << s.day+1 << ", " << slotLabel
<< " | " << s.courseName
<< " | Instructor: " << s.instructor
<< " | Room: " << s.room << "\n";
}
cout << "===============================\n";
}
Binary file added Src/Time_table_Generator/timetable.exe
Binary file not shown.
45 changes: 45 additions & 0 deletions Src/Time_table_Generator/timetable.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#ifndef TIMETABLE_H
#define TIMETABLE_H

#include <bits/stdc++.h>
using namespace std;

struct Course {
string name;
string instructor;
int sessionsPerWeek;
vector<int> allowedSlots; // optional preferred slots (0-based)
};

struct Room {
string name;
};

struct Session {
string courseName;
string instructor;
string room;
int day;
int slot;
};

class Timetable {
public:
int numDays;
int slotsPerDay;
vector<string> slotLabels;
vector<Room> rooms;
vector<Course> courses;
vector<Session> sessions;

Timetable() {}
Timetable(int d, int s, const vector<Room>& r, const vector<Course>& c,
const vector<string>& labels)
: numDays(d), slotsPerDay(s), rooms(r), courses(c), slotLabels(labels) {}

void generateRandom();
int computeConflicts() const;
void print() const;
};

#endif
115 changes: 115 additions & 0 deletions Src/Time_table_Generator/utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#ifndef UTILS_H
#define UTILS_H

#include "timetable.h"
#include <regex>
#include <iostream>
#include <string>

constexpr int MAX_DAYS = 7;
constexpr int MAX_SLOTS = 24;
constexpr int MAX_ROOMS = 50;
constexpr int MAX_COURSES = 100;

// Robust numeric input
inline int getSafeInt(const std::string &prompt, int minVal, int maxVal) {
while (true) {
std::cout << prompt;
std::string line;
std::getline(std::cin, line);

line.erase(0, line.find_first_not_of(" \t\n\r"));
line.erase(line.find_last_not_of(" \t\n\r") + 1);

if (line.empty()) {
std::cout << "Input cannot be empty! Please enter a number.\n";
continue;
}

bool allDigits = true;
for (char c : line) {
if (!isdigit(c)) { allDigits = false; break; }
}
if (!allDigits) {
std::cout << "Invalid input! Enter a numeric integer.\n";
continue;
}

try {
int val = std::stoi(line);
if (val < minVal || val > maxVal) {
std::cout << "Invalid input! Enter integer between " << minVal << " and " << maxVal << ".\n";
continue;
}
return val;
} catch (...) {
std::cout << "Invalid input! Could not convert to number.\n";
}
}
}

// Non-empty string input
inline std::string getNonEmptyString(const std::string &prompt) {
std::string s;
while (true) {
std::cout << prompt;
std::getline(std::cin, s);
if (!s.empty()) return s;
std::cout << "Input cannot be empty! Please enter a valid value.\n";
}
}

// Validate slot label (HH:MM-HH:MM or single uppercase letter)
inline bool isValidSlotLabel(const std::string &s) {
std::regex timePattern(R"(\d{1,2}:\d{2}-\d{1,2}:\d{2})");
std::regex letterPattern(R"([A-Z])");
return std::regex_match(s, timePattern) || std::regex_match(s, letterPattern);
}

inline std::string getSlotLabel(int i) {
std::string label;
while (true) {
std::cout << "Slot " << i + 1 << " label (HH:MM-HH:MM or A-Z): ";
std::getline(std::cin, label);
if (label.empty()) label = "Slot " + std::to_string(i + 1);
if (isValidSlotLabel(label)) break;
std::cout << "Invalid format! Use HH:MM-HH:MM (09:00-10:00) or single letter (A-Z).\n";
}
return label;
}

inline Timetable getUserInput() {
int days = getSafeInt("Enter number of days in week (1-7): ", 1, MAX_DAYS);
int slots = getSafeInt("Enter slots per day (1-24): ", 1, MAX_SLOTS);

std::vector<std::string> slotLabels(slots);
std::cout << "\nEnter label or time for each slot:\n";
for (int i = 0; i < slots; ++i) slotLabels[i] = getSlotLabel(i);

int numRooms = getSafeInt("\nEnter number of rooms (1-50): ", 1, MAX_ROOMS);
std::vector<Room> rooms(numRooms);
for (int i = 0; i < numRooms; ++i) rooms[i].name = getNonEmptyString("Room " + std::to_string(i + 1) + " name: ");

int numCourses = getSafeInt("\nEnter number of courses (1-100): ", 1, MAX_COURSES);
std::vector<Course> courses(numCourses);

int totalAvailableSlots = days * slots * numRooms;

for (int i = 0; i < numCourses; ++i) {
std::cout << "\nCourse " << i + 1 << " details:\n";
courses[i].name = getNonEmptyString("Course name: ");
courses[i].instructor = getNonEmptyString("Instructor name: ");
courses[i].sessionsPerWeek = getSafeInt("Sessions per week: ", 1, totalAvailableSlots);

int prefCount = getSafeInt("Number of preferred slots (0 for none): ", 0, slots);
courses[i].allowedSlots.clear();
for (int j = 0; j < prefCount; ++j) {
int slot = getSafeInt("Preferred slot index (1-" + std::to_string(slots) + "): ", 1, slots);
courses[i].allowedSlots.push_back(slot - 1);
}
}

return Timetable(days, slots, rooms, courses, slotLabels);
}

#endif
Loading