first commit
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
using Core.Entities;
|
||||
|
||||
namespace Core.Calculation;
|
||||
|
||||
public enum UnassignedScheduleStrategy
|
||||
{
|
||||
BiggestGroup,
|
||||
IndividualEvents,
|
||||
AnyNotMeetingAlready,
|
||||
LevelOfEffort,
|
||||
Any
|
||||
}
|
||||
|
||||
public class UnassignedStudentScheduler
|
||||
{
|
||||
private readonly IList<Student> _students;
|
||||
private readonly IList<Team> _teams;
|
||||
private readonly IList<Team>[] _timeslots;
|
||||
|
||||
public UnassignedStudentScheduler(IList<Team> teams, IList<Team>[] timeslots)
|
||||
{
|
||||
_teams = teams;
|
||||
_students = teams.SelectMany(t => t.Students).Distinct().ToList();
|
||||
_timeslots = timeslots.Select(ts => ts.Select(t => t.Clone()).ToList()).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 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IList<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 unassigned = UnassignedStudents(_students, slot).ToList();
|
||||
while (unassigned.Count > 0)
|
||||
{
|
||||
var assignedStudents = slot.SelectMany(t => t.Students).Distinct();
|
||||
|
||||
var availableTeams = availableTeamSelector(scheduledTeams, assignedStudents);
|
||||
|
||||
var teamToAdd = availableTeams.FirstOrDefault();
|
||||
if (teamToAdd == null)
|
||||
break;
|
||||
|
||||
slot.Add(teamToAdd);
|
||||
scheduledTeams.Add(teamToAdd);
|
||||
|
||||
foreach (var student in teamToAdd.Students)
|
||||
unassigned.Remove(student);
|
||||
}
|
||||
}
|
||||
|
||||
return _timeslots;
|
||||
}
|
||||
|
||||
|
||||
// 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))
|
||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||
.Where(t => t.Students.Count > 1) //|| t.Event.Format is EventFormat.Individual
|
||||
//.OrderBy(t => scheduledTeams.Count(st => st.Name == t.Name))
|
||||
.OrderByDescending(t => t.Students.Count); // select descending greatest number of students assigned
|
||||
//.ThenBy(t => Random.Shared.Next()); // todo: sort by student historic record of event assignment
|
||||
|
||||
// find individual events unassigned students can work on
|
||||
private IEnumerable<Team> GetAvailableTeams_Individual(
|
||||
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
||||
_teams
|
||||
.Where(t => scheduledTeams.All(st => st.Name != t.Name))
|
||||
.Where(t => t.Event.Format == EventFormat.Individual || t.Students.Count == 1)
|
||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||
.Where(t => t.Students.Count > 0);
|
||||
|
||||
// find any unassigned event students can work on
|
||||
private IEnumerable<Team> GetAvailableTeams_AnyNotMeetingAlready(
|
||||
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
||||
_teams
|
||||
.Where(t => scheduledTeams.All(st => st.Name != t.Name))
|
||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||
.Where(t => t.Students.Count > 0);
|
||||
|
||||
private IEnumerable<Team> GetAvailableTeams_Any(
|
||||
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
||||
_teams
|
||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||
.Where(t => t.Students.Count > 0);
|
||||
|
||||
|
||||
// find teams where several unassigned students can work together
|
||||
private IEnumerable<Team> GetAvailableTeams_LevelOfEffort(
|
||||
IEnumerable<Team> scheduledTeams, IEnumerable<Student> assignedStudents) =>
|
||||
_teams
|
||||
.Where(t => scheduledTeams.All(st => st.Name != t.Name))
|
||||
.Select(t => t.CloneWithOmittedStudents(assignedStudents))
|
||||
.Where(t => t.Students.Count > 1) //|| t.Event.Format is EventFormat.Individual
|
||||
//.OrderBy(t => scheduledTeams.Count(st => st.Name == t.Name))
|
||||
.OrderByDescending(t => t.Event.LevelOfEffort); // select descending greatest number of students assigned
|
||||
|
||||
//add team to another timeslot, if any available
|
||||
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)))
|
||||
{
|
||||
if (timeslot.Any(t => t.Name == team.Name))
|
||||
continue;
|
||||
timeslot.Add(team);
|
||||
break;
|
||||
}
|
||||
|
||||
return _timeslots;
|
||||
}
|
||||
|
||||
// Keep the current events rolling for the other time slots
|
||||
public IList<Team>[] ExtendEvents()
|
||||
{
|
||||
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();
|
||||
|
||||
// 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++)
|
||||
{
|
||||
var sourceTimeslot = _timeslots[i];
|
||||
|
||||
for (var j = 0; j < _timeslots.Length; j++)
|
||||
{
|
||||
if (i == j)
|
||||
continue;
|
||||
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()))
|
||||
{
|
||||
targetTimeslot.Add(clonedTeam);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _timeslots;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user