/* modified version of timetable.c to reflect changes in design */ #include #include #include #define MAX_ROOMS 20 /* max. valid rooms */ #define MAX_COURSES 30 /* max. valid courses */ #define MAX_TIMES 15 /* max. possible timeslots */ #define MAX_CAP 300 /* max. allowed room capacity */ #define MIN_CAP 10 /* min. room capacity */ #define MAX_LN_LENG 80 /* max. input line length */ #define TRUE 1 #define FALSE 0 #define MAX_COURSENO_SIZE 6 /* max. size of course number string */ #define MAX_TIMESLOT_SIZE 9 /* max. size of time slot string */ #define MAX_ROOMNO_SIZE 4 /* max. size of room no. string */ typedef char courseno[MAX_COURSENO_SIZE]; typedef char timeslot[MAX_TIMESLOT_SIZE]; typedef struct /* info about a valid room */ { char room_no[MAX_ROOMNO_SIZE]; int capacity; }room; /* A general linked list of integers */ typedef struct LstNode { int pref_index; /* Typically index in some DB will be kept */ struct LstNode *next; }lstnode; typedef struct /* information about the course to be scheduled */ { int cnum; /* index of the course in CourseDB */ int enrol; /* expected enrollment */ lstnode *prefs; /* list of valid preferences */ }course; typedef struct /* to produce conflict or explanation info */ { int err_code; /* error code */ int cnum; /* index into course no_db */ int roomno; /* index into ClassroomDB */ lstnode *prefs; }error; /* The next 6 data items obtained by parsing file1 */ room ClassroomDB[MAX_ROOMS]; /* valid courses */ courseno CourseDB[MAX_COURSES]; /* valid rooms */ timeslot TimeslotDB[MAX_TIMES]; /* valid timeslots */ int TotRooms; /* total number of rooms */ int TotTimes; /* total number of possible lecture times */ int TotCourses;/*total courses in file 1 */ course SchCourses[MAX_COURSES]; /* list of courses to be scheduled */ int TotSchCourses; /* total number of courses to scheduled */ /* The following 4 pointers are set up after the SchCourses is "sorted" */ int PgP=0; /* start of pg_prefs course; Is 0 */ int UgP; /* first index of ug_prefs courses */ int PgNP; /* first index of pg_no_pref courses */ int UgNP; /* first index of ug_no_pref courses */ int TimeTable[MAX_ROOMS][MAX_TIMES]; /* the rows are indices in ClassroomDB, columns are indices into TimeslotDB */ int PgAlloc[MAX_TIMES]; /* Time slots occupied by PG courses. An entry is -ve if not allocated to a PG course, +ve otherwise. Used for checking conflicts while scheduling PG courses */ error ConflLst[MAX_COURSES]; /* expln for unschedualble courses */ error ExplnLst[MAX_COURSES]; /* expln for each unschedualble pref */ int TotConfl = 0; /* represents last entry in ConflLst */ int TotExpln = 0; /* represents last entry in ExplnLst */ int line_no; /* number of the line being read from input file */ /*---------------------------------------------------------------------------------*/ /* function prototypes */ int validate_file2(); int validate_class_rooms(); int validate_dept_courses(); int validate_lec_times(); int sched_pg_no_pref(); int PgNoPrefSchLable(); char *get_next_line(); int chk_dup_room(); int chk_cap(); int chk_fmt_room_no(); int chk_fmt_course_no(); int chk_fmt_time_slot(); int chk_dup_course_no(); int chk_dup_time_slot(); lstnode *form_pref_list(); int chk_file2_header(); course *get_course_info(); /****************************************************************************************/ main(argc,argv) int argc; char *argv[]; { FILE *fd1,*fd2; if (argc != 3) { printf("usage : schedule file1 file2\n"); exit(-1); } /* opening file1 */ if ((fd1=fopen(argv[1],"r"))== NULL) { printf("can't open file %s\n",argv[1]); exit(-1); } /* opening file2 */ if ((fd2=fopen(argv[2],"r"))== NULL) { printf("can't open file %s\n",argv[2]); exit(-1); } /* get the data from input files */ get_validated_input(fd1,fd2,TimeslotDB,ClassroomDB,CourseDB,SchCourses); /* schedule courses */ schedule(ClassroomDB,TimeslotDB,CourseDB,SchCourses, TimeTable,ConflLst,ExplnLst); /* ouput timetable and explanations */ print_output(TimeTable,ExplnLst,ConflLst); } /***************************************************************************/ /* This module validates the contents of file1 and file2 and produces * lists for valid courses,classrooms,time slots,and courses to be * scheduled. Iferrors found in input file format, it exits if * scheduling is not possible, else ignores the erroneous data */ get_validated_input(file1,file2,TimeslotDB,ClassroomDB,CourseDB,SchCourses) FILE * file1; FILE * file2; timeslot * TimeslotDB; room * ClassroomDB; courseno * CourseDB; course * SchCourses; { /* get data from file1 */ line_no = 0; printf("Reading file1 ... \n"); validate_file1(file1,ClassroomDB,CourseDB,TimeslotDB); /* get data from file2 */ line_no = 0; printf("Reading file2 ...\n"); validate_file2(file2, SchCourses); separate_courses(CourseDB,SchCourses); } /*------------------------------------------------------------------------*/ /* Top level module to schedule courses. Besides timetable, it * gives explanation for courses/prefs which could not be satisfied */ schedule(ClassroomDB,TimeslotDB,CourseDB,SchCourses, TimeTable,ConflLst,ExplnLst) room * ClassroomDB; timeslot * TimeslotDB; courseno * CourseDB; course * SchCourses; int TimeTable[][MAX_TIMES]; error * ConflLst; error * ExplnLst; { int row,col; /* initializing time table & PgAlloc */ for(row=0;rowcnum; SchCourses[TotSchCourses].enrol = curr_course->enrol; SchCourses[TotSchCourses].prefs = curr_course->prefs; TotSchCourses++; } } if (TotSchCourses>MAX_COURSES) { /* more than permissible */ printf("line# %d : More number of courses than permissible\n",line_no++); printf("later ones are ignored\n"); } else if (TotSchCourses == 0) /* no courses */ { printf("no courses to schedule\n"); printf("scheduling aborted \n"); exit(-1); } fclose(fd); } /*------------------------------------------------------------------------*/ /* Rearrange the courses in the following order: PG courses with * preferences, UG courses with preferences, PG courses with no * preferences, UG courses with no preferences */ separate_courses(CourseDB,SchCourses) courseno *CourseDB; course *SchCourses; { /* Scans schedule courses and copies different types in corresponding * temp data structure, then copies back in order */ course pg_pref[MAX_COURSES], pg_no_pref[MAX_COURSES], ug_pref[MAX_COURSES], ug_no_pref[MAX_COURSES]; int pg_pref_i = 0; /* index of last entry in pg_pref */ int ug_pref_i = 0; int pg_no_pref_i = 0; int ug_no_pref_i = 0; char temp_char; int i,j; for (i=0;i '5') /* pg course */ if (SchCourses[i].prefs != NULL) /* preferences given */ pg_pref[pg_pref_i++]=SchCourses[i]; else pg_no_pref[pg_no_pref_i++]=SchCourses[i]; else /* ug course */ if (SchCourses[i].prefs != NULL) /* preferences given */ ug_pref[ug_pref_i++]=SchCourses[i]; else ug_no_pref[ug_no_pref_i++]=SchCourses[i]; } /* copy back from temporary storage */ for (i=0,j=0;ipref_index; sched_done=FALSE; while ((pref_list != NULL)&&(!sched_done)) { pref_index = pref_list->pref_index; if (PgAlloc[pref_index]<0) /* no pg course at this time slot */ { pref_honored = pref_index; PgAlloc[pref_index] = course_index; TimeTable[room_no][pref_index] = course_index; sched_done = TRUE; } pref_list = pref_list->next; } /* while */ if ( !sched_done) /* course is not scheduled */ { ConflLst[TotConfl].err_code = 1; ConflLst[TotConfl].cnum = course_index; ConflLst[TotConfl++].prefs = SchCourses[i].prefs; } /* first preference not honored */ else if (pref_honored != first_pref) { /* store info about course to print explns later */ ExplnLst[TotExpln].err_code = pref_honored; ExplnLst[TotExpln].prefs = SchCourses[i].prefs; ExplnLst[TotExpln].cnum = course_index; ExplnLst[TotExpln++].roomno = -1; } } /* for */ } /*------------------------------------------------------------------------*/ /* Schedule UG courses with preferences. This is not straighforward. * Before alloting a , it has to check if this * allotment is "safe", i.e. the schedulability of PG courses with * no prefs is not disturbed. It does so by first "simulating" a * scheduling of pg_no_pref courses to find out how many can be * scheduled. An allotment is safe if the same no. can be still * scheduled */ sched_ug_pref(SchCourses,PgAlloc, ConflLst,ExplnLst, TimeTable) course *SchCourses; int *PgAlloc; error *ConflLst; error *ExplnLst; int TimeTable[][MAX_TIMES]; { int pref_index, /* preference being handled */ course_index, /* current course */ enrol, /* enrollment of curr course */ i,j, /* loop indices */ room_no; /* room with required capacity */ int num_pg_no_pref, /* number of pg courses with no preferences */ pref_honored, /* preference being handled */ first_pref; /* index for first preference */ lstnode *pref_list; /* list of prefs for curr course */ int sched_done = FALSE;/* set if scheduling is done */ int TotSchPgNoPref; /* schedulable pg no prefs */ /* number of pg_no_prefs courses that can be scheduled before ug_prefs are scheduled */ TotSchPgNoPref = PgNoPrefSchLable(TimeTable,PgAlloc,SchCourses); num_pg_no_pref=UgNP-PgNP; for (i=UgP;ipref_index; sched_done = FALSE; while ((pref_list != NULL)&&(!sched_done)) { pref_index = pref_list->pref_index; if ((num_pg_no_pref == 0)|| (PgAlloc[pref_index] >= 0)) /* this time slot is not useful for pg_no_prefs */ { for(j = room_no; jnext; } /* while */ if (!sched_done) { ConflLst[TotConfl].err_code = 2; ConflLst[TotConfl].cnum = course_index; ConflLst[TotConfl++].prefs = SchCourses[i].prefs; } else if (pref_honored != first_pref) { ExplnLst[TotExpln].err_code = pref_honored; ExplnLst[TotExpln].prefs = SchCourses[i].prefs; ExplnLst[TotExpln].cnum = course_index; ExplnLst[TotExpln++].roomno = room_no; } } /* for */ } /*------------------------------------------------------------------------*/ /* Schedule PG courses with no preferences specified; Returns the * number of courses it is able to schedule */ int sched_pg_no_pref(SchCourses,ConflLst,TotConfl,PgAlloc,TimeTable) course *SchCourses; error *ConflLst; int *TotConfl; int *PgAlloc; int TimeTable[][MAX_TIMES]; /* schedule pg courses with no preferences */ { int course_index, /* curr course index */ enrol, /* enrollement of course being handled */ room_index, /* curr room index */ sched_done, /* set if course is scheduled */ cur_slot; /* curr preference index */ int i; /* loop index */ int tot_scheduled=0; /* total courses scheduled */ for(i = PgNP;ipref_index = pref_index; new_pref->next = NULL; if (pref_tail == NULL) pref_tail = pref_list = new_pref; else { pref_tail->next = new_pref; pref_tail = new_pref; } } token = strtok((char *)NULL," ,;\t\n"); } if ((token != NULL)&&(num_of_pref>4)) { printf("line# %d : more preferences than allowed\n",line_no); printf(" time slots from \"%s\" are ignored\n",token); } return(pref_list); } /*------------------------------------------------------------------------*/ /* outputs the time table in tabular form as * room numbers into rows and time slots into columns */ print_TimeTable(TimeslotDB,ClassroomDB,CourseDB,TimeTable) timeslot *TimeslotDB; room *ClassroomDB; courseno *CourseDB; int TimeTable[][MAX_TIMES]; { int i,j,breadth,space; breadth = (TotTimes+1) * 12; space = (breadth-10)/2; printf("\n"); /* initial spaces before 'TIME TABLE' */ if (space >0) for (i=0;ipref_index != pref_honored)) { printf("\t%8s : ",TimeslotDB[temp_pref->pref_index]); if (room_index <0) printf("Conflicting with course %s\n", CourseDB[PgAlloc[temp_pref->pref_index]]); else printf("Room with proper capacity not found\n"); temp_pref = temp_pref->next; } if ((room_index >= 0)&& (TimeTable[room_index][pref_honored] != ExplnLst[i].cnum)) { printf("\t %s :\n",TimeslotDB[pref_honored]); while (TimeTable[room_index][pref_honored] != ExplnLst[i].cnum) { printf("\t\t room#%s :",ClassroomDB[room_index].room_no); printf("conflicting with course %s\n", CourseDB[TimeTable[room_index][pref_honored]]); room_index++; } } } printf("\n"); } /*------------------------------------------------------------------------*/ /* Print the list of courses that are not scheduled in the final table, * and specify the conflicts that prevented their scheduling */ print_conflicts(ConflLst,CourseDB) error *ConflLst; courseno *CourseDB; { int i; lstnode *temp; if (TotConfl <1) return; printf("\n\t\t\tConflict Report\n"); printf("\t-----------------------------------------------\n\n"); for (i=0;ipref_index]); printf("conflicting with course %s \n", CourseDB[PgAlloc[temp->pref_index]]); temp = temp->next; } break; case 2 : temp = ConflLst[i].prefs; while (temp != NULL) { printf("\t %s : ",TimeslotDB[temp->pref_index]); printf("Room with proper capacity not found\n"); temp = temp->next; } break; case 3 : printf("\tExcess PG course : no time slot available\n"); break; case 4 : printf("\tNo free room with required capacity available\n"); break; case 5 : printf("\tIgnored : No room with adequate capacity \n"); break; case 6 : printf("\tIgnored : course repeated more than once in file2\n"); break; } printf("\n"); } } /*************************************************************************** * Utility routines * ***************************************************************************/ /* * returns first non empty line from the input file. * returns null string if eof is encountered & no non * empty line is found. * All the initial spaces before a line are removed. * The last character of the string will be '\n' if is * a non empty line is returned. */ char * get_next_line(fd,buffer) FILE * fd; char * buffer; { int ln_found = FALSE; /* set if line is found */ char *temp_char; while ( !ln_found ) { line_no++; /* eof reached */ if (fgets(buffer,MAX_LN_LENG,fd) == NULL) return(NULL); /* removes initial spaces in the line */ temp_char = buffer; while ( (*temp_char != NULL) && isspace(*temp_char) ) temp_char++; if (*temp_char != NULL) ln_found=TRUE; } strcpy(buffer,temp_char); return(buffer); } /*------------------------------------------------------------------------*/ /* Checks if the room_no given already exists in the ClassroomDB */ int chk_dup_room(ClassroomDB,TotRooms,room_number) room * ClassroomDB; int TotRooms; char * room_number; /* check, whether the given room number already * exists in the data base. returns TRUE if so, * otherwise false */ { int i; for (i=0;i=MIN_CAP)&&(capacity<=MAX_CAP)) ? 0 : -1; return(flag); } /*------------------------------------------------------------------------*/ int chk_fmt_room_no(room_number) char *room_number; /* checks the format of room number * returns -1 if any error * 0, otherwise */ { int i,str_leng; str_leng=strlen(room_number); if (str_leng>3) return(-1); else for (i=0;iClassroomDB[j].capacity) { strcpy(temp_room.room_no,ClassroomDB[i].room_no); temp_room.capacity=ClassroomDB[i].capacity; strcpy(ClassroomDB[i].room_no,ClassroomDB[j].room_no); ClassroomDB[i].capacity=ClassroomDB[j].capacity; strcpy(ClassroomDB[j].room_no,temp_room.room_no); ClassroomDB[j].capacity=temp_room.capacity; } } /*------------------------------------------------------------------------*/ chk_fmt_course_no(course_no) char *course_no; /* checks the format of course number i.e csddd, d is any digit * returns -1 if any error * returns 0 otherwise */ { int str_leng; int fmt_error = FALSE; str_leng = strlen(course_no); if (str_leng != 5) fmt_error=TRUE; else if ( strncmp(course_no,"cs",2) != 0 ) fmt_error = TRUE; else if ( !isdigit(course_no[2]) ) fmt_error = TRUE; else if ( !isdigit(course_no[3]) ) fmt_error = TRUE; else if ( !isdigit(course_no[4]) ) fmt_error = TRUE; if (fmt_error) return(-1); else return(0); } /*------------------------------------------------------------------------*/ int chk_dup_course_no(CourseDB,TotCourses,course_number) courseno * CourseDB; int TotCourses; char * course_number; { int i; for (i=0;i8)); if (!fmt_error) { temp_str=temp_slot; while ( !isdigit(*temp_str) ) temp_str++; strcpy(time,temp_str); *temp_str = '\0'; if (strcmp(temp_slot,"MWF")&&strcmp(temp_slot,"TT")) fmt_error = TRUE; else { temp_char = strchr(time,':'); if (temp_char != NULL) *temp_char = '9'; /* any digit */ while (time[i] != NULL) if (!isdigit(time[i++])) fmt_error = TRUE; } } if (fmt_error) return(-1); else return(0); } /*--------------------------------------------------------------------------------------*/ int chk_dup_time_slot(TimeslotDB,TotTimes,time_slot) timeslot * TimeslotDB; int TotTimes; char * time_slot; { int i; for (i=0;i=enrol) return(i); return(-1); } /*-------------------------------------------------------------------------------------*/ /* get course info from the given line */ course *get_course_info(line) char *line; { char *token; int course_index; int enrol; lstnode *pref_list; course *course_info; /* get the course number */ token = strtok(line," \t\n"); if ((course_index=get_course_index(CourseDB,TotCourses,token)) < 0) { printf("line# %d : course number \"%s\" does not exists *** ignored\n",line_no,token); return(NULL); } /* get the expected enrollment */ token=strtok((char *) NULL," \t\n"); if (token == NULL) { printf("line# %d : enrollment expected **** course ignored \n",line_no); return(NULL); } enrol=atoi(token); if ( chk_cap(enrol) < 0 ) { printf("line# %d : enrollment \"%d\" not in the allowed range \n",line_no,enrol); printf("line# %d : course \"%s\" is ignored\n",line_no,CourseDB[course_index]); return(NULL); } if (chk_dup_sched_course(SchCourses,course_index)) { printf("line# %d : \"%s\" duplicate course *** ignored\n",line_no,CourseDB[course_index]); return(NULL); } /* get the remainder of line containing preferences */ strcpy(line,(token+strlen(token)+1)); /* get the list of preferences for this course */ pref_list = form_pref_list(line,TimeslotDB); course_info = (course *)malloc(sizeof(course)); course_info->cnum = course_index; course_info->enrol = enrol; course_info->prefs = pref_list; return(course_info); } /*-------------------------------------------------------------------------------------*/ /* skips the lines in file, * until either eof is found, or * line ends with ';' */ skip_lines(fd) FILE *fd; { char temp_line[MAX_LN_LENG]; while ( (fgets(temp_line,MAX_LN_LENG,fd) != NULL) && (strchr(temp_line,';') == NULL) ) line_no++; } /*------------------------------------------------------------------------*/ /* following routines are for debugging purpose only */ display_rooms() { int i; for (i=0;ipref_index]); temp_pref=temp_pref->next; } } /*------------------------------------------------------------------------*/ display_scheds() { int i; printf("total courses %d\n",TotSchCourses); printf("ug start %d\n",UgP); printf("pg no prefstart %d\n",PgNP); printf("ug no prefstart %d\n",UgNP); for (i=0;i