using System; using System.Collections.Generic; using System.Linq; namespace spplus; public class CourseCrafter { public class CourseInstance { public Sport Sport = null!; public int Remaining; public List Students = new(); } public static List<(int Semester, CourseInstance Instance)> GeneratedCourses = new(); public static void Craft() { GeneratedCourses = new(); var settings = Settings.Instance; var students = settings.Students; var sports = settings.Sports; if (students.Count == 0 || sports.Count == 0) return; int semesterCount = sports.Max(s => s.Semester.Length); foreach (var st in students) st.Result = Enumerable.Repeat("Fehler", semesterCount).ToList(); var nameMap = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var sp in sports) { nameMap[sp.Name] = sp; foreach (var alt in sp.AlternativeNames) nameMap[alt] = sp; } // 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 remainingSemesterSlots = settings.NumCoursesPerSemester; foreach (var sp in sports) { if (sem >= sp.Semester.Length) continue; 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 maxInstances = Math.Min(sp.Semester[sem], remainingSemesterSlots); int instanceCount = 0; int index = 0; while (interested.Count - index >= sp.MinStudents && instanceCount < maxInstances) { var inst = new CourseInstance { Sport = sp, Remaining = sp.MaxStudents }; GeneratedCourses.Add((sem, inst)); remainingSemesterSlots--; instanceCount++; int filled = 0; while (filled < sp.MaxStudents && index < interested.Count) { var st = interested[index++]; if (st.Result![sem] != "Fehler") continue; inst.Students.Add(st); inst.Remaining--; st.Result[sem] = sp.Name; filled++; } // Falls Mindestanzahl nicht erreicht → Instanz verwerfen if (inst.Students.Count < sp.MinStudents) { foreach (var st in inst.Students) st.Result[sem] = "Fehler"; GeneratedCourses.Remove((sem, inst)); remainingSemesterSlots++; break; } } } } } public static string GenerateStatistics() { var settings = Settings.Instance; var students = settings.Students; if (GeneratedCourses == null || GeneratedCourses.Count == 0) return "Keine Kurse generiert."; int semesterCount = students .Where(s => s.Result != null) .Select(s => s.Result!.Count) .DefaultIfEmpty(0) .Max(); var sb = new System.Text.StringBuilder(); sb.AppendLine($"Anzahl generierter Kurse: {GeneratedCourses.Count}"); sb.AppendLine("Übersicht:"); // ===== Kursübersicht ===== var grouped = GeneratedCourses .GroupBy(g => new { g.Semester, g.Instance.Sport.Name }) .OrderBy(g => g.Key.Semester) .ThenBy(g => g.Key.Name); foreach (var group in grouped) { int counter = 1; foreach (var entry in group) { int semester = group.Key.Semester + 1; string sportName = group.Key.Name; string number = counter.ToString("D2"); int count = entry.Instance.Students.Count; sb.AppendLine( $"Semester {semester}: {sportName} {number}: {count} Schüler*innen" ); counter++; } } sb.AppendLine(); sb.AppendLine("Fehlerübersicht:"); // ===== Fehler pro Semester ===== for (int sem = 0; sem < semesterCount; sem++) { int errors = students.Count(st => st.Result != null && st.Result.Count > sem && st.Result[sem] == "Fehler"); sb.AppendLine($"Semester {sem + 1}: {errors} Fehler"); } return sb.ToString(); } }