begin step 2

This commit is contained in:
SowinskiBraeden committed 2024-10-03 23:09:02 -07:00
1 parent 5b20212b85
commit e8fc0e2138
1 file changed
+146 -11
+146 -11
View File
@@ -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;
} }