Implement applicant pool functionality in school simulation, allowing for the management of job seekers who are not yet part of the school roster. Update the catalog to include staffing definitions and enhance the API to support applicant data retrieval. Revise the school architecture to handle applicant refresh logic and ensure proper integration with existing roster management. Update tests to validate the new applicant functionalities and ensure robustness in handling staffing scenarios.

This commit is contained in:
Leonid Pershin
2026-08-19 00:01:56 +03:00
parent 65feda3756
commit c189578680
31 changed files with 541 additions and 120 deletions
+25 -3
View File
@@ -68,6 +68,9 @@ public sealed class School : IDisposable
/// <summary>Composition snapshot. Null in clock-only tests or before <see cref="InstallPeople"/>.</summary>
public Roster? Roster { get; private set; }
/// <summary>People looking for work. Not in the roster and not in the World until hired.</summary>
public ApplicantPool? Applicants { get; private set; }
public int PeopleSeed { get; private set; }
/// <summary>Name pack used to generate this school's people. Needed again on 1 September.</summary>
@@ -76,7 +79,7 @@ public sealed class School : IDisposable
/// <summary>
/// Installs a roster that already matches the map. Spawns entities; does not write to disk.
/// </summary>
public void InstallPeople(Roster roster, int seed, string? nameSetId = null)
public void InstallPeople(Roster roster, int seed, string? nameSetId = null, ApplicantPool? applicants = null)
{
ObjectDisposedException.ThrowIf(_disposed, this);
ArgumentNullException.ThrowIfNull(roster);
@@ -84,11 +87,12 @@ public sealed class School : IDisposable
Roster = roster;
PeopleSeed = seed;
NameSetId = nameSetId;
Applicants = applicants;
RosterSpawner.Spawn(World, roster);
}
/// <summary>Runs one fixed step of the school: calendar, yearly intake if 1 September passed, then need decay.</summary>
/// <returns><see langword="true"/> when the roster changed this step.</returns>
/// <summary>Runs one fixed step of the school: calendar, yearly intake, applicant refresh, then need decay.</summary>
/// <returns><see langword="true"/> when the roster or the applicant pool changed this step.</returns>
public bool Tick(double deltaTime, double gameMinutesPerRealSecond)
{
ObjectDisposedException.ThrowIf(_disposed, this);
@@ -99,6 +103,7 @@ public sealed class School : IDisposable
if (gameMinutes > 0)
{
peopleChanged = TryYearlyIntake(before, Clock.Time);
peopleChanged |= TryApplicantRefresh();
if (Catalog is not null)
{
NeedDecay.Apply(World, Catalog, gameMinutes);
@@ -130,6 +135,23 @@ public sealed class School : IDisposable
return changed;
}
private bool TryApplicantRefresh()
{
if (Applicants is null || Roster is null || Catalog is null || NameSetId is null || Catalog.StaffingRules is null)
{
return false;
}
var next = Applicants.Advance(Catalog, Roster, PeopleSeed, NameSetId, Clock.Time);
if (next.Week == Applicants.Week)
{
return false;
}
Applicants = next;
return true;
}
public void Dispose()
{
if (_disposed)