Add Team functions

This commit is contained in:
2025-09-22 12:53:46 -04:00
parent 3daa3b81b3
commit dc83a18d76
48 changed files with 2364 additions and 633 deletions
+37 -45
View File
@@ -4,49 +4,42 @@ namespace Core.Calculation;
public class UnassignedStudentScheduler
{
private readonly IList<Student> _students;
private readonly IList<Team> _teams;
private readonly IList<Team>[] _timeslots;
private readonly Student[] _students;
private readonly Team[] _teams;
private readonly IList<Team>[] _timeSlots;
public UnassignedStudentScheduler(IList<Team> teams, IList<Team>[] timeslots)
public UnassignedStudentScheduler(Team[] teams, Team[][] timeslots)
{
_teams = teams;
_students = teams.SelectMany(t => t.Students).Distinct().ToList();
_timeslots = timeslots.Select(ts => ts.Select(t => t.Clone()).ToList()).ToArray();
_students = teams.SelectMany(t => t.Students).Distinct().ToArray();
_timeSlots = timeslots.Select(ts => ts.Select(t => t.Clone()).ToArray()).ToArray();
}
public static IEnumerable<Student> UnassignedStudents(IList<Student> students, IList<Team> timeSlot)
=> students.Where(s => !timeSlot.SelectMany(t => t.Students).Contains(s));
public static IEnumerable<Student>[] UnassignedStudents(IList<Student> students, IList<Team>[] schedule)
=> schedule.Select(ts => UnassignedStudents(students, ts)).ToArray();
public TeamSchedulerSolution ScheduleStrategy(UnassignedScheduleStrategy scheduleStrategy)
{
var ss = scheduleStrategy switch
{
UnassignedScheduleStrategy.BiggestGroup => ScheduleStrategy(GetAvailableTeams_BiggestGroup),
UnassignedScheduleStrategy.IndividualEvents => ScheduleStrategy(GetAvailableTeams_Individual),
UnassignedScheduleStrategy.AnyNotMeetingAlready => ScheduleStrategy(GetAvailableTeams_AnyNotMeetingAlready),
UnassignedScheduleStrategy.Any => ScheduleStrategy(GetAvailableTeams_Any),
UnassignedScheduleStrategy.LevelOfEffort => ScheduleStrategy(GetAvailableTeams_LevelOfEffort),
_ => throw new ArgumentOutOfRangeException(nameof(scheduleStrategy), scheduleStrategy, null)
};
public IList<Team>[] ScheduleStrategy(UnassignedScheduleStrategy scheduleStrategy)
{
switch (scheduleStrategy)
{
case UnassignedScheduleStrategy.BiggestGroup:
return ScheduleStrategy(GetAvailableTeams_BiggestGroup);
case UnassignedScheduleStrategy.IndividualEvents:
return ScheduleStrategy(GetAvailableTeams_Individual);
case UnassignedScheduleStrategy.AnyNotMeetingAlready:
return ScheduleStrategy(GetAvailableTeams_AnyNotMeetingAlready);
case UnassignedScheduleStrategy.Any:
return ScheduleStrategy(GetAvailableTeams_Any);
case UnassignedScheduleStrategy.LevelOfEffort:
return ScheduleStrategy(GetAvailableTeams_LevelOfEffort);
default:
throw new ArgumentOutOfRangeException(nameof(scheduleStrategy), scheduleStrategy, null);
}
}
return new TeamSchedulerSolution(ss, "Success?");
}
public IList<Team>[] ScheduleStrategy(Func<IEnumerable<Team>, IEnumerable<Student>, IEnumerable<Team>> availableTeamSelector)
public Team[][] ScheduleStrategy(Func<IEnumerable<Team>, IEnumerable<Student>, IEnumerable<Team>> availableTeamSelector)
{
// Find stuff for unassigned students in each timeslot
var scheduledTeams = _timeslots.SelectMany(list => list).Distinct().ToList();
foreach (var slot in _timeslots)
var scheduledTeams = _timeSlots.SelectMany(list => list).Distinct().ToList();
foreach (var slot in _timeSlots)
{
var unassigned = UnassignedStudents(_students, slot).ToList();
while (unassigned.Count > 0)
@@ -67,15 +60,14 @@ public class UnassignedStudentScheduler
}
}
return _timeslots;
return _timeSlots.Select(e => e.ToArray()).ToArray();
}
// find teams where several unassigned students can work together
private IEnumerable<Team> GetAvailableTeams_BiggestGroup(
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
_teams
.Where(t => scheduledTeams.All(st => st.Name != t.Name))
.Where(t => scheduledTeams.All(st => st.TeamId != t.TeamId))
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
.Where(t => t.Students.Count > 1) //|| t.Event.EventFormat is EventFormat.Individual
//.OrderBy(t => scheduledTeams.Count(st => st.Name == t.Name))
@@ -86,7 +78,7 @@ public class UnassignedStudentScheduler
private IEnumerable<Team> GetAvailableTeams_Individual(
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
_teams
.Where(t => scheduledTeams.All(st => st.Name != t.Name))
.Where(t => scheduledTeams.All(st => st.TeamId != t.TeamId))
.Where(t => t.Event.EventFormat == EventFormat.Individual || t.Students.Count == 1)
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
.Where(t => t.Students.Count > 0);
@@ -95,7 +87,7 @@ public class UnassignedStudentScheduler
private IEnumerable<Team> GetAvailableTeams_AnyNotMeetingAlready(
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
_teams
.Where(t => scheduledTeams.All(st => st.Name != t.Name))
.Where(t => scheduledTeams.All(st => st.TeamId != t.TeamId))
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
.Where(t => t.Students.Count > 0);
@@ -110,7 +102,7 @@ public class UnassignedStudentScheduler
private IEnumerable<Team> GetAvailableTeams_LevelOfEffort(
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
_teams
.Where(t => scheduledTeams.All(st => st.Name != t.Name))
.Where(t => scheduledTeams.All(st => st.TeamId != t.TeamId))
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
.Where(t => t.Students.Count > 1) //|| t.Event.EventFormat is EventFormat.Individual
//.OrderBy(t => scheduledTeams.Count(st => st.Name == t.Name))
@@ -120,38 +112,38 @@ public class UnassignedStudentScheduler
public IList<Team>[] AddAdditionalTimeSlot(Team team)
{
// sort by how many teammembers are already in that timeslot, descending
foreach (var timeslot in _timeslots.OrderBy(ts => ts.SelectMany(t => t.Students).Count(team.Students.Contains)))
foreach (var timeslot in _timeSlots.OrderBy(ts => ts.SelectMany(t => t.Students).Count(team.Students.Contains)))
{
if (timeslot.Any(t => t.Name == team.Name))
if (timeslot.Any(t => t.TeamId == team.TeamId))
continue;
timeslot.Add(team);
break;
}
return _timeslots;
return _timeSlots;
}
// Keep the current events rolling for the other time slots
public IList<Team>[] ExtendEvents()
{
var scheduledTeams = _timeslots.SelectMany(list => list).Distinct().ToList();
var scheduledTeams = _timeSlots.SelectMany(list => list).Distinct().ToList();
// get all the students in each time slot
var timeslotStudents = _timeslots.Select(ts => ts.SelectMany(t => t.Students).Distinct()).ToArray();
var timeslotStudents = _timeSlots.Select(ts => ts.SelectMany(t => t.Students).Distinct()).ToArray();
// clone teams from timeslot for each other timeslot, removing the students
//var copiedTeams = new Dictionary<int, IList<Team>>();
for (var i = 0; i < _timeslots.Length; i++)
for (var i = 0; i < _timeSlots.Length; i++)
{
var sourceTimeslot = _timeslots[i];
var sourceTimeslot = _timeSlots[i];
for (var j = 0; j < _timeslots.Length; j++)
for (var j = 0; j < _timeSlots.Length; j++)
{
if (i == j)
continue;
var targetTimeslot = _timeslots[j];
var targetTimeslot = _timeSlots[j];
var targetTimeslotStudents = targetTimeslot.SelectMany(t => t.Students).Distinct();
var clonedTeams = sourceTimeslot.Select(t => t.CloneWithOmittedStudents(targetTimeslotStudents));
foreach (var clonedTeam in clonedTeams.Where(t => t.Students.Any()))
@@ -161,6 +153,6 @@ public class UnassignedStudentScheduler
}
}
return _timeslots;
return _timeSlots;
}
}