diff --git a/crafter.cs b/crafter.cs index 739c716..a04fda0 100644 --- a/crafter.cs +++ b/crafter.cs @@ -17,6 +17,7 @@ public class CourseCrafter public static void Craft() { GeneratedCourses = new(); + var settings = Settings.Instance; var students = settings.Students; var sports = settings.Sports; @@ -26,11 +27,9 @@ public class CourseCrafter int semesterCount = sports.Max(s => s.Semester.Length); - // Result initialisieren foreach (var st in students) st.Result = Enumerable.Repeat("Fehler", semesterCount).ToList(); - // Mapping Name + Alternativen → Sport var nameMap = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var sp in sports) { @@ -39,127 +38,85 @@ public class CourseCrafter nameMap[alt] = sp; } - // ===== Semesterweise Planung ===== + // Nachfrage je Sport + var demand = new Dictionary>(); + foreach (var sp in sports) + demand[sp] = new List(); + + foreach (var st in students) + { + foreach (var sel in st.SelectedCourseNames.Distinct()) + { + if (nameMap.TryGetValue(sel, out var sp)) + demand[sp].Add(st); + } + } + + // ===== Semesterweise echte Verteilung ===== for (int sem = 0; sem < semesterCount; sem++) { - int remainingCourseSlots = settings.NumCoursesPerSemester; + int remainingSemesterSlots = settings.NumCoursesPerSemester; - // Interessenten dieses Semesters - var interested = new Dictionary>(); - foreach (var sp in sports) - interested[sp] = new List(); - - foreach (var st in students) - { - if (st.SelectedCourseNames.Count <= sem) - continue; - - var sel = st.SelectedCourseNames[sem]; - if (nameMap.TryGetValue(sel, out var sp)) - interested[sp].Add(st); - } - - var instances = new List(); - - // ===== Kurse erzeugen ===== foreach (var sp in sports) { if (sem >= sp.Semester.Length) continue; - int demand = interested[sp].Count; - if (demand < sp.MinStudents) + if (remainingSemesterSlots <= 0) + break; + + var interested = demand[sp] + .Where(st => + st.Result![sem] == "Fehler" && + !st.Result.Contains(sp.Name)) + .ToList(); + + if (interested.Count < sp.MinStudents) continue; - int needed = (int)Math.Ceiling(demand / (double)sp.MaxStudents); - int allowed = Math.Min(needed, sp.Semester[sem]); - allowed = Math.Min(allowed, remainingCourseSlots); + int maxInstances = Math.Min(sp.Semester[sem], remainingSemesterSlots); + int instanceCount = 0; + int index = 0; - if (allowed <= 0) - continue; - - remainingCourseSlots -= allowed; - - for (int i = 0; i < allowed; i++) + while (interested.Count - index >= sp.MinStudents && + instanceCount < maxInstances) { - var instance = new CourseInstance + var inst = new CourseInstance { Sport = sp, Remaining = sp.MaxStudents }; - instances.Add(instance); - GeneratedCourses.Add((sem, instance)); - } - } - // ===== Schüler verteilen ===== - foreach (var sp in sports) - { - var pool = interested[sp]; - if (pool.Count == 0) - continue; + GeneratedCourses.Add((sem, inst)); + remainingSemesterSlots--; + instanceCount++; - var targets = instances.Where(i => i.Sport == sp).ToList(); - if (targets.Count == 0) - continue; + int filled = 0; - int idx = 0; - foreach (var st in pool) - { - if (st.SelectedCourseNames.Count <= sem) - continue; - - if (st.Result![sem] != "Fehler") - continue; - - bool assigned = false; - - for (int t = 0; t < targets.Count; t++) + while (filled < sp.MaxStudents && + index < interested.Count) { - var inst = targets[(idx + t) % targets.Count]; - if (inst.Remaining > 0) - { - inst.Remaining--; - inst.Students.Add(st); - st.Result[sem] = sp.Name; - idx++; - assigned = true; - break; - } + var st = interested[index++]; + + if (st.Result![sem] != "Fehler") + continue; + + inst.Students.Add(st); + inst.Remaining--; + st.Result[sem] = sp.Name; + filled++; } - if (!assigned) - continue; - } - } + // Falls Mindestanzahl nicht erreicht → Instanz verwerfen + if (inst.Students.Count < sp.MinStudents) + { + foreach (var st in inst.Students) + st.Result[sem] = "Fehler"; - // ===== Alternativen ===== - foreach (var st in students) - { - if (st.SelectedCourseNames.Count <= sem) - continue; - - if (st.Result![sem] != "Fehler") - continue; - - var sel = st.SelectedCourseNames[sem]; - if (!nameMap.TryGetValue(sel, out var wanted)) - continue; - - foreach (var altId in wanted.AlternativeCourses) - { - var altSport = sports.FirstOrDefault(s => s.ID == altId); - if (altSport == null) - continue; - - var inst = instances.FirstOrDefault(i => i.Sport == altSport && i.Remaining > 0); - if (inst == null) - continue; - - inst.Remaining--; - inst.Students.Add(st); - st.Result[sem] = altSport.Name; - break; + GeneratedCourses.Remove((sem, inst)); + remainingSemesterSlots++; + break; + } } } }