Example #1
0
 /// <summary>
 /// How many credit points worth of subjects still need to be taken before this plan is valid?
 /// </summary>
 public int RemainingCreditPoints()
 {
     if (!SelectedCourses.Any())
     {
         return(int.MaxValue);
     }
     return(SelectedCourses.First().CreditPoints() - SelectedSubjects.Sum(subject => subject.CreditPoints()));
 }
Example #2
0
 /// <summary>
 /// Remove content from the study plan
 /// </summary>
 /// <param name="content">A subject or course that is being removed from the plan</param>
 public void RemoveContent(Content content)
 {
     if (content is Subject)
     {
         SelectedSubjects.Remove(content as Subject);
     }
     else
     {
         SelectedCourses.Remove(content as Course);
     }
     RefreshBannedSubjectsList();
     RefreshRelations();
     Order();
 }
        public void RegisterTalks()
        {
            try
            {
                var newSubjects = SubjectsLoader.Load();

                //if (CannotBeRegistered(newSubjects)) // advance code to check the limits
                //    throw new ArgumentException("Exceeding Time Limit");

                SelectedSubjects.InsertRange(SelectedSubjects.Count, newSubjects);
            }
            catch (ArgumentException e)
            {
                throw;
            }
        }
Example #4
0
 /// <summary>
 /// Add content(s) to this study plan
 /// </summary>
 /// <param name="contents">A list of subjects or courses that need to be added</param>
 public void AddContents(IEnumerable <Content> contents)
 {
     contents = contents.Except(SelectedSubjects);
     if (!contents.Any())
     {
         return;
     }
     foreach (Content content in contents)
     {
         if (content is Subject)
         {
             SelectedSubjects.Add(content as Subject);
         }
         else
         {
             SelectedCourses.Add(content as Course);
         }
     }
     RefreshBannedSubjectsList();
     RefreshRelations();
     Order();
 }
Example #5
0
 /// <summary>
 /// Find out the relation between all selected subjects to see if any are required
 /// </summary>
 private void RefreshRelations()
 {
     ContentRelations.Clear();
     // Iterate over every selected subject to see what it links to
     foreach (Content content in SelectedSubjects.Cast <Content>().Concat(SelectedCourses))
     {
         // Use a breadth-first search for subjects that this subject rely on
         Queue <(Decision, Edge.Importance)> toAnalyze = new Queue <(Decision, Edge.Importance)>();
         toAnalyze.Enqueue((content.Prerequisites, Edge.Importance.Compulsory));
         toAnalyze.Enqueue((content.Corequisites, Edge.Importance.Compulsory));
         while (toAnalyze.Any())
         {
             (Decision requisite, Edge.Importance importance) = toAnalyze.Dequeue();
             // Ignore electives
             if (requisite.IsElective())
             {
                 continue;
             }
             // Rank the importance according to whether the option is compulsory or not
             importance = (importance == Edge.Importance.Compulsory && requisite.MustPickAll()) ? Edge.Importance.Compulsory : Edge.Importance.Optional;
             foreach (Option option in requisite.Options)
             {
                 // Search the sub-decisions
                 if (option is Decision decision)
                 {
                     toAnalyze.Enqueue((decision, importance));
                 }
                 // If the option has been selected, create an edge from the subject to this option
                 else if (SelectedSubjects.Contains(option))
                 {
                     ContentRelations.Add(new Edge {
                         source = content, importance = importance, dest = option as Content
                     });
                 }
             }
         }
     }
 }
 public Subject GetSubjectByName(string topic)
 {
     return(SelectedSubjects.FirstOrDefault(talk => string.Equals(talk.Topic, topic, StringComparison.OrdinalIgnoreCase)));
 }
Example #7
0
        /// <summary>
        /// Assign a time to all selected subjects
        /// </summary>
        private void Order()
        {
            Stopwatch timerOrder = new Stopwatch();

            timerOrder.Restart();

            // Check that all forcedTimes are allowed
            foreach (Subject subject in SubjectsWithForcedTimes)
            {
                if (!subject.AllowedDuringSemester(AssignedTimes[subject], this))
                {
                    SubjectsWithForcedTimes.Remove(subject);
                }
            }

            // Remove current time assignments
            foreach (Subject subject in SelectedSubjects)
            {
                if (!SubjectsWithForcedTimes.Contains(subject))
                {
                    AssignedTimes.Remove(subject);
                }
            }

            // This variable works because IEnumerables get evaluated every time a method is called on it. I usually use a local function instead of a variable.
            IEnumerable <Subject> RemainingSubjects = SelectedSubjects.Except(SelectedSubjectsSoFar(Time.All)).OrderBy(subject => subject.GetLevel());

            for (Time semester = Time.First; MaxCreditPoints.Keys.Contains(semester) || RemainingSubjects.Any(); semester = semester.Next())
            {
                // If neccessary, add another year to the planner
                if (!MaxCreditPoints.Keys.Contains(semester))
                {
                    AddYear();
                }
                // Fill the semester with subjects that can be chosen
                int selectedCredits = SelectedSubjects.Where(subject => AssignedTimes.TryGetValue(subject, out Time selectedTime) && selectedTime == semester).Sum(subject => subject.CreditPoints());
                while (selectedCredits < GetMaxCreditPoints(semester))
                {
                    // Prepare a list of what subjects could be chosen
                    var possibleSubjects = RemainingSubjects;
                    // Do not pick subjects with forced times later than the current session
                    possibleSubjects = possibleSubjects.Where(subject => !(SubjectsWithForcedTimes.Contains(subject) && semester.IsEarlierThan(AssignedTimes[subject])));
                    // Pick from subjects that are allowed during this semester
                    possibleSubjects = possibleSubjects.Where(subject => subject.AllowedDuringSemester(semester, this));
                    // Pick from subjects which would not result in Credit Overflow
                    possibleSubjects = possibleSubjects.Where(subject => subject.CreditPoints() + selectedCredits <= GetMaxCreditPoints(semester));
                    // Check if any subjects are forced
                    IEnumerable <Subject> forcedSubjects = possibleSubjects
                                                           .Where(subject => SubjectsWithForcedTimes.Contains(subject) && AssignedTimes[subject].IsEarlierThanOrAtTheSameTime(semester));
                    // If any subjects are forced, only consider the forced subjects
                    if (forcedSubjects.Any())
                    {
                        possibleSubjects = forcedSubjects.OrderBy(subject => AssignedTimes[subject]);
                    }
                    // Otherwise, filter subjects according to whether their requisites are completed
                    else
                    {
                        possibleSubjects = possibleSubjects.Where(subject => RequisitesHaveBeenSelected(subject, semester));
                    }
                    // Favor subjects that have many other subjects relying on them
                    possibleSubjects = possibleSubjects.OrderByDescending(subject => RemainingSubjects.Sum(other => IsAbove(parent: other, child: subject, out int size) ? size : 0))
                                       // Favor subjects that cannot fit in many semesters
                                       .ThenBy(subject => subject.Semesters.Select(semester => semester.session).Distinct().Count())
                                       // Favor lower level subjects
                                       .ThenBy(subject => subject.GetLevel())
                                       // Favor subjects that don't require many electives as prerequisites
                                       .ThenBy(subject => subject.Prerequisites.SizeOfElective() + subject.Corequisites.SizeOfElective())
                                       // Favor subjects that are requisites to their Degree (or any course)
                                       .ThenBy(subject => ContentRelations.Any(relation => relation.source is Course && relation.dest == subject) ? 0 : 1);
                    // Pick the first item from that list
                    Subject nextSubject = possibleSubjects.FirstOrDefault();
                    // If no subject was chosen, go to the next semester
                    if (nextSubject == null)
                    {
                        break;
                    }
                    // Add the selected subject to this semester
                    AssignedTimes[nextSubject] = semester;
                    // Keep track of how many more times this loop can repeat
                    selectedCredits += nextSubject.CreditPoints();
                }
            }

            if (SelectedSubjects.Except(SelectedSubjectsSoFar(Time.All)).Any())
            {
                throw new InvalidOperationException("Not all the subjects were added to the table");
            }

            timerOrder.Stop();
            Debug.WriteLine("Ordering Plan:       " + timerOrder.ElapsedMilliseconds + "ms");

            // Helper functions to determine whether a subject can be picked

            bool RequisitesHaveBeenSelected(Subject subject, Time time, Decision requisite = null)
            {
                // This function goes through a subject's requisites to make sure every SelectedSubject that is a requisite to this subject has already been added to the schedule

                // If no requisite has been specified, then this must have been called by outside this function
                if (requisite == null)
                {
                    // Check if this is one of those subjects which don't have any actual requisites (eg no data or "permission by special approval")
                    // If so, assign it an earliest position based on its level
                    if (subject.Prerequisites.HasBeenCompleted() && subject.Corequisites.HasBeenCompleted())
                    {
                        return(time.AsNumber() / 2 >= subject.GetLevel() - 1);
                    }
                    // Call this function again but with the prerequisites and corequisites
                    return(RequisitesHaveBeenSelected(subject, time.Previous(), subject.Prerequisites) && RequisitesHaveBeenSelected(subject, time, subject.Corequisites));
                }

                // If the requisit is met by the scheduled selected subjects, return true
                if (requisite.HasBeenCompleted(this, time))
                {
                    return(true);
                }
                // If the requisit is an elective, don't process it. Instead, compare the subject's level to the time
                if (requisite.IsElective())
                {
                    return(subject.GetLevel() <= time.Next().year);
                }
                // Iterate over all of the requisite's option
                // The requisite's selectionType doesn't matter, all that matters is whether it HasBeenCompleted.
                foreach (Option option in requisite.Options)
                {
                    // If the option is a subject that needs to be picked, hasn't been picked, and must come before the current subject: return false
                    if (option is Content content && !SelectedSubjectsSoFar(time).Contains(content) && OptionMustComeBeforeSubject(parent: subject, child: content as Subject))
                    {
                        return(false);
                    }