/// <summary> /// Assigns students to rooms for the given course. /// </summary> /// <param name="course"> /// The course to schedule for. /// </param> /// <param name="courseStudents"> /// The students to try and assign. /// </param> /// <param name="availableFaculty"> /// Faculty members that are available for scheduling for this course. /// </param> /// <param name="labRoom"> /// If we're scheduling for labs. /// </param> /// <param name="classRoom"> /// If we're scheduling for class rooms. /// </param> /// <param name="instructRoom"> /// If we're scheduling for instructional rooms. /// </param> /// <returns> /// A list of students that were unable to be scheduled. /// </returns> private List<Student> AssignStudents(Course course, List<Student> courseStudents, List<Faculty> availableFaculty, bool labRoom, bool classRoom, bool instructRoom, bool library) { int currentFacultyIndex = 0; int requiredHours = course.Requirement.LabHr; while (requiredHours > 0) { int assignedHours = requiredHours > 2 ? 2 : requiredHours; // This is the confusing part, we can assign students across different hours if there's too many // Now here's the confusing part, we locate a teacher and assign consecutive slots until there's no students while (courseStudents.Count > 0) { // We can assign either 1 or 2 hour slots, so first see if we can find a 2 hour available lab List<TimeSlot> availability = RequestAvailableRooms(course, labRoom, classRoom, instructRoom, library); // Find the largest available room here TimeSlot largest = FindLargestRoom(availability, courseStudents.Count, assignedHours); // If there is no available slot, we're slightly screwed and will have to forget about scheduling the rest now if (largest == null) { foreach (Student student in courseStudents) { if (!UnscheduledCourses.ContainsKey(student.StudentID)) UnscheduledCourses[student.StudentID] = new List<Course>(); UnscheduledCourses[student.StudentID].Add(course); } UpdateProgress(string.Format("Failed to schedule {0} students for course {1}", courseStudents.Count, course.Names)); return courseStudents; } // Our endDate is the start plus assigned hours DateTime endDate = largest.Start.AddHours(assignedHours); // We've got some slot available, ram everyone into it and keep going Faculty teachingFaculty = availableFaculty[currentFacultyIndex++ % availableFaculty.Count]; Schedule newSchedule = new Schedule() { StartTime = largest.Start, EndTime = endDate, CourseID = course.CourseID, RoomID = largest.TargetRoom.RoomID, }; CurrentSchedules.Add(newSchedule); Program.Database.Schedules.Add(newSchedule); // Add the teaching faculty time first FacultySchedule facultySchedule = new FacultySchedule() { StartTime = largest.Start, FacultyID = teachingFaculty.FacultyID, RoomID = largest.TargetRoom.RoomID, Schedule = newSchedule, }; Program.Database.FacultySchedules.Add(facultySchedule); // Now assign all the students int removedCount = largest.TargetRoom.Capacity > courseStudents.Count ? courseStudents.Count : largest.TargetRoom.Capacity; List<Student> assignedStudents = courseStudents.GetRange(0, removedCount); courseStudents.RemoveRange(0, removedCount); foreach (Student student in assignedStudents) { StudentSchedule studentSchedule = new StudentSchedule() { StudentID = student.StudentID, StartTime = largest.Start, Schedule = newSchedule, }; Program.Database.StudentSchedules.Add(studentSchedule); } } requiredHours -= assignedHours; } if (courseStudents.Count != 0) Console.WriteLine("DUH"); return courseStudents; }
/// <summary> /// Returns a list of time slots available for the given course under the restrictions that it should be a lab room, /// class room, instructional room, etc. /// </summary> /// <param name="course"> /// The course to account for. /// </param> /// <param name="labRoom"> /// Should the room be a lab room? /// </param> /// <param name="classRoom"> /// Should the room be a class room? /// </param> /// <param name="instructRoom"> /// Should the room be an instructional room? /// </param> /// <returns> /// A list of time slots representing available times at what rooms. /// </returns> private List<TimeSlot> RequestAvailableRooms(Course course, bool labRoom, bool classRoom, bool instructRoom, bool library) { List<TimeSlot> availableSlots = new List<TimeSlot>(); foreach (Room room in Program.Database.Rooms) { if ((room.RoomID != "Lib" && library) && (!room.Resource.Cad && course.Requirement.Cad) && (!room.Resource.Program && course.Requirement.Program) && (!room.Resource.Multi && course.Requirement.Multi) && (!room.Resource.Gaming && course.Requirement.Gaming)) continue; if ((labRoom && !room.Resource.Lab) || (classRoom && !room.Resource.Classroom) || (instructRoom && !room.Resource.Instruct) || (library && !room.RoomID.Contains("Lib"))) continue; // Figure out where existing schedule usages are var usages = from schedule in CurrentSchedules where schedule.RoomID == room.RoomID select schedule; // We don't care so much about what's already here, just flag it RangedList<Room> usedSlots = new RangedList<Room>(); usedSlots.Payload = usedSlots.Payload.Distinct().ToList(); foreach (Schedule schedule in usages) { usedSlots.AddRange(schedule.StartTime, schedule.EndTime, room); } // We start on monday and blow through each day of the week, looking in 1-hour increments for available slots DateTime startDate = StartDate(); for (int day = 0; day < 5; day++) { DateTime currentDate = startDate.AddDays(day).AddHours(8.335); DateTime? openStart = currentDate; for (int hour = 0; hour < 8; hour++) { // Is the current date available? RangedList<Room>.PayloadIndex currentSlot = usedSlots.GetIndex(currentDate); if (currentSlot != null) { // We've got to close an availability block if (openStart != null && (DateTime)openStart != currentDate) { DateTime start = (DateTime)openStart; DateTime end = currentDate; // Record our availability range TimeSlot newSlot = new TimeSlot() { Start = start, End = end, TargetRoom = room, }; availableSlots.Add(newSlot); } openStart = null; // Not available, move along to the next hour } else if (openStart == null) openStart = currentDate; currentDate = currentDate.AddHours(1); } // This will happen on our first pass because entire days are still available if (openStart != null && (DateTime)openStart != currentDate) { DateTime start = (DateTime)openStart; DateTime end = currentDate; // Record our availability range TimeSlot newSlot = new TimeSlot() { Start = start, End = end, TargetRoom = room, }; availableSlots.Add(newSlot); } } } List<TimeSlot> result = availableSlots.Distinct().ToList(); return result; }