- Introduced the `HSchool.Ai` project, responsible for routing, day plans, and presence management. - Updated the `HSchool.Simulation` project to integrate with the new AI functionalities, improving decision-making and presence tracking. - Added `travelMinutes` to room and territory definitions, ensuring accurate movement calculations within the simulation. - Enhanced the `School` class to manage presence and implement empty-time skipping functionality. - Updated documentation to reflect the new AI features and their impact on school simulation. - Added tests for presence management and routing to ensure robust functionality and reliability.
158 lines
4.3 KiB
C#
158 lines
4.3 KiB
C#
using HSchool.Content;
|
|
using HSchool.People;
|
|
using HSchool.Schedule;
|
|
|
|
namespace HSchool.Ai;
|
|
|
|
/// <summary>
|
|
/// Where this person ought to be right now. The timetable and the job, not the walk graph.
|
|
/// </summary>
|
|
public static class Duty
|
|
{
|
|
/// <summary>
|
|
/// The room of the current obligation, or <see langword="null"/> when they should be off campus.
|
|
/// A hole in the class table sends the pupil to their homeroom.
|
|
/// </summary>
|
|
public static string? RoomAt(
|
|
Person person,
|
|
SchoolClass? schoolClass,
|
|
Timetable? timetable,
|
|
DefCatalog catalog,
|
|
DateTime time,
|
|
int weekDays)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(person);
|
|
ArgumentNullException.ThrowIfNull(catalog);
|
|
|
|
if (!ComesToday(person, timetable, catalog, time, weekDays))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (IsOtherStaff(person))
|
|
{
|
|
return person.WorkplaceRoomId;
|
|
}
|
|
|
|
var utc = DateTime.SpecifyKind(time, DateTimeKind.Utc);
|
|
if (!SchoolDay.IsWorkday(catalog, utc, weekDays))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var slot = SchoolDay.At(catalog, utc, weekDays);
|
|
var day = SchoolDay.WeekdayIndex(utc);
|
|
var lessons = LessonsToday(person, schoolClass, timetable, day);
|
|
if (lessons.Count == 0)
|
|
{
|
|
return person.WorkplaceRoomId;
|
|
}
|
|
|
|
if (slot.Kind == DaySlotKind.Lesson)
|
|
{
|
|
var current = lessons.FirstOrDefault(lesson => lesson.Period == slot.Index);
|
|
if (current is not null)
|
|
{
|
|
return current.RoomId;
|
|
}
|
|
|
|
return schoolClass?.RoomId ?? person.WorkplaceRoomId;
|
|
}
|
|
|
|
if (slot.Kind == DaySlotKind.Break)
|
|
{
|
|
var next = lessons.Where(lesson => lesson.Period > slot.Index).OrderBy(lesson => lesson.Period).FirstOrDefault();
|
|
return next?.RoomId ?? schoolClass?.RoomId ?? person.WorkplaceRoomId;
|
|
}
|
|
|
|
var frame = catalog.DayFrame;
|
|
if (frame is not null
|
|
&& SchoolDay.TryParseTime(frame.FirstLesson, out var first)
|
|
&& utc.TimeOfDay < first.ToTimeSpan())
|
|
{
|
|
return lessons[0].RoomId;
|
|
}
|
|
|
|
if (frame is not null
|
|
&& SchoolDay.TryLastBell(catalog, out var last)
|
|
&& utc.TimeOfDay >= last.ToTimeSpan())
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return lessons[0].RoomId;
|
|
}
|
|
|
|
public static bool ComesToday(
|
|
Person person,
|
|
Timetable? timetable,
|
|
DefCatalog catalog,
|
|
DateTime time,
|
|
int weekDays)
|
|
{
|
|
if (person.IsParent && !person.IsStaff && !person.IsStudent)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!SchoolDay.IsWorkday(catalog, time, weekDays))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (IsOtherStaff(person))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
var day = SchoolDay.WeekdayIndex(time);
|
|
if (person.IsStudent)
|
|
{
|
|
return timetable?.Lessons.Any(lesson =>
|
|
lesson.ClassId == person.ClassId && lesson.Day == day) == true;
|
|
}
|
|
|
|
if (person.IsStaff)
|
|
{
|
|
return timetable?.Lessons.Any(lesson =>
|
|
lesson.TeacherId == person.Id && lesson.Day == day) == true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static bool IsOtherStaff(Person person) =>
|
|
person.IsStaff && !Staffing.TeacherPosition.Equals(person.Position, StringComparison.Ordinal);
|
|
|
|
public static IReadOnlyList<LessonPlacement> LessonsToday(
|
|
Person person,
|
|
SchoolClass? schoolClass,
|
|
Timetable? timetable,
|
|
int day)
|
|
{
|
|
if (timetable is null)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
if (person.IsStudent)
|
|
{
|
|
var classId = person.ClassId ?? schoolClass?.Id;
|
|
return timetable.Lessons
|
|
.Where(lesson => lesson.ClassId == classId && lesson.Day == day)
|
|
.OrderBy(lesson => lesson.Period)
|
|
.ToArray();
|
|
}
|
|
|
|
if (person.IsStaff)
|
|
{
|
|
return timetable.Lessons
|
|
.Where(lesson => lesson.TeacherId == person.Id && lesson.Day == day)
|
|
.OrderBy(lesson => lesson.Period)
|
|
.ToArray();
|
|
}
|
|
|
|
return [];
|
|
}
|
|
}
|