Files
h-school/tests/HSchool.Ai.Tests/WalkingTests.cs
T
Leonid Pershin 4137400621
ci / server (push) Failing after 3m39s
ci / client (push) Successful in 14s
Enhance AI and simulation components with presence management and routing capabilities
- 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.
2026-08-19 16:33:52 +03:00

157 lines
6.9 KiB
C#

using HSchool.Content;
using HSchool.People;
using HSchool.Schedule;
namespace HSchool.Ai.Tests;
public class WalkingTests
{
private static readonly DateTime TuesdayLesson = new(2012, 4, 3, 8, 45, 0, DateTimeKind.Utc);
private static readonly DateTime Sunday = new(2012, 4, 8, 10, 0, 0, DateTimeKind.Utc);
private static readonly DateTime SpringBreak = new(2012, 3, 31, 10, 0, 0, DateTimeKind.Utc);
[Fact]
public void PathFrom201ToFirstFloorRestroom_GoesThroughStairsAndCorridors()
{
var (catalog, map) = Fixtures.Vanilla();
var walks = WalkGraph.Build(catalog, map);
var hops = walks.Path("classroom-201", "restroom-1");
Assert.Equal(["corridor-2", "stairs-2", "stairs-1", "corridor-1", "restroom-1"], hops);
Assert.DoesNotContain("yard", hops);
Assert.Equal(6.5f, walks.Minutes("classroom-201", "restroom-1"));
}
[Fact]
public void PresenceStepper_TakesTheSameMinutesAsTheGraph()
{
var (catalog, map) = Fixtures.Vanilla();
var walks = WalkGraph.Build(catalog, map);
var hops = walks.Path("classroom-201", "restroom-1");
var cost = walks.Minutes("classroom-201", "restroom-1");
var presence = new Presence("classroom-201", 0f, "restroom-1", HeadingHome: false, [.. hops]);
var walked = PresenceStepper.Advance(presence, walks, cost);
Assert.Equal("restroom-1", walked.NodeId);
Assert.Empty(walked.Path);
Assert.Equal(0f, walked.RemainingMinutes);
var stepwise = presence;
var elapsed = 0f;
const float step = 0.25f;
while (stepwise.NodeId != "restroom-1" || stepwise.Path.Length > 0 || stepwise.RemainingMinutes > 0)
{
stepwise = PresenceStepper.Advance(stepwise, walks, step);
elapsed += step;
Assert.True(elapsed <= cost + step);
}
Assert.Equal(cost, elapsed);
}
[Fact]
public void GymAfterClassroom101_CostsSixMinutesAgainstATenMinuteBreak()
{
var (catalog, map) = Fixtures.Vanilla();
var walks = WalkGraph.Build(catalog, map);
var hops = walks.Path("classroom-101", "gym-hall");
Assert.Equal(["corridor-1", "porch", "yard", "gym-hall"], hops);
Assert.Equal(6f, walks.Minutes("classroom-101", "gym-hall"));
Assert.Equal(10, catalog.DayFrame!.BreakMinutes);
Assert.True(walks.Minutes("classroom-101", "gym-hall") < catalog.DayFrame.BreakMinutes);
}
[Fact]
public void ComesToday_IsFalseOnSundayAndHolidays()
{
var (catalog, map) = Fixtures.Vanilla();
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Slavic", TuesdayLesson);
var pupil = roster.People.First(person => person.IsStudent);
var schoolClass = roster.Classes.First(row => row.Id == pupil.ClassId);
var table = new Timetable(
[new LessonPlacement(schoolClass.Id, "Mathematics", "t1", schoolClass.RoomId, Day: 1, Period: 1)],
[]);
Assert.True(Duty.ComesToday(pupil, table, catalog, TuesdayLesson, weekDays: 5));
Assert.False(Duty.ComesToday(pupil, table, catalog, Sunday, weekDays: 5));
Assert.False(Duty.ComesToday(pupil, table, catalog, SpringBreak, weekDays: 5));
Assert.False(Duty.ComesToday(
roster.People.First(person => person.IsParent && !person.IsStaff && !person.IsStudent),
table,
catalog,
TuesdayLesson,
weekDays: 5));
}
[Fact]
public void ClassWithoutLessons_StaysAway_TeacherWithAnotherClassComes()
{
var (catalog, map) = Fixtures.Vanilla();
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Slavic", TuesdayLesson);
var idle = roster.Classes[0];
var busy = roster.Classes[1];
var idlePupil = roster.People.First(person => person.Id == idle.PupilIds[0]);
var busyPupil = roster.People.First(person => person.Id == busy.PupilIds[0]);
var teacher = roster.People.First(person => person.IsParent && !person.IsStudent && !person.IsStaff) with
{
IsStaff = true,
Position = Staffing.TeacherPosition,
};
var table = new Timetable(
[new LessonPlacement(busy.Id, "Mathematics", teacher.Id, busy.RoomId, Day: 1, Period: 1)],
[]);
Assert.False(Duty.ComesToday(idlePupil, table, catalog, TuesdayLesson, weekDays: 5));
Assert.True(Duty.ComesToday(busyPupil, table, catalog, TuesdayLesson, weekDays: 5));
Assert.True(Duty.ComesToday(teacher, table, catalog, TuesdayLesson, weekDays: 5));
Assert.Null(Duty.RoomAt(idlePupil, idle, table, catalog, TuesdayLesson, weekDays: 5));
Assert.Equal(busy.RoomId, Duty.RoomAt(busyPupil, busy, table, catalog, TuesdayLesson, weekDays: 5));
Assert.Equal(busy.RoomId, Duty.RoomAt(teacher, null, table, catalog, TuesdayLesson, weekDays: 5));
}
[Fact]
public void SameSeedAndMap_YieldTheSameDayPlan()
{
var (catalog, map) = Fixtures.Vanilla();
var walks = WalkGraph.Build(catalog, map);
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 9, "Slavic", TuesdayLesson);
var pupil = roster.People.First(person => person.IsStudent);
var schoolClass = roster.Classes.First(row => row.Id == pupil.ClassId);
var table = new Timetable(
[new LessonPlacement(schoolClass.Id, "Mathematics", "t1", schoolClass.RoomId, Day: 1, Period: 1)],
[]);
var first = DayPlans.Build(catalog, walks, pupil, schoolClass, table, TuesdayLesson, weekDays: 5, schoolSeed: 9);
var second = DayPlans.Build(catalog, walks, pupil, schoolClass, table, TuesdayLesson, weekDays: 5, schoolSeed: 9);
Assert.Equal(first, second);
Assert.True(first.Comes);
Assert.Equal(schoolClass.RoomId, first.FirstRoom);
Assert.True(first.AppearAt < DateTime.SpecifyKind(TuesdayLesson.Date.Add(new TimeSpan(8, 30, 0)), DateTimeKind.Utc));
}
[Fact]
public void Assembly_DoesNotReferenceArchAspNetOrSockets()
{
var names = typeof(WalkGraph).Assembly.GetReferencedAssemblies().Select(assembly => assembly.Name!);
Assert.DoesNotContain(names, name => name.StartsWith("Arch", StringComparison.OrdinalIgnoreCase));
Assert.DoesNotContain(names, name => name.Contains("AspNet", StringComparison.OrdinalIgnoreCase));
Assert.DoesNotContain(names, name => name.Contains("Sockets", StringComparison.OrdinalIgnoreCase));
}
[Fact]
public void Sources_DoNotUseWallClock()
{
var root = Path.Combine(Fixtures.RepoRoot(), "src", "HSchool.Ai");
foreach (var path in Directory.EnumerateFiles(root, "*.cs"))
{
var text = File.ReadAllText(path);
Assert.DoesNotContain("DateTime.Now", text, StringComparison.Ordinal);
Assert.DoesNotContain("DateTime.UtcNow", text, StringComparison.Ordinal);
}
}
}