67 lines
2.2 KiB
C#
67 lines
2.2 KiB
C#
namespace HSchool.People;
|
||
|
||
internal readonly record struct FamilyPlan(int FamilyIndex, IReadOnlyList<PupilSeat> Seats);
|
||
|
||
/// <summary>
|
||
/// Walks seats in order and groups them into families of 1–3, taking a run of consecutive seats
|
||
/// per family. Callers hand in a shuffled seat list — a run of *adjacent* seats is one classroom,
|
||
/// which would put every sibling in the same class.
|
||
///
|
||
/// A family whose intended size does not fit the remainder takes one seat instead of shrinking,
|
||
/// so growing the seat list only disturbs the tail.
|
||
/// </summary>
|
||
internal static class FamilyPlanner
|
||
{
|
||
/// <summary>
|
||
/// One child per family. Used by the yearly intake: every seat there is a first-year seat, so
|
||
/// grouping would hand a family two or three same-age children at once. An arriving pupil who
|
||
/// does have an older sibling gets attached to that family instead.
|
||
/// </summary>
|
||
public static IReadOnlyList<FamilyPlan> Singletons(IReadOnlyList<PupilSeat> seats, int startIndex)
|
||
{
|
||
var plans = new FamilyPlan[seats.Count];
|
||
for (var i = 0; i < seats.Count; i++)
|
||
{
|
||
plans[i] = new FamilyPlan(startIndex + i, [seats[i]]);
|
||
}
|
||
|
||
return plans;
|
||
}
|
||
|
||
public static IReadOnlyList<FamilyPlan> Plan(int schoolSeed, IReadOnlyList<PupilSeat> seats, int startIndex = 0)
|
||
{
|
||
var plans = new List<FamilyPlan>();
|
||
var offset = 0;
|
||
var index = startIndex;
|
||
while (offset < seats.Count)
|
||
{
|
||
var remaining = seats.Count - offset;
|
||
var want = ChildCount(schoolSeed, index);
|
||
var take = want <= remaining ? want : 1;
|
||
var slice = new PupilSeat[take];
|
||
for (var i = 0; i < take; i++)
|
||
{
|
||
slice[i] = seats[offset + i];
|
||
}
|
||
|
||
plans.Add(new FamilyPlan(index, slice));
|
||
offset += take;
|
||
index++;
|
||
}
|
||
|
||
return plans;
|
||
}
|
||
|
||
public static int ChildCount(int schoolSeed, int familyIndex)
|
||
{
|
||
var rng = new Random(Seed.Mix(schoolSeed, familyIndex, Seed.ChildCountSalt));
|
||
var roll = rng.Next(100);
|
||
if (roll < 50)
|
||
{
|
||
return 1;
|
||
}
|
||
|
||
return roll < 85 ? 2 : 3;
|
||
}
|
||
}
|