Enhance school simulation and management by integrating roster functionality, allowing for the installation and persistence of student and staff data. Update the school architecture to include a roster alongside existing components, ensuring proper validation against map layouts. Revise room definitions to support homeroom designations and update related tests to validate new functionalities and ensure robustness in roster handling.
This commit is contained in:
@@ -106,7 +106,7 @@ public class GameSocketTests(AppHostFixture fixture)
|
||||
Assert.Equal("floor-1", office.ParentId);
|
||||
Assert.Contains("Директор", office.Positions);
|
||||
Assert.NotEmpty(office.Items);
|
||||
var classroom = Assert.Single(snapshot.Nodes, node => node.Id == "classroom-1a");
|
||||
var classroom = Assert.Single(snapshot.Nodes, node => node.Id == "classroom-101");
|
||||
Assert.Equal(16, classroom.PupilSlots);
|
||||
Assert.Contains(classroom.Items, item => item.Name == "Парта" && item.Count == 16);
|
||||
Assert.Contains(classroom.Items, item => item.Name == "Стул" && item.Count == 1);
|
||||
|
||||
@@ -41,8 +41,8 @@ public class MapViewTests
|
||||
Assert.Empty(corridor.Positions);
|
||||
Assert.Equal("Коридор 1", corridor.Name);
|
||||
|
||||
var classroom = ru.Single(node => node.Id == "classroom-1a");
|
||||
Assert.Equal("Класс 1A", classroom.Name);
|
||||
var classroom = ru.Single(node => node.Id == "classroom-101");
|
||||
Assert.Equal("Класс 101", classroom.Name);
|
||||
Assert.Equal(
|
||||
[
|
||||
new MapViewItem("Доска", 1),
|
||||
@@ -53,7 +53,7 @@ public class MapViewTests
|
||||
classroom.Items);
|
||||
Assert.Equal(16, classroom.PupilSlots);
|
||||
Assert.Equal(["Учитель"], classroom.Positions);
|
||||
Assert.Equal("Classroom 1A", en.Single(node => node.Id == "classroom-1a").Name);
|
||||
Assert.Equal("Classroom 101", en.Single(node => node.Id == "classroom-101").Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -23,6 +23,8 @@ public class VanillaCoreTests
|
||||
Assert.Equal(["Sit"], catalog.Things["DirectorsChair"].Actions);
|
||||
Assert.True(catalog.Buildings.ContainsKey("GymBuilding"));
|
||||
Assert.True(catalog.Rooms.ContainsKey("Classroom"));
|
||||
Assert.True(catalog.Rooms["Classroom"].Homeroom);
|
||||
Assert.False(catalog.Rooms["ComputerLab"].Homeroom);
|
||||
Assert.Equal("Класс", catalog.Label("ru", catalog.Rooms["Classroom"]));
|
||||
Assert.Equal(["Teacher"], catalog.PositionsFor(DefKind.Room, "Classroom"));
|
||||
Assert.Equal(1, catalog.Things["StudentDesk"].PupilSlots);
|
||||
@@ -32,13 +34,16 @@ public class VanillaCoreTests
|
||||
Assert.Contains(catalog.Rooms["Classroom"].Slots, slot => slot.Key == "teacherChair" && slot.Thing == "Chair");
|
||||
Assert.Equal(16, catalog.Rooms["Classroom"].Slots.Single(slot => slot.Key == "studentDesks").Count);
|
||||
Assert.Equal(2, map.Buildings.Count);
|
||||
Assert.True(map.Rooms.Count >= 18, "Vanilla layout should look like a small school, not a stub.");
|
||||
Assert.Contains(map.Rooms, room => room.Id == "classroom-1a" && room.Label == "1A");
|
||||
var homerooms = map.Rooms.Where(room => room.Def == "Classroom").ToList();
|
||||
Assert.Equal(11, homerooms.Count);
|
||||
Assert.Contains(map.Rooms, room => room.Id == "classroom-101" && room.Label == "101");
|
||||
Assert.Contains(map.Rooms, room => room.Id == "classroom-207" && room.Label == "207");
|
||||
Assert.DoesNotContain(map.Rooms, room => room.Label is "1A" or "1B" or "2A" or "2B");
|
||||
Assert.Equal(
|
||||
16,
|
||||
map.Rooms.Single(room => room.Id == "classroom-1a").Slots.Single(slot => slot.Key == "studentDesks").Count);
|
||||
map.Rooms.Single(room => room.Id == "classroom-101").Slots.Single(slot => slot.Key == "studentDesks").Count);
|
||||
Assert.Contains(
|
||||
map.Rooms.Single(room => room.Id == "classroom-1a").Slots,
|
||||
map.Rooms.Single(room => room.Id == "classroom-101").Slots,
|
||||
slot => slot.Key == "teacherChair" && slot.Thing == "Chair");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,6 +90,37 @@ public class RosterGeneratorTests
|
||||
Assert.Contains(roster.People, person => person.IsStaff && person.IsParent);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void VanillaMap_HasElevenHomeroomsNotTheComputerLab()
|
||||
{
|
||||
var catalog = Fixtures.Catalog();
|
||||
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
|
||||
var map = CatalogLoader.LastDefaultMap(
|
||||
[CatalogLoader.CorePackId],
|
||||
PackDocuments.FromDirectory(CatalogLoader.CorePackId, root));
|
||||
Assert.NotNull(map);
|
||||
|
||||
var demand = SchoolDemand.From(catalog, map);
|
||||
Assert.Equal(11, demand.Classes.Count);
|
||||
Assert.Equal(11 * 16, demand.Seats.Count);
|
||||
Assert.DoesNotContain(demand.Classes, schoolClass => schoolClass.RoomId == "computer-lab");
|
||||
Assert.Contains(demand.Staff, opening => opening.RoomId == "computer-lab" && opening.Position == "Teacher");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RosterJson_RoundTripsAGeneratedRoster()
|
||||
{
|
||||
var roster = Fixtures.Generate(Fixtures.Classrooms(1));
|
||||
var json = RosterJson.Serialize(RosterDocument.From(7, roster));
|
||||
var loaded = RosterJson.Parse(json).ToRoster();
|
||||
|
||||
Assert.Equal(roster.People.Count, loaded.People.Count);
|
||||
Assert.Equal(roster.People[0].Name.Given, loaded.People[0].Name.Given);
|
||||
Assert.Equal(roster.People[0].Name.SurnameCases.Gen, loaded.People[0].Name.SurnameCases.Gen);
|
||||
Assert.True(RosterFit.Matches(loaded, SchoolDemand.From(Fixtures.Catalog(), Fixtures.Classrooms(1))));
|
||||
Assert.False(RosterFit.Matches(loaded, SchoolDemand.From(Fixtures.Catalog(), Fixtures.Classrooms(2))));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Assembly_DoesNotReferenceArchAspNetOrSockets()
|
||||
{
|
||||
|
||||
@@ -15,10 +15,18 @@
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\HSchool.Simulation\HSchool.Simulation.csproj" />
|
||||
<ProjectReference Include="..\..\src\HSchool.Content\HSchool.Content.csproj" />
|
||||
<ProjectReference Include="..\..\src\HSchool.People\HSchool.People.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="..\..\src\HSchool.Server\mods\core\**\*">
|
||||
<Link>vanilla\%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
using Arch.Core;
|
||||
using HSchool.Content;
|
||||
using HSchool.People;
|
||||
|
||||
namespace HSchool.Simulation.Tests;
|
||||
|
||||
public class PeopleInSchoolTests
|
||||
{
|
||||
private static readonly DateTime Start = new(2012, 4, 3, 6, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
[Fact]
|
||||
public void InstallPeople_FillsHomeroomsAndJobs()
|
||||
{
|
||||
var (catalog, map) = Vanilla();
|
||||
var demand = SchoolDemand.From(catalog, map);
|
||||
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Slavic", Start);
|
||||
|
||||
using var school = School.Create(1, "Полная", Start, catalog, map);
|
||||
school.InstallPeople(roster, seed: 1);
|
||||
school.Tick(1d / 20d, 5d);
|
||||
|
||||
Assert.Same(roster, school.Roster);
|
||||
Assert.Equal(11, roster.Classes.Count);
|
||||
Assert.Equal(demand.Seats.Count, roster.People.Count(person => person.IsStudent));
|
||||
Assert.Equal(demand.Staff.Count, roster.People.Count(person => person.IsStaff));
|
||||
Assert.True(RosterFit.Matches(roster, demand));
|
||||
|
||||
var peopleQuery = new QueryDescription().WithAll<PersonIdentity, PersonNeeds, PersonRoles>();
|
||||
var classesQuery = new QueryDescription().WithAll<ClassIdentity>();
|
||||
Assert.Equal(roster.People.Count, school.World.CountEntities(in peopleQuery));
|
||||
Assert.Equal(11, school.World.CountEntities(in classesQuery));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CoreNeeds_DoNotMoveOnTick()
|
||||
{
|
||||
var (catalog, map) = Vanilla();
|
||||
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 2, "Slavic", Start);
|
||||
using var school = School.Create(2, "Нужды", Start, catalog, map);
|
||||
school.InstallPeople(roster, seed: 2);
|
||||
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
school.Tick(1d / 20d, 5d);
|
||||
}
|
||||
|
||||
var hunger = new List<float>();
|
||||
var query = new QueryDescription().WithAll<PersonNeeds>();
|
||||
school.World.Query(in query, (ref PersonNeeds needs) => hunger.Add(needs.Values["Hunger"]));
|
||||
Assert.NotEmpty(hunger);
|
||||
Assert.All(hunger, value => Assert.Equal(1f, value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NeedDecay_DropsWhenTheDefSaysSo()
|
||||
{
|
||||
var catalog = new CatalogLoader().Load(
|
||||
[CatalogLoader.CorePackId],
|
||||
[
|
||||
new ContentDocument(
|
||||
CatalogLoader.CorePackId,
|
||||
"defs/needs/hunger.jsonc",
|
||||
"""{ "defName": "Hunger", "initial": 1, "decayPerHour": 1, "min": 0, "max": 1 }"""),
|
||||
]);
|
||||
|
||||
var world = World.Create();
|
||||
try
|
||||
{
|
||||
world.Create(new PersonNeeds(new Dictionary<string, float>(StringComparer.Ordinal) { ["Hunger"] = 1f }));
|
||||
NeedDecay.Apply(world, catalog, gameMinutes: 60);
|
||||
var query = new QueryDescription().WithAll<PersonNeeds>();
|
||||
world.Query(in query, (ref PersonNeeds needs) => Assert.Equal(0f, needs.Values["Hunger"]));
|
||||
}
|
||||
finally
|
||||
{
|
||||
World.Destroy(world);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SameSeed_InstallsTheSameNames()
|
||||
{
|
||||
var (catalog, map) = Vanilla();
|
||||
var first = RosterGenerator.Generate(catalog, map, schoolSeed: 9, "Slavic", Start);
|
||||
var second = RosterGenerator.Generate(catalog, map, schoolSeed: 9, "Slavic", Start);
|
||||
|
||||
using var a = School.Create(9, "А", Start, catalog, map);
|
||||
using var b = School.Create(9, "Б", Start, catalog, map);
|
||||
a.InstallPeople(first, 9);
|
||||
b.InstallPeople(second, 9);
|
||||
|
||||
Assert.Equal(
|
||||
first.People.Select(person => $"{person.Name.Surname} {person.Name.Given} {person.Name.Patronymic}"),
|
||||
second.People.Select(person => $"{person.Name.Surname} {person.Name.Given} {person.Name.Patronymic}"));
|
||||
}
|
||||
|
||||
private static (DefCatalog Catalog, MapLayout Map) Vanilla()
|
||||
{
|
||||
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
|
||||
var documents = PackDocumentsFrom(root);
|
||||
var catalog = new CatalogLoader().Load([CatalogLoader.CorePackId], documents);
|
||||
var map = CatalogLoader.LastDefaultMap([CatalogLoader.CorePackId], documents);
|
||||
Assert.NotNull(map);
|
||||
return (catalog, map);
|
||||
}
|
||||
|
||||
private static List<ContentDocument> PackDocumentsFrom(string packRoot)
|
||||
{
|
||||
var documents = new List<ContentDocument>();
|
||||
foreach (var path in Directory.EnumerateFiles(packRoot, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (!path.EndsWith(".jsonc", StringComparison.OrdinalIgnoreCase)
|
||||
&& !path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var relative = Path.GetRelativePath(packRoot, path).Replace('\\', '/');
|
||||
documents.Add(new ContentDocument(CatalogLoader.CorePackId, relative, File.ReadAllText(path)));
|
||||
}
|
||||
|
||||
return documents;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user