299 lines
10 KiB
C#
299 lines
10 KiB
C#
namespace HSchool.People;
|
|
|
|
internal static class FamilyFactory
|
|
{
|
|
public static (Family Family, List<Person> Members) Create(
|
|
DefCatalog catalog,
|
|
NameSetDef names,
|
|
int schoolSeed,
|
|
FamilyPlan plan,
|
|
DateTime yearStart,
|
|
DateTime asOf)
|
|
{
|
|
var rng = new Random(Seed.Mix(schoolSeed, plan.FamilyIndex, Seed.AppearanceSalt));
|
|
var familyId = $"f{plan.FamilyIndex}";
|
|
var surname = names.Surnames[rng.Next(names.Surnames.Count)];
|
|
|
|
var childDrafts = new ChildDraft[plan.Seats.Count];
|
|
var usedGiven = new HashSet<string>(StringComparer.Ordinal);
|
|
for (var i = 0; i < plan.Seats.Count; i++)
|
|
{
|
|
var seat = plan.Seats[i];
|
|
var female = rng.Next(2) == 0;
|
|
var given = PickGiven(female ? names.FemaleGiven : names.MaleGiven, rng, usedGiven);
|
|
var (first, last) = SchoolYears.BirthWindow(yearStart, seat.Year);
|
|
childDrafts[i] = new ChildDraft(seat, female, given, SchoolYears.RandomInRange(rng, first, last));
|
|
}
|
|
|
|
var fatherGiven = PickGiven(names.MaleGiven, rng);
|
|
var motherGiven = PickGiven(names.FemaleGiven, rng);
|
|
var fatherPatronymicSource = PickGiven(names.MaleGiven, rng);
|
|
var motherPatronymicSource = PickGiven(names.MaleGiven, rng);
|
|
|
|
DateTime motherBirth;
|
|
DateTime fatherBirth;
|
|
if (childDrafts.Length == 0)
|
|
{
|
|
motherBirth = asOf.AddYears(-(28 + rng.Next(28))).AddDays(-rng.Next(365));
|
|
fatherBirth = motherBirth.AddDays(rng.Next(-5 * 365, (5 * 365) + 1));
|
|
}
|
|
else
|
|
{
|
|
var oldestBirth = childDrafts[0].Birth;
|
|
foreach (var child in childDrafts)
|
|
{
|
|
if (child.Birth < oldestBirth)
|
|
{
|
|
oldestBirth = child.Birth;
|
|
}
|
|
}
|
|
|
|
var motherYearsOlder = 22 + rng.Next(17);
|
|
motherBirth = oldestBirth.AddYears(-motherYearsOlder).AddDays(-rng.Next(30));
|
|
fatherBirth = motherBirth.AddDays(rng.Next(-5 * 365, (5 * 365) + 1));
|
|
}
|
|
|
|
var members = new List<Person>();
|
|
var fatherId = $"{familyId}.p0";
|
|
var motherId = $"{familyId}.p1";
|
|
members.Add(
|
|
RollAdult(
|
|
catalog,
|
|
names,
|
|
rng,
|
|
fatherId,
|
|
familyId,
|
|
female: false,
|
|
fatherBirth,
|
|
asOf,
|
|
surname,
|
|
fatherGiven,
|
|
NameGrammar.Patronymic(fatherPatronymicSource.Form, female: false, names.PatronymicRule),
|
|
isParent: childDrafts.Length > 0));
|
|
members.Add(
|
|
RollAdult(
|
|
catalog,
|
|
names,
|
|
rng,
|
|
motherId,
|
|
familyId,
|
|
female: true,
|
|
motherBirth,
|
|
asOf,
|
|
surname,
|
|
motherGiven,
|
|
NameGrammar.Patronymic(motherPatronymicSource.Form, female: true, names.PatronymicRule),
|
|
isParent: childDrafts.Length > 0));
|
|
|
|
var childIds = new List<string>(childDrafts.Length);
|
|
for (var i = 0; i < childDrafts.Length; i++)
|
|
{
|
|
var draft = childDrafts[i];
|
|
var id = $"{familyId}.c{i}";
|
|
childIds.Add(id);
|
|
members.Add(
|
|
RollChild(
|
|
catalog,
|
|
names,
|
|
rng,
|
|
id,
|
|
familyId,
|
|
draft,
|
|
asOf,
|
|
surname,
|
|
fatherGiven.Form));
|
|
}
|
|
|
|
var family = new Family(familyId, [fatherId, motherId], childIds);
|
|
return (family, members);
|
|
}
|
|
|
|
public static (Family Family, List<Person> Members) CreateStaffOnly(
|
|
DefCatalog catalog,
|
|
NameSetDef names,
|
|
int schoolSeed,
|
|
int familyIndex,
|
|
DateTime asOf)
|
|
{
|
|
var empty = new FamilyPlan(familyIndex, []);
|
|
return Create(catalog, names, schoolSeed, empty, SchoolYears.StartOn(asOf), asOf);
|
|
}
|
|
|
|
private static Person RollAdult(
|
|
DefCatalog catalog,
|
|
NameSetDef names,
|
|
Random rng,
|
|
string id,
|
|
string familyId,
|
|
bool female,
|
|
DateTime birth,
|
|
DateTime asOf,
|
|
SurnameEntry surname,
|
|
GivenNameEntry given,
|
|
string patronymic,
|
|
bool isParent)
|
|
{
|
|
var age = SchoolYears.AgeYears(birth, asOf);
|
|
var roles = isParent ? new[] { PersonRoles.Parent } : new[] { PersonRoles.Staff };
|
|
return FinishPerson(
|
|
catalog,
|
|
names,
|
|
rng,
|
|
id,
|
|
familyId,
|
|
female,
|
|
birth,
|
|
asOf,
|
|
age,
|
|
surname,
|
|
given,
|
|
patronymic,
|
|
roles,
|
|
isStudent: false,
|
|
isParent,
|
|
classId: null);
|
|
}
|
|
|
|
private static Person RollChild(
|
|
DefCatalog catalog,
|
|
NameSetDef names,
|
|
Random rng,
|
|
string id,
|
|
string familyId,
|
|
ChildDraft draft,
|
|
DateTime asOf,
|
|
SurnameEntry surname,
|
|
string fatherGiven)
|
|
{
|
|
var age = SchoolYears.AgeYears(draft.Birth, asOf);
|
|
var patronymic = NameGrammar.Patronymic(fatherGiven, draft.Female, names.PatronymicRule);
|
|
return FinishPerson(
|
|
catalog,
|
|
names,
|
|
rng,
|
|
id,
|
|
familyId,
|
|
draft.Female,
|
|
draft.Birth,
|
|
asOf,
|
|
age,
|
|
surname,
|
|
draft.Given,
|
|
patronymic,
|
|
[PersonRoles.Student],
|
|
isStudent: true,
|
|
isParent: false,
|
|
draft.Seat.ClassId);
|
|
}
|
|
|
|
private static Person FinishPerson(
|
|
DefCatalog catalog,
|
|
NameSetDef names,
|
|
Random rng,
|
|
string id,
|
|
string familyId,
|
|
bool female,
|
|
DateTime birth,
|
|
DateTime asOf,
|
|
int age,
|
|
SurnameEntry surname,
|
|
GivenNameEntry given,
|
|
string patronymic,
|
|
IReadOnlyList<string> roles,
|
|
bool isStudent,
|
|
bool isParent,
|
|
string? classId)
|
|
{
|
|
var (numbers, choices) = PersonSampler.Body(catalog, rng, female, age);
|
|
var traits = PersonSampler.Traits(catalog, rng, roles, age);
|
|
var skills = PersonSampler.Skills(catalog, rng, age, choices, traits);
|
|
var needs = PersonSampler.Needs(catalog);
|
|
var name = new PersonName(
|
|
given.Form,
|
|
female ? surname.Female : surname.Male,
|
|
patronymic,
|
|
GivenTable(given, names.DefaultGivenDeclension),
|
|
SurnameTable(surname, female, names.DefaultSurnameDeclension),
|
|
PatronymicTable(patronymic, female));
|
|
|
|
return new Person
|
|
{
|
|
Id = id,
|
|
FamilyId = familyId,
|
|
Female = female,
|
|
BirthDate = DateTime.SpecifyKind(birth, DateTimeKind.Utc),
|
|
Name = name,
|
|
IsStudent = isStudent,
|
|
IsStaff = false,
|
|
IsParent = isParent,
|
|
ClassId = classId,
|
|
Numbers = numbers,
|
|
Choices = choices,
|
|
Skills = skills,
|
|
Traits = traits,
|
|
Needs = needs,
|
|
};
|
|
}
|
|
|
|
private static GivenNameEntry PickGiven(
|
|
IReadOnlyList<GivenNameEntry> pool,
|
|
Random rng,
|
|
HashSet<string>? used = null)
|
|
{
|
|
if (pool.Count == 0)
|
|
{
|
|
throw new InvalidOperationException("Name set has no given names for this sex.");
|
|
}
|
|
|
|
for (var attempt = 0; attempt < 16; attempt++)
|
|
{
|
|
var pick = pool[rng.Next(pool.Count)];
|
|
if (used is null || used.Add(pick.Form) || used.Count >= pool.Count)
|
|
{
|
|
return pick;
|
|
}
|
|
}
|
|
|
|
return pool[rng.Next(pool.Count)];
|
|
}
|
|
|
|
private static CaseTable GivenTable(GivenNameEntry entry, string defaultModel) =>
|
|
new()
|
|
{
|
|
Nom = NameGrammar.InflectGiven(entry, GrammaticalCase.Nominative, defaultModel),
|
|
Gen = NameGrammar.InflectGiven(entry, GrammaticalCase.Genitive, defaultModel),
|
|
Dat = NameGrammar.InflectGiven(entry, GrammaticalCase.Dative, defaultModel),
|
|
Acc = NameGrammar.InflectGiven(entry, GrammaticalCase.Accusative, defaultModel),
|
|
Ins = NameGrammar.InflectGiven(entry, GrammaticalCase.Instrumental, defaultModel),
|
|
Pre = NameGrammar.InflectGiven(entry, GrammaticalCase.Prepositional, defaultModel),
|
|
};
|
|
|
|
private static CaseTable SurnameTable(SurnameEntry entry, bool female, string defaultModel) =>
|
|
new()
|
|
{
|
|
Nom = NameGrammar.InflectSurname(entry, female, GrammaticalCase.Nominative, defaultModel),
|
|
Gen = NameGrammar.InflectSurname(entry, female, GrammaticalCase.Genitive, defaultModel),
|
|
Dat = NameGrammar.InflectSurname(entry, female, GrammaticalCase.Dative, defaultModel),
|
|
Acc = NameGrammar.InflectSurname(entry, female, GrammaticalCase.Accusative, defaultModel),
|
|
Ins = NameGrammar.InflectSurname(entry, female, GrammaticalCase.Instrumental, defaultModel),
|
|
Pre = NameGrammar.InflectSurname(entry, female, GrammaticalCase.Prepositional, defaultModel),
|
|
};
|
|
|
|
private static CaseTable PatronymicTable(string nominative, bool female) =>
|
|
new()
|
|
{
|
|
Nom = nominative,
|
|
Gen = NameGrammar.InflectPatronymic(nominative, female, GrammaticalCase.Genitive),
|
|
Dat = NameGrammar.InflectPatronymic(nominative, female, GrammaticalCase.Dative),
|
|
Acc = NameGrammar.InflectPatronymic(nominative, female, GrammaticalCase.Accusative),
|
|
Ins = NameGrammar.InflectPatronymic(nominative, female, GrammaticalCase.Instrumental),
|
|
Pre = NameGrammar.InflectPatronymic(nominative, female, GrammaticalCase.Prepositional),
|
|
};
|
|
|
|
private readonly record struct ChildDraft(
|
|
PupilSeat Seat,
|
|
bool Female,
|
|
GivenNameEntry Given,
|
|
DateTime Birth);
|
|
}
|