Replace selectable name sets with a country that owns names and climate presets.

Create picks a country; climate is rolled from the school seed and stored. Old Slavic saves lift as Russia.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-20 02:55:13 +03:00
co-authored by Cursor
parent 450495f666
commit 1b66c89cd7
58 changed files with 902 additions and 477 deletions
@@ -17,7 +17,7 @@ public class ApplicantPoolTests
{
var roster = Fixtures.Generate(Fixtures.VanillaMap());
var first = Create(roster);
var next = first.Advance(Fixtures.Catalog(), roster, Fixtures.SchoolSeed, "Slavic", Fixtures.AsOf.AddDays(7));
var next = first.Advance(Fixtures.Catalog(), roster, Fixtures.SchoolSeed, "Russia", Fixtures.AsOf.AddDays(7));
Assert.NotEqual(first.Week, next.Week);
var stayed = first.Applicants
@@ -45,12 +45,12 @@ public class ApplicantPoolTests
var roster = Fixtures.Generate(Fixtures.VanillaMap());
var size = catalog.StaffingRules!.PoolSize;
var asOf = Fixtures.AsOf;
var pool = ApplicantPool.Create(catalog, roster, Fixtures.SchoolSeed, "Slavic", asOf);
var pool = ApplicantPool.Create(catalog, roster, Fixtures.SchoolSeed, "Russia", asOf);
for (var week = 0; week < 50; week++)
{
asOf = asOf.AddDays(7);
pool = pool.Advance(catalog, roster, Fixtures.SchoolSeed, "Slavic", asOf);
pool = pool.Advance(catalog, roster, Fixtures.SchoolSeed, "Russia", asOf);
Assert.Equal(size, pool.Applicants.Count);
}
}
@@ -94,7 +94,7 @@ public class ApplicantPoolTests
for (var seed = 1; seed <= 20 && pool is null; seed++)
{
var candidate = Fixtures.Generate(Fixtures.VanillaMap(), seed);
var created = ApplicantPool.Create(Fixtures.Catalog(), candidate, seed, "Slavic", Fixtures.AsOf);
var created = ApplicantPool.Create(Fixtures.Catalog(), candidate, seed, "Russia", Fixtures.AsOf);
if (created.Applicants.Any(applicant => candidate.People.Any(person => person.Id == applicant.Person.Id)))
{
roster = candidate;
@@ -132,7 +132,7 @@ public class ApplicantPoolTests
}
private static ApplicantPool Create(Roster roster) =>
ApplicantPool.Create(Fixtures.Catalog(), roster, Fixtures.SchoolSeed, "Slavic", Fixtures.AsOf);
ApplicantPool.Create(Fixtures.Catalog(), roster, Fixtures.SchoolSeed, "Russia", Fixtures.AsOf);
private static string Snapshot(ApplicantPool pool) =>
string.Join('\n', pool.Applicants.Select(applicant =>
+1 -1
View File
@@ -96,7 +96,7 @@ internal static class Fixtures
}
public static Roster Generate(MapLayout map, int seed = SchoolSeed) =>
RosterGenerator.Generate(Catalog(), map, seed, "Slavic", AsOf, "RussianLanguage");
RosterGenerator.Generate(Catalog(), map, seed, "Russia", AsOf, "RussianLanguage");
public static string RepoRoot()
{
@@ -8,7 +8,7 @@ public class GoldenRosterTests
public void GoldenRoster_MatchesFixture()
{
var roster = Fixtures.Generate(Fixtures.Classrooms(4));
var actual = RosterFingerprint.Format(roster, Fixtures.SchoolSeed, "Slavic", Native);
var actual = RosterFingerprint.Format(roster, Fixtures.SchoolSeed, "Russia", Native);
var relative = Path.Combine("golden", "roster.txt");
var output = Path.Combine(AppContext.BaseDirectory, relative);
var source = Path.Combine(Fixtures.RepoRoot(), "tests", "HSchool.People.Tests", relative);
+2 -2
View File
@@ -21,8 +21,8 @@ public class GoldenSaveDump
var catalog = Fixtures.Catalog();
var map = TwoClassrooms();
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Slavic", Fixtures.AsOf, "RussianLanguage");
var pool = ApplicantPool.Create(catalog, roster, schoolSeed: 1, "Slavic", Fixtures.AsOf, "RussianLanguage");
var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Russia", Fixtures.AsOf, "RussianLanguage");
var pool = ApplicantPool.Create(catalog, roster, schoolSeed: 1, "Russia", Fixtures.AsOf, "RussianLanguage");
var people = RosterJson.Serialize(RosterDocument.From(1, roster, pool));
var mapNode = JsonNode.Parse(JsonSerializer.Serialize(map, Jsonc.SerializerOptions));
var current = SchoolJson(mapNode, includeNative: true);
+4 -4
View File
@@ -69,7 +69,7 @@ public class ReviewFixTests
for (var year = 0; year < 4; year++)
{
var before = roster.People.ToDictionary(person => person.Id, StringComparer.Ordinal);
roster = YearlyIntake.Apply(catalog, roster, Fixtures.SchoolSeed, "Slavic", date);
roster = YearlyIntake.Apply(catalog, roster, Fixtures.SchoolSeed, "Russia", date);
date = date.AddYears(1);
foreach (var person in roster.People)
@@ -98,7 +98,7 @@ public class ReviewFixTests
catalog,
roster,
Fixtures.SchoolSeed,
"Slavic",
"Russia",
new DateTime(2012, 9, 1, 0, 0, 0, DateTimeKind.Utc));
// Every seat an intake fills is a first-year seat, so a brand-new family that took two or
@@ -153,7 +153,7 @@ public class ReviewFixTests
public void AChildOfASingleMother_StillCarriesTheFathersSurnameAndPatronymic()
{
var catalog = Fixtures.Catalog();
var names = catalog.NameSets["Slavic"];
var names = catalog.Countries["Russia"].Names;
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
var people = roster.People.ToDictionary(person => person.Id, StringComparer.Ordinal);
@@ -180,7 +180,7 @@ public class ReviewFixTests
public void ASingleMotherFamily_CanStillTakeInAYoungerSibling()
{
var catalog = Fixtures.Catalog();
var names = catalog.NameSets["Slavic"];
var names = catalog.Countries["Russia"].Names;
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
var people = roster.People.ToDictionary(person => person.Id, StringComparer.Ordinal);
var motherOnly = roster.Families
@@ -6,11 +6,11 @@ namespace HSchool.People.Tests;
/// </summary>
internal static class RosterFingerprint
{
public static string Format(Roster roster, int seed, string nameSet, string native)
public static string Format(Roster roster, int seed, string country, string native)
{
var lines = new List<string>
{
$"# seed={seed} nameset={nameSet} native={native} people={roster.People.Count} classes={roster.Classes.Count}",
$"# seed={seed} country={country} native={native} people={roster.People.Count} classes={roster.Classes.Count}",
};
foreach (var schoolClass in roster.Classes.OrderBy(row => row.Id, StringComparer.Ordinal))
@@ -3,7 +3,7 @@ namespace HSchool.People.Tests;
public class RosterGeneratorTests
{
[Fact]
public void SameSeedMapAndNameSet_YieldTheSameRoster()
public void SameSeedMapAndCountry_YieldTheSameRoster()
{
var map = Fixtures.Classrooms(4);
var a = Fixtures.Generate(map);
@@ -70,11 +70,11 @@ public class SkillGrantTests
catalog,
Fixtures.Classrooms(11),
Fixtures.SchoolSeed,
"Slavic",
"Russia",
Fixtures.AsOf,
"BelarusianLanguage");
var max = catalog.NameSets["Slavic"].RelatedLanguageMax;
var max = catalog.Countries["Russia"].Names.RelatedLanguageMax;
Assert.All(
roster.People,
person =>
@@ -97,7 +97,7 @@ public class SkillGrantTests
public void RelatedLanguages_AreCommonAndStayLow()
{
var catalog = Fixtures.Catalog();
var max = catalog.NameSets["Slavic"].RelatedLanguageMax;
var max = catalog.Countries["Russia"].Names.RelatedLanguageMax;
var roster = Fixtures.Generate(Fixtures.Classrooms(11));
Assert.Contains(
roster.People,
@@ -138,7 +138,7 @@ public class SkillGrantTests
[Fact]
public void ReloadWithoutASavedPick_TakesTheFirstLanguageInsteadOfRolling()
{
var names = Fixtures.Catalog().NameSets["Slavic"];
var names = Fixtures.Catalog().Countries["Russia"].Names;
Assert.True(names.Spoken.Count > 1, "this pins behaviour that only matters for a multi-language set");
foreach (var seed in new[] { 1, 2, 7, 20260818 })
@@ -164,8 +164,8 @@ public class SkillGrantTests
public void OmittedNativeLanguage_IsStableForTheSameSeed()
{
var map = Fixtures.Classrooms(2);
var a = RosterGenerator.Generate(Fixtures.Catalog(), map, 7, "Slavic", Fixtures.AsOf);
var b = RosterGenerator.Generate(Fixtures.Catalog(), map, 7, "Slavic", Fixtures.AsOf);
var a = RosterGenerator.Generate(Fixtures.Catalog(), map, 7, "Russia", Fixtures.AsOf);
var b = RosterGenerator.Generate(Fixtures.Catalog(), map, 7, "Russia", Fixtures.AsOf);
Assert.Equal(
a.People.Select(person => string.Join(',', person.Skills.Keys.Order(StringComparer.Ordinal))),
b.People.Select(person => string.Join(',', person.Skills.Keys.Order(StringComparer.Ordinal))));
@@ -195,7 +195,7 @@ public class SkillGrantTests
catalog,
before,
Fixtures.SchoolSeed,
"Slavic",
"Russia",
new DateTime(2012, 9, 1, 0, 0, 0, DateTimeKind.Utc));
var grown = after.People.First(person => person.Id == pupil.Id);
Assert.True(grown.ClassId is { } classId && after.Classes.Any(schoolClass =>
+2 -2
View File
@@ -178,7 +178,7 @@ public class StaffingTests
for (var seed = 1; seed <= 30 && pool is null; seed++)
{
var candidate = Fixtures.Generate(map, seed);
var created = ApplicantPool.Create(catalog, candidate, seed, "Slavic", Fixtures.AsOf);
var created = ApplicantPool.Create(catalog, candidate, seed, "Russia", Fixtures.AsOf);
var rosterIds = candidate.People.Select(person => person.Id).ToHashSet(StringComparer.Ordinal);
var parentHere = created.Applicants.FirstOrDefault(applicant => rosterIds.Contains(applicant.Person.Id));
var generatedHere = created.Applicants.FirstOrDefault(applicant => applicant.Person.Id.StartsWith('a'));
@@ -292,7 +292,7 @@ public class StaffingTests
var catalog = Fixtures.Catalog();
var map = Fixtures.VanillaMap();
var roster = Fixtures.Generate(map);
var pool = ApplicantPool.Create(catalog, roster, Fixtures.SchoolSeed, "Slavic", Fixtures.AsOf);
var pool = ApplicantPool.Create(catalog, roster, Fixtures.SchoolSeed, "Russia", Fixtures.AsOf);
return (catalog, map, roster, pool);
}
@@ -23,7 +23,7 @@ public class YearlyIntakeTests
Assert.Equal(4, before.Classes.Max(schoolClass => schoolClass.Year));
Assert.DoesNotContain(before.Classes, schoolClass => schoolClass.Year == 11);
var after = YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Slavic", September);
var after = YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Russia", September);
var demand = SchoolDemand.From(catalog, map);
Assert.True(RosterFit.Matches(after, demand));
@@ -46,8 +46,8 @@ public class YearlyIntakeTests
var map = Fixtures.Classrooms(4);
var catalog = Fixtures.Catalog();
var before = Fixtures.Generate(map);
var a = Snapshot(YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Slavic", September));
var b = Snapshot(YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Slavic", September));
var a = Snapshot(YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Russia", September));
var b = Snapshot(YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Russia", September));
Assert.Equal(a, b);
}
@@ -57,7 +57,7 @@ public class YearlyIntakeTests
var map = Fixtures.Classrooms(4);
var catalog = Fixtures.Catalog();
var before = Fixtures.Generate(map);
var after = YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Slavic", September);
var after = YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Russia", September);
var people = after.People.ToDictionary(person => person.Id, StringComparer.Ordinal);
var yearOf = after.Classes.ToDictionary(schoolClass => schoolClass.Id, schoolClass => schoolClass.Year, StringComparer.Ordinal);
@@ -106,7 +106,7 @@ public class YearlyIntakeTests
}
}
var after = YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Slavic", September);
var after = YearlyIntake.Apply(catalog, before, Fixtures.SchoolSeed, "Russia", September);
var afterIds = after.People.Select(person => person.Id).ToHashSet(StringComparer.Ordinal);
Assert.NotEmpty(shouldLeave);
Assert.All(shouldLeave, id => Assert.DoesNotContain(id, afterIds));
+1 -1
View File
@@ -1,4 +1,4 @@
# seed=20260818 nameset=Slavic native=RussianLanguage people=130 classes=4
# seed=20260818 country=Russia native=RussianLanguage people=130 classes=4
class class-classroom-00 year=1 letter=А room=classroom-00 capacity=16 pupils=f1.c1,f1.c2,f13.c0,f18.c0,f19.c0,f24.c1,f26.c1,f27.c1,f27.c2,f28.c2,f32.c1,f33.c0,f34.c0,f6.c0,f6.c2,f8.c0
class class-classroom-01 year=2 letter=А room=classroom-01 capacity=16 pupils=f0.c1,f12.c0,f12.c1,f12.c2,f13.c1,f13.c2,f14.c2,f20.c0,f23.c1,f26.c0,f27.c0,f29.c0,f31.c0,f4.c0,f7.c0,f9.c0
class class-classroom-02 year=3 letter=А room=classroom-02 capacity=16 pupils=f10.c0,f14.c0,f16.c0,f17.c1,f2.c0,f21.c0,f22.c1,f23.c0,f25.c0,f28.c0,f3.c0,f3.c1,f32.c0,f32.c2,f5.c0,f6.c1