using Arch.Core; using HSchool.Ai; using HSchool.Content; using HSchool.People; namespace HSchool.Simulation.Tests; public class ActivityTests { private static readonly DateTime Start = new(2012, 4, 3, 12, 0, 0, DateTimeKind.Utc); [Fact] public void EatLunch_FillsHungerAfterTheStatedMinutes() { using var school = OpenCafeteria(chairs: 8); Spawn(school, "a", hunger: 0.3f); Assert.True(school.TryStartAction("a", "EatLunch")); school.Tick(2.8d, 5d); Assert.Equal("EatLunch", ActivityOf(school, "a")); Assert.Equal(0.3f, HungerOf(school, "a")); school.Tick(0.2d, 5d); Assert.Null(ActivityOf(school, "a")); Assert.Equal(0.8f, HungerOf(school, "a")); } [Fact] public void FewerChairsThanPeople_ExtrasDoNotStart() { using var school = OpenCafeteria(chairs: 1); Spawn(school, "a", hunger: 0.3f); Spawn(school, "b", hunger: 0.3f); Assert.True(school.TryStartAction("a", "EatLunch")); Assert.False(school.TryStartAction("b", "EatLunch")); Assert.Equal("EatLunch", ActivityOf(school, "a")); Assert.Null(ActivityOf(school, "b")); } private static School OpenCafeteria(int chairs) { var catalog = new CatalogLoader().Load( [CatalogLoader.CorePackId], [ new ContentDocument("core", "defs/actions/sit.jsonc", """{ "defName": "Sit", "abstract": true }"""), new ContentDocument( "core", "defs/actions/eat.jsonc", """ { "defName": "EatLunch", "room": "Cafeteria", "thing": "Chair", "minutes": 15, "need": "Hunger", "needGain": 0.5, "roles": ["student"] } """), new ContentDocument( "core", "defs/needs/hunger.jsonc", """{ "defName": "Hunger", "initial": 1, "decayPerHour": 0, "min": 0, "max": 1 }"""), new ContentDocument("core", "defs/things/chair.jsonc", """{ "defName": "Chair", "actions": ["Sit"] }"""), new ContentDocument("core", "defs/territories/yard.jsonc", """{ "defName": "Yard", "travelMinutes": 1 }"""), new ContentDocument("core", "defs/buildings/main.jsonc", """{ "defName": "Main" }"""), new ContentDocument("core", "defs/floors/floor.jsonc", """{ "defName": "Floor" }"""), new ContentDocument( "core", "defs/rooms/cafeteria.jsonc", """ { "defName": "Cafeteria", "slots": [{ "key": "seats", "thing": "Chair" }], "travelMinutes": 1 } """), ]); var map = new MapLayout { Territory = new TerritoryNode { Id = "yard", Def = "Yard" }, Buildings = [new BuildingNode { Id = "main", Def = "Main" }], Floors = [new FloorNode { Id = "floor-1", Def = "Floor", Building = "main" }], Rooms = [ new RoomNode { Id = "cafe", Def = "Cafeteria", Building = "main", Floor = "floor-1", Slots = [new SlotFill { Key = "seats", Thing = "Chair", Count = chairs }], }, ], Links = [new MapLink { A = "yard", B = "cafe" }], }; MapValidator.Validate(map, catalog); return School.Create(1, "Столовая", Start, catalog, map); } private static void Spawn(School school, string id, float hunger) { school.World.Create( new PersonIdentity(id, "f", false, Start, DummyName()), new PersonRoles(true, false, false, null, null, null), new PersonNeeds(new Dictionary(StringComparer.Ordinal) { ["Hunger"] = hunger }), new Presence("cafe", 0f, "cafe", false, []), PersonActivity.Idle); } private static string? ActivityOf(School school, string id) { string? found = null; var query = new QueryDescription().WithAll(); school.World.Query(in query, (ref PersonIdentity identity, ref PersonActivity activity) => { if (identity.Id.Equals(id, StringComparison.Ordinal)) { found = activity.ActionId; } }); return found; } private static float HungerOf(School school, string id) { var value = float.NaN; var query = new QueryDescription().WithAll(); school.World.Query(in query, (ref PersonIdentity identity, ref PersonNeeds needs) => { if (identity.Id.Equals(id, StringComparison.Ordinal)) { value = needs.Values["Hunger"]; } }); return value; } private static PersonName DummyName() { var cases = new CaseTable { Nom = "А", Gen = "А", Dat = "А", Acc = "А", Ins = "А", Pre = "А", }; return new PersonName("А", "А", "А", cases, cases, cases); } }