begin step 2
This commit is contained in:
1 file changed
+146
-11
@@ -23,7 +23,7 @@
|
|||||||
#define MAX_PUPIL_NUM_LEN 8
|
#define MAX_PUPIL_NUM_LEN 8
|
||||||
#define MAX_COURSE_DES_LEN 50
|
#define MAX_COURSE_DES_LEN 50
|
||||||
#define MAX_COURSE_NO_LEN 21
|
#define MAX_COURSE_NO_LEN 21
|
||||||
#define MAX_COURSE_ID_LEN MAX_COURSE_NO_LEN + 4 // 4 includes the underscore and the 3 digit unique number to identify the course
|
#define MAX_COURSE_ID_LEN MAX_COURSE_NO_LEN + 3 // 3 includes the underscore and the 2 digit unique number to identify the course
|
||||||
|
|
||||||
#define TOTAL_BLOCKS 10 // the number of blocks between 2 semesters i.e 8 = 4 blocks per semester, 10 = 5 blocks per semester
|
#define TOTAL_BLOCKS 10 // the number of blocks between 2 semesters i.e 8 = 4 blocks per semester, 10 = 5 blocks per semester
|
||||||
#define MAX_REQUEST_ALTS 6
|
#define MAX_REQUEST_ALTS 6
|
||||||
@@ -36,6 +36,8 @@
|
|||||||
// 5 is the length of "FALSE" & 3 is the number of commas per line
|
// 5 is the length of "FALSE" & 3 is the number of commas per line
|
||||||
#define MAX_CHAR MAX_PUPIL_NUM_LEN + MAX_COURSE_NO_LEN + MAX_COURSE_DES_LEN + 5 + 3
|
#define MAX_CHAR MAX_PUPIL_NUM_LEN + MAX_COURSE_NO_LEN + MAX_COURSE_DES_LEN + 5 + 3
|
||||||
|
|
||||||
|
const char FLEX[2][11] = {"XAT--12A-S", "XAT--12B-S"};
|
||||||
|
|
||||||
/*** DEFINE CSV READER FUNCTIONS & DATASTRUCTURES ***/
|
/*** DEFINE CSV READER FUNCTIONS & DATASTRUCTURES ***/
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -132,9 +134,16 @@ typedef struct {
|
|||||||
uint16_t requests;
|
uint16_t requests;
|
||||||
char description[MAX_COURSE_DES_LEN];
|
char description[MAX_COURSE_DES_LEN];
|
||||||
uint8_t credits;
|
uint8_t credits;
|
||||||
|
uint32_t students[CLASS_CAP * CLASSROOMS * TOTAL_BLOCKS]; // Will be large enough to handle all pupil numbers taking this course, even if it is highly requested.
|
||||||
|
uint8_t globalNumberOfStudents;
|
||||||
|
} COURSE; // Course is the general information for selection and stuff?
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char crsNo[MAX_COURSE_NO_LEN];
|
||||||
|
char description[MAX_COURSE_DES_LEN];
|
||||||
uint32_t students[CLASS_CAP];
|
uint32_t students[CLASS_CAP];
|
||||||
uint8_t numberOfStudents;
|
uint8_t numberOfStudents;
|
||||||
} COURSE;
|
} CLASS; // A class is an actual active course, there can be many classes for 1 course, each with its own number of students.
|
||||||
|
|
||||||
/*** DEFINE GET FUNCTIONS FOR STUDENTS & COURSES FROM CSV ***/
|
/*** DEFINE GET FUNCTIONS FOR STUDENTS & COURSES FROM CSV ***/
|
||||||
|
|
||||||
@@ -252,7 +261,7 @@ COURSE *getCourses(CSV_LINE *lines, size_t lines_len, UNIQUE_COURSES unique_cour
|
|||||||
for (size_t i = 0; i < unique_course_info.numberOfCourses; i++) {
|
for (size_t i = 0; i < unique_course_info.numberOfCourses; i++) {
|
||||||
for (size_t j = 0; j < lines_len; j++) {
|
for (size_t j = 0; j < lines_len; j++) {
|
||||||
if (strcmp(lines[j].crsNo, unique_course_info.uniqueCrsNos[j]) == 0) {
|
if (strcmp(lines[j].crsNo, unique_course_info.uniqueCrsNos[j]) == 0) {
|
||||||
COURSE course = {"\0", 0, "\0", 4, {0}, 0}; // Ensure array of pupil numbers is null till we populate it later with the correct size
|
COURSE course = {"\0", 0, "\0", 4, 0}; // Ensure array of pupil numbers is null till we populate it later with the correct size
|
||||||
strcpy(course.crsNo, lines[i].crsNo);
|
strcpy(course.crsNo, lines[i].crsNo);
|
||||||
strcpy(course.description, lines[i].description);
|
strcpy(course.description, lines[i].description);
|
||||||
courses[i] = course;
|
courses[i] = course;
|
||||||
@@ -357,17 +366,17 @@ int writeCoursesToJson(COURSE *courses, size_t numberOfCourses, char output_dir[
|
|||||||
fprintf(stream, " \"description\": \"%s\",\n", courses[i].description);
|
fprintf(stream, " \"description\": \"%s\",\n", courses[i].description);
|
||||||
fprintf(stream, " \"credits\": %d,\n", courses[i].credits);
|
fprintf(stream, " \"credits\": %d,\n", courses[i].credits);
|
||||||
|
|
||||||
if (courses[i].numberOfStudents > 0) {
|
if (courses[i].globalNumberOfStudents > 0) {
|
||||||
fprintf(stream, " \"students\": [\n");
|
fprintf(stream, " \"students\": [\n");
|
||||||
for (size_t j = 0; j < courses[i].numberOfStudents; j++) {
|
for (size_t j = 0; j < courses[i].globalNumberOfStudents; j++) {
|
||||||
fprintf(stream, " \"%d\"%s\n", courses[i].students[j], j == courses[i].numberOfStudents - 1 ? "" : ",");
|
fprintf(stream, " \"%d\"%s\n", courses[i].students[j], j == courses[i].globalNumberOfStudents - 1 ? "" : ",");
|
||||||
}
|
}
|
||||||
fprintf(stream, " ],\n");
|
fprintf(stream, " ],\n");
|
||||||
} else {
|
} else {
|
||||||
fprintf(stream, " \"students\": [],\n");
|
fprintf(stream, " \"students\": [],\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stream, " \"numberOfStudents\": %d\n", courses[i].numberOfStudents);
|
fprintf(stream, " \"globalNumberOfStudents\": %d\n", courses[i].globalNumberOfStudents);
|
||||||
fprintf(stream, " }%s\n", i == numberOfCourses - 1 ? "" : ",");
|
fprintf(stream, " }%s\n", i == numberOfCourses - 1 ? "" : ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -384,18 +393,45 @@ typedef struct {
|
|||||||
bool success;
|
bool success;
|
||||||
} TIMETABLE;
|
} TIMETABLE;
|
||||||
|
|
||||||
char FLEX[2][11] = {"XAT--12A-S", "XAT--12B-S"};
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
TIMETABLE generateTimetable(STUDENT *students, size_t size_students, COURSE *courses, size_t size_courses) {
|
TIMETABLE generateTimetable(STUDENT *students, size_t size_students, COURSE *courses, size_t size_courses) {
|
||||||
TIMETABLE timetable = {{{{0}}}, 0};
|
TIMETABLE timetable = {{{{0}}}, 0};
|
||||||
|
|
||||||
uint8_t median = floor((MIN_REQ + CLASS_CAP) / 2);
|
uint8_t MEDIAN = floor((MIN_REQ + CLASS_CAP) / 2);
|
||||||
uint8_t blocksPerSemester = TOTAL_BLOCKS / 2;
|
uint8_t BLOCKS_PER_SEMESTER = TOTAL_BLOCKS / 2;
|
||||||
|
|
||||||
|
|
||||||
// Step 1 - Tally requests to check which courses are eligable to run
|
// Step 1 - Tally requests to check which courses are eligable to run
|
||||||
char activeCourses[MAX_COURSES][MAX_COURSE_NO_LEN] = {{"\0"}};
|
char activeCourses[MAX_COURSES][MAX_COURSE_NO_LEN] = {{"\0"}};
|
||||||
size_t activeCoursesIndexes[MAX_COURSES] = {0};
|
size_t activeCoursesIndexes[MAX_COURSES] = {0};
|
||||||
uint16_t activeCourseIndex = 0;
|
uint16_t activeCourseIndex = 0; // also acts as the length of activeCourses
|
||||||
for (size_t i = 0; i < size_students; i++) {
|
for (size_t i = 0; i < size_students; i++) {
|
||||||
for (size_t j = 0; j < students[i].requestsLen; j++) {
|
for (size_t j = 0; j < students[i].requestsLen; j++) {
|
||||||
if (students[i].requests[j].alternate) continue;
|
if (students[i].requests[j].alternate) continue;
|
||||||
@@ -432,6 +468,105 @@ TIMETABLE generateTimetable(STUDENT *students, size_t size_students, COURSE *cou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Step 2 - Generate classes with no students, but calculate the number of expected students per class
|
||||||
|
|
||||||
|
char hex[] = "0123456789abcdefABCDEF"; // Used to get a unique character to identify different classes of the same course
|
||||||
|
|
||||||
|
CLASS *emptyClasses = malloc(CLASSROOMS * TOTAL_BLOCKS * sizeof(CLASS)); // max this out to total number of classrooms available between both semesters
|
||||||
|
size_t currentIndex = 0;
|
||||||
|
for (size_t i = 0; i < activeCourseIndex; i++) {
|
||||||
|
uint16_t index = activeCoursesIndexes[i];
|
||||||
|
uint8_t classRunCount = floor(courses[index].requests / MEDIAN);
|
||||||
|
uint8_t remaining = courses[index].requests % MEDIAN;
|
||||||
|
|
||||||
|
size_t *courseClassIndexes = malloc((classRunCount + 1) * sizeof(size_t)); // add 1 to classRunCount in case we need to create an extra class with remaining
|
||||||
|
for (size_t j = 0; j < classRunCount; j++) {
|
||||||
|
CLASS newClass;
|
||||||
|
char courseID[MAX_COURSE_NO_LEN + 3]; // +3 = "_n\0" where n is the ID
|
||||||
|
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
|
||||||
|
|
||||||
|
// CLASS_HASH emptyClass;
|
||||||
|
// strcpy(emptyClass.key, courses[index].crsNo);
|
||||||
|
// emptyClass.class = newClass;
|
||||||
|
// emptyClasses[currentIndex] = emptyClass;
|
||||||
|
emptyClasses[currentIndex] = newClass;
|
||||||
|
courseClassIndexes[j] = currentIndex;
|
||||||
|
currentIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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++) {
|
||||||
|
emptyClasses[courseClassIndexes[j]].numberOfStudents++;
|
||||||
|
remaining--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (remainingCanCreateNewClass) {
|
||||||
|
// Create new class
|
||||||
|
CLASS newClass;
|
||||||
|
char courseID[MAX_COURSE_NO_LEN + 3]; // +3 = "_n\0" where n is the ID
|
||||||
|
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
|
||||||
|
emptyClasses[currentIndex] = newClass;
|
||||||
|
courseClassIndexes[classRunCount] = currentIndex;
|
||||||
|
currentIndex++;
|
||||||
|
|
||||||
|
// 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] = emptyClasses[courseClassIndexes[j]].numberOfStudents;
|
||||||
|
|
||||||
|
numberOfStudentsArr = equal(numberOfStudentsArr, classRunCount);
|
||||||
|
for (size_t j = 0; j < classRunCount; j++)
|
||||||
|
emptyClasses[courseClassIndexes[j]].numberOfStudents = numberOfStudentsArr[j];
|
||||||
|
|
||||||
|
free(numberOfStudentsArr);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (remainingPlusExtraFromExistingCanCreateNewClass) {
|
||||||
|
// TODO handle this logic
|
||||||
|
|
||||||
|
} 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: handle this logic too
|
||||||
|
}
|
||||||
|
|
||||||
|
free(courseClassIndexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DO MORE ALGORITHM
|
||||||
|
|
||||||
|
free(emptyClasses);
|
||||||
|
|
||||||
return timetable;
|
return timetable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user