namespace HSchool.People; /// /// Per-family streams derived from the school seed. Family N never consumes family N-1's rolls, /// so appending a thirteenth family leaves the first twelve unchanged. /// public static class Seed { public const int ChildCountSalt = 1; public const int AppearanceSalt = 2; public const int IntakeSalt = 3; public const int SeatShuffleSalt = 4; public const int HouseholdSalt = 5; public const int ApplicantSalt = 6; public const int CommuteSalt = 7; public const int SkillGrantSalt = 8; public const int NativeLanguageSalt = 9; public const int ClimatePresetSalt = 10; public const int ApparelSalt = 11; public const int WhisperSalt = 12; /// A stream that belongs to the school rather than to one family. public static int ForSchool(int schoolSeed, int salt) => Mix(schoolSeed, familyIndex: -1, salt); public static int Mix(int schoolSeed, int familyIndex, int salt) { var z = Mix64((uint)schoolSeed); z = Mix64(z ^ (uint)(familyIndex + 1)); z = Mix64(z ^ (uint)(salt + 1)); return (int)z; } /// A stream that belongs to one person on one calendar day — commute slack, not looks. public static int Mix(int schoolSeed, string personId, int dayNumber, int salt) { ArgumentNullException.ThrowIfNull(personId); var z = Mix64((uint)schoolSeed); z = Mix64(z ^ Stable(personId)); z = Mix64(z ^ (uint)(dayNumber + 1)); z = Mix64(z ^ (uint)(salt + 1)); return (int)z; } private static uint Stable(string value) { ulong z = 0; foreach (var character in value) { z = Mix64(z ^ character); } return (uint)z; } private static ulong Mix64(ulong z) { z += 0x9E3779B97F4A7C15UL; z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9UL; z = (z ^ (z >> 27)) * 0x94D049BB133111EBUL; return z ^ (z >> 31); } }