move algorithm to own file
This commit is contained in:
3 files changed
+573
-540
No files matched your search
@@ -0,0 +1,31 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "main.h"
|
||||
#include "students.h"
|
||||
#include "courses.h"
|
||||
|
||||
#ifndef GENERATOR_H_INCLUDED
|
||||
#define GENERATOR_H_INCLUDED
|
||||
|
||||
const char FLEX[2][11] = {"XAT--12A-S", "XAT--12B-S"};
|
||||
|
||||
typedef struct {
|
||||
uint8_t numberOfClasses;
|
||||
char classes[CLASSROOMS][MAX_COURSE_ID_LEN];
|
||||
} TIMETABLE_BLOCK;
|
||||
|
||||
typedef struct {
|
||||
TIMETABLE_BLOCK timetable[TOTAL_BLOCKS];
|
||||
bool success;
|
||||
} TIMETABLE;
|
||||
|
||||
typedef enum {
|
||||
FirstToSecondSemester,
|
||||
SecondToFirstSemester
|
||||
} StepType;
|
||||
|
||||
TIMETABLE generateTimetable(STUDENT *students, size_t size_students, COURSE *courses, size_t size_courses);
|
||||
|
||||
bool isFlex(char crsNo[MAX_COURSE_NO_LEN]);
|
||||
|
||||
#endif
|
||||
+541
@@ -0,0 +1,541 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
#include "../include/main.h"
|
||||
#include "../include/students.h"
|
||||
#include "../include/courses.h"
|
||||
#include "../include/generator.h"
|
||||
|
||||
const char hex[] = "0123456789abcdefABCDEF";
|
||||
|
||||
// May be needed later
|
||||
bool isFlex(char crsNo[MAX_COURSE_NO_LEN]) {
|
||||
for (uint8_t i = 0; i < sizeof(FLEX)/sizeof(FLEX[0]); i++)
|
||||
if (strcmp(crsNo, FLEX[i]) == 0) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void appendChar(char *str, char ch) {
|
||||
int len = strlen(str);
|
||||
str[len] = ch;
|
||||
str[len + 1] = '\0';
|
||||
}
|
||||
|
||||
// Equally disperses the sum of an array to each index
|
||||
uint8_t *equal(uint8_t *arr, size_t size) {
|
||||
size_t sum = 0;
|
||||
|
||||
// Calculate the sum of the array
|
||||
for (size_t i = 0; i < size; i++)
|
||||
sum += arr[i];
|
||||
|
||||
// Calculate quotient and remainder
|
||||
size_t q = sum / size;
|
||||
size_t r = sum % size;
|
||||
|
||||
// Fill the result array
|
||||
for (size_t i = 0; i < r; i++)
|
||||
arr[i] = q + 1;
|
||||
|
||||
for (size_t i = r; i < size; i++)
|
||||
arr[i] = q;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
int stepIndex(int offset, StepType type, uint8_t blocksPerSemester) {
|
||||
if (type == FirstToSecondSemester)
|
||||
return (offset == 0 || offset == -1 * (blocksPerSemester - 1)) ? blocksPerSemester : (-1 * (blocksPerSemester - 1));
|
||||
|
||||
if (type == SecondToFirstSemester)
|
||||
return (offset == 0 || offset == blocksPerSemester + 1) ? (-1 * blocksPerSemester) : (blocksPerSemester + 1);
|
||||
|
||||
// Should not be possible
|
||||
return -1;
|
||||
}
|
||||
|
||||
TIMETABLE generateTimetable(STUDENT *students, size_t size_students, COURSE *courses, size_t size_courses) {
|
||||
TIMETABLE_BLOCK defaultBlock = {0, {"\0"}};
|
||||
TIMETABLE timetable = {{defaultBlock}, false};
|
||||
|
||||
uint8_t MEDIAN = floor((float) (MIN_REQ + CLASS_CAP) / 2);
|
||||
uint8_t BLOCKS_PER_SEMESTER = TOTAL_BLOCKS / 2;
|
||||
|
||||
|
||||
/*** STEP 1 - Tally requests to check which courses are eligable to run ***/
|
||||
char **activeCourses = malloc(MAX_CLASSES * sizeof(char *));
|
||||
uint16_t *activeCoursesIndexes = malloc(MAX_CLASSES * sizeof(uint16_t));
|
||||
uint16_t activeCoursesLen = 0; // also acts as the length of activeCourses
|
||||
for (size_t i = 0; i < size_students; i++) {
|
||||
for (size_t j = 0; j < students[i].requestsLen; j++) {
|
||||
if (students[i].requests[j].alternate || isFlex(students[i].requests[j].crsNo)) continue;
|
||||
|
||||
for (size_t k = 0; k < size_courses; k++) {
|
||||
if (strcmp(courses[k].crsNo, students[i].requests[j].crsNo) == 0) {
|
||||
courses[k].requests++;
|
||||
if (courses[k].requests >= MIN_REQ) {
|
||||
if (activeCoursesLen == 0) {
|
||||
activeCourses[activeCoursesLen] = malloc(sizeof(char) * MAX_COURSE_NO_LEN);
|
||||
strcpy(activeCourses[activeCoursesLen], courses[k].crsNo);
|
||||
activeCoursesIndexes[activeCoursesLen] = k;
|
||||
activeCoursesLen++;
|
||||
} else {
|
||||
bool exists = false;
|
||||
for (size_t l = 0; l < activeCoursesLen; l++) {
|
||||
if (strcmp(activeCourses[l], courses[k].crsNo) == 0) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
activeCourses[activeCoursesLen] = malloc(sizeof(char) * MAX_COURSE_NO_LEN);
|
||||
strcpy(activeCourses[activeCoursesLen], courses[k].crsNo);
|
||||
activeCoursesIndexes[activeCoursesLen] = k;
|
||||
activeCoursesLen++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*** STEP 2 - Generate classes with no students, but calculate the number of expected students per class ***/
|
||||
|
||||
uint8_t *allClassRunCounts = malloc(activeCoursesLen * sizeof(uint8_t));
|
||||
// max this out to total number of classrooms available between both semesters
|
||||
CLASS *classes = malloc(CLASSROOMS * TOTAL_BLOCKS * sizeof(CLASS));
|
||||
size_t classesLen = 0;
|
||||
for (size_t i = 0; i < activeCoursesLen; i++) {
|
||||
uint16_t index = activeCoursesIndexes[i];
|
||||
uint8_t classRunCount = floor((float) courses[index].requests / MEDIAN);
|
||||
uint8_t remaining = courses[index].requests % MEDIAN;
|
||||
|
||||
// add 1 to classRunCount in case we need to create an extra class with remaining
|
||||
size_t *courseClassIndexes = malloc((classRunCount + 1) * sizeof(size_t));
|
||||
for (size_t j = 0; j < classRunCount; j++) {
|
||||
CLASS newClass;
|
||||
strcpy(newClass.baseCrsNo, courses[index].crsNo);
|
||||
char courseID[MAX_COURSE_ID_LEN];
|
||||
strcpy(courseID, courses[index].crsNo);
|
||||
appendChar(courseID, hex[j]);
|
||||
strcpy(newClass.crsNo, courseID);
|
||||
strcpy(newClass.description, courses[index].description);
|
||||
newClass.numberOfStudents = MEDIAN; // The expected number of students in this class
|
||||
|
||||
classes[classesLen] = newClass;
|
||||
courseClassIndexes[j] = classesLen;
|
||||
classesLen++;
|
||||
}
|
||||
|
||||
//*** Handle remaining requests ***/
|
||||
|
||||
// Can we add remaining requests to existing classes
|
||||
bool remainingFitsInExistingClasses = remaining <= classRunCount * (CLASS_CAP - MEDIAN);
|
||||
|
||||
// Can we create a new class using only remaining requests
|
||||
bool remainingCanCreateNewClass = remaining >= MIN_REQ;
|
||||
|
||||
// Can we create a new class if we borrow students from created classes to add to remaining requests to meet min req
|
||||
bool remainingPlusExtraFromExistingCanCreateNewClass = MIN_REQ - remaining < classRunCount * (MEDIAN - MIN_REQ);
|
||||
|
||||
if (remainingFitsInExistingClasses) {
|
||||
// Simply add remaining to existing classes
|
||||
while (remaining > 0) {
|
||||
for (size_t j = 0; j < classRunCount; j++) {
|
||||
classes[courseClassIndexes[j]].numberOfStudents++;
|
||||
remaining--;
|
||||
if (remaining == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (remainingCanCreateNewClass) {
|
||||
// Create new class
|
||||
CLASS newClass;
|
||||
strcpy(newClass.baseCrsNo, courses[index].crsNo);
|
||||
char courseID[MAX_COURSE_ID_LEN];
|
||||
strcpy(courseID, courses[index].crsNo);
|
||||
appendChar(courseID, hex[classRunCount]);
|
||||
strcpy(newClass.crsNo, courseID);
|
||||
strcpy(newClass.description, courses[index].description);
|
||||
newClass.numberOfStudents = remaining;
|
||||
|
||||
// Insert class into empty classes array and update index
|
||||
classes[classesLen] = newClass;
|
||||
courseClassIndexes[classRunCount] = classesLen;
|
||||
classesLen++;
|
||||
|
||||
// update class run count; if there is more than one class, equalize the class number of students
|
||||
classRunCount++;
|
||||
if (classRunCount >= 2) {
|
||||
uint8_t *numberOfStudentsArr = malloc(classRunCount * sizeof(uint8_t));
|
||||
for (size_t j = 0; j < classRunCount; j++)
|
||||
numberOfStudentsArr[j] = classes[courseClassIndexes[j]].numberOfStudents;
|
||||
|
||||
numberOfStudentsArr = equal(numberOfStudentsArr, classRunCount);
|
||||
for (size_t j = 0; j < classRunCount; j++)
|
||||
classes[courseClassIndexes[j]].numberOfStudents = numberOfStudentsArr[j];
|
||||
|
||||
free(numberOfStudentsArr);
|
||||
}
|
||||
|
||||
} else if (remainingPlusExtraFromExistingCanCreateNewClass) {
|
||||
// Take 1 student from each existing class till min requirement is met
|
||||
while (remaining < MIN_REQ) {
|
||||
for (size_t j = 0; j < classRunCount; j++) {
|
||||
classes[courseClassIndexes[j]].numberOfStudents--;
|
||||
remaining++;
|
||||
if (remaining == MIN_REQ) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new class with remaining
|
||||
CLASS newClass;
|
||||
strcpy(newClass.baseCrsNo, courses[index].crsNo);
|
||||
char courseID[MAX_COURSE_ID_LEN];
|
||||
strcpy(courseID, courses[index].crsNo);
|
||||
appendChar(courseID, hex[classRunCount]);
|
||||
strcpy(newClass.crsNo, courseID);
|
||||
strcpy(newClass.description, courses[index].description);
|
||||
newClass.numberOfStudents = remaining;
|
||||
|
||||
// Insert class into empty classes array and update index
|
||||
classes[classesLen] = newClass;
|
||||
courseClassIndexes[classRunCount] = classesLen;
|
||||
classesLen++;
|
||||
classRunCount++;
|
||||
|
||||
// Equalize the class number of students
|
||||
uint8_t *numberOfStudentsArr = malloc(classRunCount * sizeof(uint8_t));
|
||||
for (size_t j = 0; j < classRunCount; j++)
|
||||
numberOfStudentsArr[j] = classes[courseClassIndexes[j]].numberOfStudents;
|
||||
|
||||
numberOfStudentsArr = equal(numberOfStudentsArr, classRunCount);
|
||||
for (size_t j = 0; j < classRunCount; j++)
|
||||
classes[courseClassIndexes[j]].numberOfStudents = numberOfStudentsArr[j];
|
||||
|
||||
free(numberOfStudentsArr);
|
||||
|
||||
} else {
|
||||
/*
|
||||
If all above cannot handle remaining requests we will add as many of
|
||||
the remaining requests to the existing classes. Any number of requests
|
||||
that dont fit will be ignored so later they can be folded into their
|
||||
alternative choices
|
||||
*/
|
||||
|
||||
bool full = false;
|
||||
while (!full) {
|
||||
for (size_t j = 0; j < classRunCount; j++) {
|
||||
if (classes[courseClassIndexes[classRunCount - 1]].numberOfStudents == CLASS_CAP) {
|
||||
// If the last class in the array is at class_cap, all other classes must be at class cap and we are full
|
||||
full = true;
|
||||
break;
|
||||
}
|
||||
|
||||
classes[courseClassIndexes[j]].numberOfStudents++;
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(courseClassIndexes);
|
||||
allClassRunCounts[i] = classRunCount;
|
||||
}
|
||||
|
||||
// realloc classes to correct size
|
||||
CLASS *tempclasses = malloc(classesLen * sizeof(CLASS));
|
||||
for (size_t i = 0; i < classesLen; i++)
|
||||
tempclasses[i] = classes[i];
|
||||
classes = realloc(classes, classesLen * sizeof(CLASS));
|
||||
memcpy(classes, tempclasses, classesLen * sizeof(CLASS));
|
||||
free(tempclasses);
|
||||
free(activeCoursesIndexes);
|
||||
|
||||
|
||||
/*** STEP 3 - Insert students into empty classes ***/
|
||||
STUDENT *tempStudents = malloc(size_students * sizeof(STUDENT));
|
||||
size_t size_tempStudents = size_students;
|
||||
memcpy(tempStudents, students, size_students * sizeof(STUDENT));
|
||||
|
||||
uint8_t *currentInserted = malloc(classesLen * sizeof(uint8_t));
|
||||
for (size_t i = 0; i < classesLen; i++)
|
||||
currentInserted[i] = 0;
|
||||
|
||||
while (size_tempStudents > 0) {
|
||||
// Choose student at random, to prevent success bias to students first in the array
|
||||
STUDENT student = tempStudents[rand() % size_tempStudents];
|
||||
|
||||
// Create an array of students alternates
|
||||
size_t numberOfAlts = 0;
|
||||
for (size_t i = 0; i < student.requestsLen; i++)
|
||||
if (student.requests[i].alternate)
|
||||
numberOfAlts++;
|
||||
|
||||
REQUEST *alternates = malloc(numberOfAlts * sizeof(REQUEST));
|
||||
size_t alternateIdx = 0;
|
||||
for (size_t i = 0; i < student.requestsLen; i++) {
|
||||
if (student.requests[i].alternate) {
|
||||
alternates[alternateIdx] = student.requests[i];
|
||||
alternateIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
// Search existing classes to insert student based off request
|
||||
for (size_t i = 0; i < student.requestsLen; i++) {
|
||||
if (student.requests[i].alternate) continue; // Ignore alternates
|
||||
|
||||
char course[MAX_COURSE_NO_LEN] = {"\0"};
|
||||
strcpy(course, student.requests[i].crsNo);
|
||||
bool getAvailableCourse = true;
|
||||
bool isAlt = false;
|
||||
|
||||
while (getAvailableCourse) {
|
||||
for (size_t j = 0; j < classesLen; j++) {
|
||||
// Class exists in classes
|
||||
if (strcmp(classes[j].baseCrsNo, course) == 0) {
|
||||
// If this is an alternate, and there is room to expand, increase number of students to allow extra
|
||||
if (isAlt && classes[j].numberOfStudents < CLASS_CAP)
|
||||
classes[j].numberOfStudents++;
|
||||
|
||||
// Class exists with room for student
|
||||
if (currentInserted[j] < classes[j].numberOfStudents) {
|
||||
classes[j].students[currentInserted[j]] = student.pupilNum;
|
||||
currentInserted[j]++;
|
||||
getAvailableCourse = false;
|
||||
break;
|
||||
|
||||
} else if (currentInserted[j] == classes[j].numberOfStudents) {
|
||||
// If class is full, and there's no more classes available for that course, convert to alt
|
||||
if (j == classesLen - 1 || (j != classesLen - 1 && strcmp(classes[j + 1].baseCrsNo, course) != 0)) {
|
||||
if (numberOfAlts > 0) {
|
||||
// Use alternate
|
||||
strcpy(course, alternates[0].crsNo); // asign alternate to course and retry
|
||||
|
||||
// remove alternate from array of alts to retry same alt over and over
|
||||
REQUEST *tempAlternates = malloc(numberOfAlts * sizeof(REQUEST));
|
||||
memcpy(tempAlternates, alternates, numberOfAlts * sizeof(REQUEST));
|
||||
numberOfAlts--;
|
||||
|
||||
alternates = realloc(alternates, numberOfAlts * sizeof(REQUEST));
|
||||
for (size_t k = 1; k <= numberOfAlts; k++)
|
||||
alternates[k - 1] = tempAlternates[k];
|
||||
free(tempAlternates);
|
||||
|
||||
isAlt = true;
|
||||
break;
|
||||
|
||||
} else {
|
||||
// Force break the loop, ignore as it cannot be resolved
|
||||
// Allow administrator to handle error manually
|
||||
getAvailableCourse = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// this class does not exist, i.e not enough requests
|
||||
} else if (j == classesLen - 1) {
|
||||
if (numberOfAlts > 0) {
|
||||
// Use alternate
|
||||
strcpy(course, alternates[0].crsNo); // asign alternate to course and retry
|
||||
// remove alternate from array of alts to retry same alt over and over
|
||||
REQUEST *tempAlternates = malloc(numberOfAlts * sizeof(REQUEST));
|
||||
memcpy(tempAlternates, alternates, numberOfAlts * sizeof(REQUEST));
|
||||
numberOfAlts--;
|
||||
alternates = realloc(alternates, numberOfAlts * sizeof(REQUEST));
|
||||
for (size_t k = 1; k <= numberOfAlts; k++)
|
||||
alternates[k - 1] = tempAlternates[k];
|
||||
free(tempAlternates);
|
||||
isAlt = true;
|
||||
break;
|
||||
} else {
|
||||
// Force break the loop, ignore as it cannot be resolved
|
||||
// Allow administrator to handle error manually
|
||||
getAvailableCourse = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Asign remaining alternates to student
|
||||
for (size_t i = 0; i < size_students; i++) {
|
||||
if (students[i].pupilNum == student.pupilNum) {
|
||||
students[i].remainingAlts = realloc(students[i].remainingAlts, numberOfAlts * sizeof(REQUEST));
|
||||
memcpy(students[i].remainingAlts, alternates, numberOfAlts * sizeof(REQUEST));
|
||||
students[i].remainingAltsLen = numberOfAlts;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(alternates);
|
||||
|
||||
// realloc tempStudents to be size_tempStudents - 1 without struct of student just processed
|
||||
size_tempStudents--;
|
||||
STUDENT *new_tempStudents = malloc(size_tempStudents * sizeof(STUDENT));
|
||||
size_t idx = 0;
|
||||
for (size_t i = 0; i <= size_tempStudents; i++) {
|
||||
if (tempStudents[i].pupilNum != student.pupilNum) {
|
||||
new_tempStudents[idx] = tempStudents[i];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
tempStudents = realloc(tempStudents, size_tempStudents * sizeof(STUDENT));
|
||||
memcpy(tempStudents, new_tempStudents, size_tempStudents * sizeof(STUDENT));
|
||||
free(new_tempStudents);
|
||||
}
|
||||
|
||||
free(currentInserted);
|
||||
free(tempStudents);
|
||||
|
||||
|
||||
/*** STEP 4 - Insert classes into timetable ***/
|
||||
while (activeCoursesLen > 0) {
|
||||
// Find highest resource class (most times run)
|
||||
size_t index = 0;
|
||||
uint8_t max = 0;
|
||||
for (size_t i = 0; i < activeCoursesLen; i++) {
|
||||
if (allClassRunCounts[i] > max) {
|
||||
max = allClassRunCounts[i];
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Tally first semester and second semester
|
||||
uint8_t allSemesterBlockLens[TOTAL_BLOCKS] = {0};
|
||||
for (uint8_t i = 0; i < TOTAL_BLOCKS; i++)
|
||||
allSemesterBlockLens[i] = timetable.timetable[i].numberOfClasses;
|
||||
|
||||
// If there is more than one class running
|
||||
if (allClassRunCounts[index] > 1) {
|
||||
// Get index of block with least class run counts
|
||||
uint8_t minClassrooms = CLASSROOMS;
|
||||
uint8_t blockIndex = 0;
|
||||
for (uint8_t i = 0; i < TOTAL_BLOCKS; i++) {
|
||||
if (allSemesterBlockLens[i] < minClassrooms) {
|
||||
minClassrooms = allSemesterBlockLens[i];
|
||||
blockIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
StepType step = blockIndex < BLOCKS_PER_SEMESTER ? FirstToSecondSemester : SecondToFirstSemester;
|
||||
int offset = 0;
|
||||
|
||||
// Disperse classes throughout both semesters
|
||||
// Find base class ID index
|
||||
size_t indexOffset = 0;
|
||||
size_t baseIndex = 0;
|
||||
for (size_t i = 0; i < classesLen; i++) {
|
||||
if (strcmp(classes[i].baseCrsNo, activeCourses[index]) == 0) {
|
||||
baseIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint8_t classRunCounts = allClassRunCounts[index];
|
||||
for (size_t i = 0; i < classRunCounts; i++) {
|
||||
// Get class ID
|
||||
char className[MAX_COURSE_ID_LEN] = {"\0"};
|
||||
strcpy(className, classes[baseIndex + indexOffset].crsNo);
|
||||
|
||||
bool classInserted = false;
|
||||
while (!classInserted) {
|
||||
blockIndex += offset;
|
||||
|
||||
// Insert class
|
||||
if (timetable.timetable[blockIndex].numberOfClasses < CLASSROOMS) {
|
||||
uint8_t classIndex = timetable.timetable[blockIndex].numberOfClasses;
|
||||
strcpy(timetable.timetable[blockIndex].classes[classIndex], className);
|
||||
timetable.timetable[blockIndex].numberOfClasses++;
|
||||
allClassRunCounts[index]--;
|
||||
indexOffset++;
|
||||
classInserted = true;
|
||||
}
|
||||
|
||||
// Update offset and to get index of block for next semester
|
||||
offset = stepIndex(offset, step, BLOCKS_PER_SEMESTER);
|
||||
if (blockIndex >= (TOTAL_BLOCKS - 1)) {
|
||||
blockIndex = step == FirstToSecondSemester ? 0 : BLOCKS_PER_SEMESTER;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the class only runs once, place in semester with least classes
|
||||
} else if (allClassRunCounts[index] == 1) {
|
||||
// Get index of block with least class run counts
|
||||
uint8_t minClassrooms = CLASSROOMS;
|
||||
uint8_t blockIndex = 0;
|
||||
for (uint8_t i = 0; i < TOTAL_BLOCKS; i++) {
|
||||
if (allSemesterBlockLens[i] < minClassrooms) {
|
||||
minClassrooms = allSemesterBlockLens[i];
|
||||
blockIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Get class ID index
|
||||
char className[MAX_COURSE_ID_LEN] = {"\0"};
|
||||
size_t baseIndex = 0;
|
||||
for (size_t i = 0; i < classesLen; i++) {
|
||||
if (strcmp(classes[i].baseCrsNo, activeCourses[index]) == 0) {
|
||||
baseIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert class
|
||||
strcpy(className, classes[baseIndex].crsNo);
|
||||
uint8_t classIndex = timetable.timetable[blockIndex].numberOfClasses;
|
||||
strcpy(timetable.timetable[blockIndex].classes[classIndex], className);
|
||||
timetable.timetable[blockIndex].numberOfClasses++;
|
||||
allClassRunCounts[index]--;
|
||||
}
|
||||
|
||||
// Now that the classRunCount is 0 since all classes have been inserted into the timetable
|
||||
// delete this course as it has been handled
|
||||
activeCoursesLen--;
|
||||
|
||||
// create temp arrays of data
|
||||
uint8_t *tempAllClassRunCounts = malloc(activeCoursesLen * sizeof(uint8_t));
|
||||
char **tempActiveCourses = malloc(activeCoursesLen * sizeof(char*));
|
||||
|
||||
// Copy data without the course we just handled
|
||||
size_t tempIndex = 0;
|
||||
for (size_t i = 0; i <= activeCoursesLen; i++) {
|
||||
if (activeCourses[i] == activeCourses[index]) {
|
||||
free(activeCourses[i]);
|
||||
continue;
|
||||
};
|
||||
|
||||
tempAllClassRunCounts[tempIndex] = allClassRunCounts[i];
|
||||
tempActiveCourses[tempIndex] = activeCourses[i];
|
||||
tempIndex++;
|
||||
}
|
||||
|
||||
// Reallocate the arrays of course data to be 1 less in size and copy temp data back to them, then free temp data
|
||||
allClassRunCounts = realloc(allClassRunCounts, activeCoursesLen * sizeof(uint8_t));
|
||||
activeCourses = realloc(activeCourses, activeCoursesLen * sizeof(char*));
|
||||
memcpy(allClassRunCounts, tempAllClassRunCounts, activeCoursesLen * sizeof(uint8_t));
|
||||
memcpy(activeCourses, tempActiveCourses, activeCoursesLen * sizeof(char*));
|
||||
free(tempAllClassRunCounts);
|
||||
free(tempActiveCourses);
|
||||
}
|
||||
|
||||
|
||||
// STEP 5 - something
|
||||
|
||||
|
||||
// STEP 6 - most complex something
|
||||
|
||||
|
||||
free(classes);
|
||||
free(allClassRunCounts);
|
||||
free(activeCourses);
|
||||
|
||||
return timetable;
|
||||
}
|
||||
+1
-540
@@ -9,546 +9,7 @@
|
||||
#include "../include/students.h"
|
||||
#include "../include/courses.h"
|
||||
#include "../include/json.h"
|
||||
|
||||
const char FLEX[2][11] = {"XAT--12A-S", "XAT--12B-S"};
|
||||
|
||||
typedef struct {
|
||||
uint8_t numberOfClasses;
|
||||
char classes[CLASSROOMS][MAX_COURSE_ID_LEN];
|
||||
} TIMETABLE_BLOCK;
|
||||
|
||||
typedef struct {
|
||||
TIMETABLE_BLOCK timetable[TOTAL_BLOCKS];
|
||||
bool success;
|
||||
} TIMETABLE;
|
||||
|
||||
void appendChar(char *str, char ch) {
|
||||
int len = strlen(str);
|
||||
str[len] = ch;
|
||||
str[len + 1] = '\0';
|
||||
}
|
||||
|
||||
// Equally disperses the sum of an array to each index
|
||||
uint8_t *equal(uint8_t *arr, size_t size) {
|
||||
size_t sum = 0;
|
||||
|
||||
// Calculate the sum of the array
|
||||
for (size_t i = 0; i < size; i++)
|
||||
sum += arr[i];
|
||||
|
||||
// Calculate quotient and remainder
|
||||
size_t q = sum / size;
|
||||
size_t r = sum % size;
|
||||
|
||||
// Fill the result array
|
||||
for (size_t i = 0; i < r; i++)
|
||||
arr[i] = q + 1;
|
||||
|
||||
for (size_t i = r; i < size; i++)
|
||||
arr[i] = q;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
FirstToSecondSemester,
|
||||
SecondToFirstSemester
|
||||
} StepType;
|
||||
|
||||
int stepIndex(int offset, StepType type, uint8_t blocksPerSemester) {
|
||||
if (type == FirstToSecondSemester)
|
||||
return (offset == 0 || offset == -1 * (blocksPerSemester - 1)) ? blocksPerSemester : (-1 * (blocksPerSemester - 1));
|
||||
|
||||
if (type == SecondToFirstSemester)
|
||||
return (offset == 0 || offset == blocksPerSemester + 1) ? (-1 * blocksPerSemester) : (blocksPerSemester + 1);
|
||||
|
||||
// Should not be possible
|
||||
return -1;
|
||||
}
|
||||
|
||||
TIMETABLE generateTimetable(STUDENT *students, size_t size_students, COURSE *courses, size_t size_courses) {
|
||||
TIMETABLE_BLOCK defaultBlock = {0, {"\0"}};
|
||||
TIMETABLE timetable = {{defaultBlock}, false};
|
||||
|
||||
uint8_t MEDIAN = floor((float) (MIN_REQ + CLASS_CAP) / 2);
|
||||
uint8_t BLOCKS_PER_SEMESTER = TOTAL_BLOCKS / 2;
|
||||
|
||||
|
||||
/*** STEP 1 - Tally requests to check which courses are eligable to run ***/
|
||||
char **activeCourses = malloc(MAX_CLASSES * sizeof(char *));
|
||||
uint16_t *activeCoursesIndexes = malloc(MAX_CLASSES * sizeof(uint16_t));
|
||||
uint16_t activeCoursesLen = 0; // also acts as the length of activeCourses
|
||||
for (size_t i = 0; i < size_students; i++) {
|
||||
for (size_t j = 0; j < students[i].requestsLen; j++) {
|
||||
if (students[i].requests[j].alternate) continue;
|
||||
if (strcmp(students[i].requests[j].crsNo, FLEX[0]) == 0) continue;
|
||||
if (strcmp(students[i].requests[j].crsNo, FLEX[1]) == 0) continue;
|
||||
|
||||
for (size_t k = 0; k < size_courses; k++) {
|
||||
if (strcmp(courses[k].crsNo, students[i].requests[j].crsNo) == 0) {
|
||||
courses[k].requests++;
|
||||
if (courses[k].requests >= MIN_REQ) {
|
||||
if (activeCoursesLen == 0) {
|
||||
activeCourses[activeCoursesLen] = malloc(sizeof(char) * MAX_COURSE_NO_LEN);
|
||||
strcpy(activeCourses[activeCoursesLen], courses[k].crsNo);
|
||||
activeCoursesIndexes[activeCoursesLen] = k;
|
||||
activeCoursesLen++;
|
||||
} else {
|
||||
bool exists = false;
|
||||
for (size_t l = 0; l < activeCoursesLen; l++) {
|
||||
if (strcmp(activeCourses[l], courses[k].crsNo) == 0) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
activeCourses[activeCoursesLen] = malloc(sizeof(char) * MAX_COURSE_NO_LEN);
|
||||
strcpy(activeCourses[activeCoursesLen], courses[k].crsNo);
|
||||
activeCoursesIndexes[activeCoursesLen] = k;
|
||||
activeCoursesLen++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*** STEP 2 - Generate classes with no students, but calculate the number of expected students per class ***/
|
||||
|
||||
char hex[] = "0123456789abcdefABCDEF";
|
||||
|
||||
uint8_t *allClassRunCounts = malloc(activeCoursesLen * sizeof(uint8_t));
|
||||
// max this out to total number of classrooms available between both semesters
|
||||
CLASS *classes = malloc(CLASSROOMS * TOTAL_BLOCKS * sizeof(CLASS));
|
||||
size_t classesLen = 0;
|
||||
for (size_t i = 0; i < activeCoursesLen; i++) {
|
||||
uint16_t index = activeCoursesIndexes[i];
|
||||
uint8_t classRunCount = floor((float) courses[index].requests / MEDIAN);
|
||||
uint8_t remaining = courses[index].requests % MEDIAN;
|
||||
|
||||
// add 1 to classRunCount in case we need to create an extra class with remaining
|
||||
size_t *courseClassIndexes = malloc((classRunCount + 1) * sizeof(size_t));
|
||||
for (size_t j = 0; j < classRunCount; j++) {
|
||||
CLASS newClass;
|
||||
strcpy(newClass.baseCrsNo, courses[index].crsNo);
|
||||
char courseID[MAX_COURSE_ID_LEN];
|
||||
strcpy(courseID, courses[index].crsNo);
|
||||
appendChar(courseID, hex[j]);
|
||||
strcpy(newClass.crsNo, courseID);
|
||||
strcpy(newClass.description, courses[index].description);
|
||||
newClass.numberOfStudents = MEDIAN; // The expected number of students in this class
|
||||
|
||||
classes[classesLen] = newClass;
|
||||
courseClassIndexes[j] = classesLen;
|
||||
classesLen++;
|
||||
}
|
||||
|
||||
//*** Handle remaining requests ***/
|
||||
|
||||
// Can we add remaining requests to existing classes
|
||||
bool remainingFitsInExistingClasses = remaining <= classRunCount * (CLASS_CAP - MEDIAN);
|
||||
|
||||
// Can we create a new class using only remaining requests
|
||||
bool remainingCanCreateNewClass = remaining >= MIN_REQ;
|
||||
|
||||
// Can we create a new class if we borrow students from created classes to add to remaining requests to meet min req
|
||||
bool remainingPlusExtraFromExistingCanCreateNewClass = MIN_REQ - remaining < classRunCount * (MEDIAN - MIN_REQ);
|
||||
|
||||
if (remainingFitsInExistingClasses) {
|
||||
// Simply add remaining to existing classes
|
||||
while (remaining > 0) {
|
||||
for (size_t j = 0; j < classRunCount; j++) {
|
||||
classes[courseClassIndexes[j]].numberOfStudents++;
|
||||
remaining--;
|
||||
if (remaining == 0) break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (remainingCanCreateNewClass) {
|
||||
// Create new class
|
||||
CLASS newClass;
|
||||
strcpy(newClass.baseCrsNo, courses[index].crsNo);
|
||||
char courseID[MAX_COURSE_ID_LEN];
|
||||
strcpy(courseID, courses[index].crsNo);
|
||||
appendChar(courseID, hex[classRunCount]);
|
||||
strcpy(newClass.crsNo, courseID);
|
||||
strcpy(newClass.description, courses[index].description);
|
||||
newClass.numberOfStudents = remaining;
|
||||
|
||||
// Insert class into empty classes array and update index
|
||||
classes[classesLen] = newClass;
|
||||
courseClassIndexes[classRunCount] = classesLen;
|
||||
classesLen++;
|
||||
|
||||
// update class run count; if there is more than one class, equalize the class number of students
|
||||
classRunCount++;
|
||||
if (classRunCount >= 2) {
|
||||
uint8_t *numberOfStudentsArr = malloc(classRunCount * sizeof(uint8_t));
|
||||
for (size_t j = 0; j < classRunCount; j++)
|
||||
numberOfStudentsArr[j] = classes[courseClassIndexes[j]].numberOfStudents;
|
||||
|
||||
numberOfStudentsArr = equal(numberOfStudentsArr, classRunCount);
|
||||
for (size_t j = 0; j < classRunCount; j++)
|
||||
classes[courseClassIndexes[j]].numberOfStudents = numberOfStudentsArr[j];
|
||||
|
||||
free(numberOfStudentsArr);
|
||||
}
|
||||
|
||||
} else if (remainingPlusExtraFromExistingCanCreateNewClass) {
|
||||
// Take 1 student from each existing class till min requirement is met
|
||||
while (remaining < MIN_REQ) {
|
||||
for (size_t j = 0; j < classRunCount; j++) {
|
||||
classes[courseClassIndexes[j]].numberOfStudents--;
|
||||
remaining++;
|
||||
if (remaining == MIN_REQ) break;
|
||||
}
|
||||
}
|
||||
|
||||
// Create new class with remaining
|
||||
CLASS newClass;
|
||||
strcpy(newClass.baseCrsNo, courses[index].crsNo);
|
||||
char courseID[MAX_COURSE_ID_LEN];
|
||||
strcpy(courseID, courses[index].crsNo);
|
||||
appendChar(courseID, hex[classRunCount]);
|
||||
strcpy(newClass.crsNo, courseID);
|
||||
strcpy(newClass.description, courses[index].description);
|
||||
newClass.numberOfStudents = remaining;
|
||||
|
||||
// Insert class into empty classes array and update index
|
||||
classes[classesLen] = newClass;
|
||||
courseClassIndexes[classRunCount] = classesLen;
|
||||
classesLen++;
|
||||
classRunCount++;
|
||||
|
||||
// Equalize the class number of students
|
||||
uint8_t *numberOfStudentsArr = malloc(classRunCount * sizeof(uint8_t));
|
||||
for (size_t j = 0; j < classRunCount; j++)
|
||||
numberOfStudentsArr[j] = classes[courseClassIndexes[j]].numberOfStudents;
|
||||
|
||||
numberOfStudentsArr = equal(numberOfStudentsArr, classRunCount);
|
||||
for (size_t j = 0; j < classRunCount; j++)
|
||||
classes[courseClassIndexes[j]].numberOfStudents = numberOfStudentsArr[j];
|
||||
|
||||
free(numberOfStudentsArr);
|
||||
|
||||
} else {
|
||||
/*
|
||||
If all above cannot handle remaining requests we will add as many of
|
||||
the remaining requests to the existing classes. Any number of requests
|
||||
that dont fit will be ignored so later they can be folded into their
|
||||
alternative choices
|
||||
*/
|
||||
|
||||
bool full = false;
|
||||
while (!full) {
|
||||
for (size_t j = 0; j < classRunCount; j++) {
|
||||
if (classes[courseClassIndexes[classRunCount - 1]].numberOfStudents == CLASS_CAP) {
|
||||
// If the last class in the array is at class_cap, all other classes must be at class cap and we are full
|
||||
full = true;
|
||||
break;
|
||||
}
|
||||
|
||||
classes[courseClassIndexes[j]].numberOfStudents++;
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(courseClassIndexes);
|
||||
allClassRunCounts[i] = classRunCount;
|
||||
}
|
||||
|
||||
// realloc classes to correct size
|
||||
CLASS *tempclasses = malloc(classesLen * sizeof(CLASS));
|
||||
for (size_t i = 0; i < classesLen; i++)
|
||||
tempclasses[i] = classes[i];
|
||||
classes = realloc(classes, classesLen * sizeof(CLASS));
|
||||
memcpy(classes, tempclasses, classesLen * sizeof(CLASS));
|
||||
free(tempclasses);
|
||||
free(activeCoursesIndexes);
|
||||
|
||||
|
||||
/*** STEP 3 - Insert students into empty classes ... yikes ***/
|
||||
STUDENT *tempStudents = malloc(size_students * sizeof(STUDENT));
|
||||
size_t size_tempStudents = size_students;
|
||||
memcpy(tempStudents, students, size_students * sizeof(STUDENT));
|
||||
|
||||
uint8_t *currentInserted = malloc(classesLen * sizeof(uint8_t));
|
||||
for (size_t i = 0; i < classesLen; i++)
|
||||
currentInserted[i] = 0;
|
||||
|
||||
while (size_tempStudents > 0) {
|
||||
// Choose student at random, to prevent success bias to students first in the array
|
||||
STUDENT student = tempStudents[rand() % size_tempStudents];
|
||||
|
||||
// Create an array of students alternates
|
||||
size_t numberOfAlts = 0;
|
||||
for (size_t i = 0; i < student.requestsLen; i++)
|
||||
if (student.requests[i].alternate)
|
||||
numberOfAlts++;
|
||||
|
||||
REQUEST *alternates = malloc(numberOfAlts * sizeof(REQUEST));
|
||||
size_t alternateIdx = 0;
|
||||
for (size_t i = 0; i < student.requestsLen; i++) {
|
||||
if (student.requests[i].alternate) {
|
||||
alternates[alternateIdx] = student.requests[i];
|
||||
alternateIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
// Search existing classes to insert student based off request
|
||||
for (size_t i = 0; i < student.requestsLen; i++) {
|
||||
if (student.requests[i].alternate) continue; // Ignore alternates
|
||||
char course[MAX_COURSE_NO_LEN] = {"\0"};
|
||||
strcpy(course, student.requests[i].crsNo);
|
||||
bool getAvailableCourse = true;
|
||||
bool isAlt = false;
|
||||
while (getAvailableCourse) {
|
||||
for (size_t j = 0; j < classesLen; j++) {
|
||||
// Class exists in classes
|
||||
if (strcmp(classes[j].baseCrsNo, course) == 0) {
|
||||
// If this is an alternate, and there is room to expand, increase number of students to allow extra
|
||||
if (isAlt && classes[j].numberOfStudents < CLASS_CAP)
|
||||
classes[j].numberOfStudents++;
|
||||
|
||||
// Class exists with room for student
|
||||
if (currentInserted[j] < classes[j].numberOfStudents) {
|
||||
classes[j].students[currentInserted[j]] = student.pupilNum;
|
||||
currentInserted[j]++;
|
||||
getAvailableCourse = false;
|
||||
break;
|
||||
|
||||
} else if (currentInserted[j] == classes[j].numberOfStudents) {
|
||||
// If class is full, and there's no more classes available for that course, convert to alt
|
||||
if (j == classesLen - 1 || (j != classesLen - 1 && strcmp(classes[j + 1].baseCrsNo, course) != 0)) {
|
||||
if (numberOfAlts > 0) {
|
||||
// Use alternate
|
||||
strcpy(course, alternates[0].crsNo); // asign alternate to course and retry
|
||||
// remove alternate from array of alts to retry same alt over and over
|
||||
REQUEST *tempAlternates = malloc(numberOfAlts * sizeof(REQUEST));
|
||||
memcpy(tempAlternates, alternates, numberOfAlts * sizeof(REQUEST));
|
||||
numberOfAlts--;
|
||||
alternates = realloc(alternates, numberOfAlts * sizeof(REQUEST));
|
||||
for (size_t k = 1; k <= numberOfAlts; k++)
|
||||
alternates[k - 1] = tempAlternates[k];
|
||||
free(tempAlternates);
|
||||
isAlt = true;
|
||||
break;
|
||||
} else {
|
||||
// Force break the loop, ignore as it cannot be resolved
|
||||
// Allow administrator to handle error manually
|
||||
getAvailableCourse = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// this class does not exist, i.e not enough requests
|
||||
} else if (j == classesLen - 1) {
|
||||
if (numberOfAlts > 0) {
|
||||
// Use alternate
|
||||
strcpy(course, alternates[0].crsNo); // asign alternate to course and retry
|
||||
// remove alternate from array of alts to retry same alt over and over
|
||||
REQUEST *tempAlternates = malloc(numberOfAlts * sizeof(REQUEST));
|
||||
memcpy(tempAlternates, alternates, numberOfAlts * sizeof(REQUEST));
|
||||
numberOfAlts--;
|
||||
alternates = realloc(alternates, numberOfAlts * sizeof(REQUEST));
|
||||
for (size_t k = 1; k <= numberOfAlts; k++)
|
||||
alternates[k - 1] = tempAlternates[k];
|
||||
free(tempAlternates);
|
||||
isAlt = true;
|
||||
break;
|
||||
} else {
|
||||
// Force break the loop, ignore as it cannot be resolved
|
||||
// Allow administrator to handle error manually
|
||||
getAvailableCourse = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Asign remaining alternates to student
|
||||
for (size_t i = 0; i < size_students; i++) {
|
||||
if (students[i].pupilNum == student.pupilNum) {
|
||||
students[i].remainingAlts = realloc(students[i].remainingAlts, numberOfAlts * sizeof(REQUEST));
|
||||
memcpy(students[i].remainingAlts, alternates, numberOfAlts * sizeof(REQUEST));
|
||||
students[i].remainingAltsLen = numberOfAlts;
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(alternates);
|
||||
|
||||
// realloc tempStudents to be size_tempStudents - 1 without struct of student just processed
|
||||
size_tempStudents--;
|
||||
STUDENT *new_tempStudents = malloc(size_tempStudents * sizeof(STUDENT));
|
||||
size_t idx = 0;
|
||||
for (size_t i = 0; i <= size_tempStudents; i++) {
|
||||
if (tempStudents[i].pupilNum != student.pupilNum) {
|
||||
new_tempStudents[idx] = tempStudents[i];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
tempStudents = realloc(tempStudents, size_tempStudents * sizeof(STUDENT));
|
||||
memcpy(tempStudents, new_tempStudents, size_tempStudents * sizeof(STUDENT));
|
||||
free(new_tempStudents);
|
||||
}
|
||||
|
||||
free(currentInserted);
|
||||
free(tempStudents);
|
||||
|
||||
|
||||
/*** STEP 4 - Insert classes into timetable ***/
|
||||
while (activeCoursesLen > 0) {
|
||||
// Find highest resource class (most times run)
|
||||
size_t index = 0;
|
||||
uint8_t max = 0;
|
||||
for (size_t i = 0; i < activeCoursesLen; i++) {
|
||||
if (allClassRunCounts[i] > max) {
|
||||
max = allClassRunCounts[i];
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Tally first semester and second semester
|
||||
uint8_t allSemesterBlockLens[TOTAL_BLOCKS] = {0};
|
||||
for (uint8_t i = 0; i < TOTAL_BLOCKS; i++)
|
||||
allSemesterBlockLens[i] = timetable.timetable[i].numberOfClasses;
|
||||
|
||||
// If there is more than one class running
|
||||
if (allClassRunCounts[index] > 1) {
|
||||
// Get index of block with least class run counts
|
||||
uint8_t minClassrooms = CLASSROOMS;
|
||||
uint8_t blockIndex = 0;
|
||||
for (uint8_t i = 0; i < TOTAL_BLOCKS; i++) {
|
||||
if (allSemesterBlockLens[i] < minClassrooms) {
|
||||
minClassrooms = allSemesterBlockLens[i];
|
||||
blockIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
StepType step = blockIndex < BLOCKS_PER_SEMESTER ? FirstToSecondSemester : SecondToFirstSemester;
|
||||
int offset = 0;
|
||||
|
||||
// Disperse classes throughout both semesters
|
||||
// Find base class ID index
|
||||
size_t indexOffset = 0;
|
||||
size_t baseIndex = 0;
|
||||
for (size_t i = 0; i < classesLen; i++) {
|
||||
if (strcmp(classes[i].baseCrsNo, activeCourses[index]) == 0) {
|
||||
baseIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint8_t classRunCounts = allClassRunCounts[index];
|
||||
for (size_t i = 0; i < classRunCounts; i++) {
|
||||
// Get class ID
|
||||
char className[MAX_COURSE_ID_LEN] = {"\0"};
|
||||
strcpy(className, classes[baseIndex + indexOffset].crsNo);
|
||||
|
||||
bool classInserted = false;
|
||||
while (!classInserted) {
|
||||
blockIndex += offset;
|
||||
|
||||
// Insert class
|
||||
if (timetable.timetable[blockIndex].numberOfClasses < CLASSROOMS) {
|
||||
uint8_t classIndex = timetable.timetable[blockIndex].numberOfClasses;
|
||||
strcpy(timetable.timetable[blockIndex].classes[classIndex], className);
|
||||
timetable.timetable[blockIndex].numberOfClasses++;
|
||||
allClassRunCounts[index]--;
|
||||
indexOffset++;
|
||||
classInserted = true;
|
||||
}
|
||||
|
||||
// Update offset and to get index of block for next semester
|
||||
offset = stepIndex(offset, step, BLOCKS_PER_SEMESTER);
|
||||
if (blockIndex >= (TOTAL_BLOCKS - 1)) {
|
||||
blockIndex = step == FirstToSecondSemester ? 0 : BLOCKS_PER_SEMESTER;
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the class only runs once, place in semester with least classes
|
||||
} else if (allClassRunCounts[index] == 1) {
|
||||
// Get index of block with least class run counts
|
||||
uint8_t minClassrooms = CLASSROOMS;
|
||||
uint8_t blockIndex = 0;
|
||||
for (uint8_t i = 0; i < TOTAL_BLOCKS; i++) {
|
||||
if (allSemesterBlockLens[i] < minClassrooms) {
|
||||
minClassrooms = allSemesterBlockLens[i];
|
||||
blockIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Get class ID index
|
||||
char className[MAX_COURSE_ID_LEN] = {"\0"};
|
||||
size_t baseIndex = 0;
|
||||
for (size_t i = 0; i < classesLen; i++) {
|
||||
if (strcmp(classes[i].baseCrsNo, activeCourses[index]) == 0) {
|
||||
baseIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert class
|
||||
strcpy(className, classes[baseIndex].crsNo);
|
||||
uint8_t classIndex = timetable.timetable[blockIndex].numberOfClasses;
|
||||
strcpy(timetable.timetable[blockIndex].classes[classIndex], className);
|
||||
timetable.timetable[blockIndex].numberOfClasses++;
|
||||
allClassRunCounts[index]--;
|
||||
}
|
||||
|
||||
// Now that the classRunCount is 0 since all classes have been inserted into the timetable
|
||||
// delete this course as it has been handled
|
||||
activeCoursesLen--;
|
||||
|
||||
// create temp arrays of data
|
||||
uint8_t *tempAllClassRunCounts = malloc(activeCoursesLen * sizeof(uint8_t));
|
||||
char **tempActiveCourses = malloc(activeCoursesLen * sizeof(char*));
|
||||
|
||||
// Copy data without the course we just handled
|
||||
size_t tempIndex = 0;
|
||||
for (size_t i = 0; i <= activeCoursesLen; i++) {
|
||||
if (activeCourses[i] == activeCourses[index]) {
|
||||
free(activeCourses[i]);
|
||||
continue;
|
||||
};
|
||||
|
||||
tempAllClassRunCounts[tempIndex] = allClassRunCounts[i];
|
||||
tempActiveCourses[tempIndex] = activeCourses[i];
|
||||
tempIndex++;
|
||||
}
|
||||
|
||||
// Reallocate the arrays of course data to be 1 less in size and copy temp data back to them, then free temp data
|
||||
allClassRunCounts = realloc(allClassRunCounts, activeCoursesLen * sizeof(uint8_t));
|
||||
activeCourses = realloc(activeCourses, activeCoursesLen * sizeof(char*));
|
||||
memcpy(allClassRunCounts, tempAllClassRunCounts, activeCoursesLen * sizeof(uint8_t));
|
||||
memcpy(activeCourses, tempActiveCourses, activeCoursesLen * sizeof(char*));
|
||||
free(tempAllClassRunCounts);
|
||||
free(tempActiveCourses);
|
||||
}
|
||||
|
||||
|
||||
// STEP 5 - something
|
||||
|
||||
|
||||
// STEP 6 - most complex something
|
||||
|
||||
|
||||
free(classes);
|
||||
free(allClassRunCounts);
|
||||
free(activeCourses);
|
||||
|
||||
return timetable;
|
||||
}
|
||||
|
||||
/*** MAIN ***/
|
||||
#include "../include/generator.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
/*
|
||||
|
||||
Reference in new issue
Block a user