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_COURSE_DES_LEN 50
|
||||
#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 MAX_REQUEST_ALTS 6
|
||||
@@ -36,6 +36,8 @@
|
||||
// 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
|
||||
|
||||
const char FLEX[2][11] = {"XAT--12A-S", "XAT--12B-S"};
|
||||
|
||||
/*** DEFINE CSV READER FUNCTIONS & DATASTRUCTURES ***/
|
||||
|
||||
typedef struct {
|
||||
@@ -132,9 +134,16 @@ typedef struct {
|
||||
uint16_t requests;
|
||||
char description[MAX_COURSE_DES_LEN];
|
||||
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];
|
||||
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 ***/
|
||||
|
||||
@@ -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 j = 0; j < lines_len; j++) {
|
||||
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.description, lines[i].description);
|
||||
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, " \"credits\": %d,\n", courses[i].credits);
|
||||
|
||||
if (courses[i].numberOfStudents > 0) {
|
||||
if (courses[i].globalNumberOfStudents > 0) {
|
||||
fprintf(stream, " \"students\": [\n");
|
||||
for (size_t j = 0; j < courses[i].numberOfStudents; j++) {
|
||||
fprintf(stream, " \"%d\"%s\n", courses[i].students[j], j == courses[i].numberOfStudents - 1 ? "" : ",");
|
||||
for (size_t j = 0; j < courses[i].globalNumberOfStudents; j++) {
|
||||
fprintf(stream, " \"%d\"%s\n", courses[i].students[j], j == courses[i].globalNumberOfStudents - 1 ? "" : ",");
|
||||
}
|
||||
fprintf(stream, " ],\n");
|
||||
} else {
|
||||
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 ? "" : ",");
|
||||
}
|
||||
|
||||
@@ -384,18 +393,45 @@ typedef struct {
|
||||
bool success;
|
||||
} 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 timetable = {{{{0}}}, 0};
|
||||
|
||||
uint8_t median = floor((MIN_REQ + CLASS_CAP) / 2);
|
||||
uint8_t blocksPerSemester = TOTAL_BLOCKS / 2;
|
||||
uint8_t MEDIAN = floor((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[MAX_COURSES][MAX_COURSE_NO_LEN] = {{"\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 j = 0; j < students[i].requestsLen; j++) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user