Files
h-school/src/HSchool.Simulation/SchoolNameGenerator.cs
T

99 lines
3.2 KiB
C#

namespace HSchool.Simulation;
/// <summary>Which word list the random-name button draws from.</summary>
public enum SchoolNameLanguage
{
Russian = 0,
English = 1,
}
/// <summary>
/// Suggestions for the "random name" button. Lives here rather than in the client because the
/// server is the one that knows which names are already taken.
/// </summary>
public sealed class SchoolNameGenerator(Random? random = null)
{
private const int AttemptsBeforeNumbering = 24;
private readonly record struct Catalog(
string[] Kinds,
string[] Epithets,
string NumberSign,
bool NumberSpace,
bool EpithetFirst,
string FallbackKind);
private static readonly Catalog Russian = new(
["Школа", "Гимназия", "Лицей", "Школа-интернат"],
[
"Северная", "Приморская", "Заречная", "Нагорная", "Слободская", "Озёрная",
"Кленовая", "Рябиновая", "Солнечная", "Луговая", "Тихая", "Ясная",
],
"№",
false,
false,
"Школа");
private static readonly Catalog English = new(
["School", "Academy", "Grammar School", "High School"],
[
"Northern", "Seaside", "Riverside", "Hillside", "Maple", "Rowan",
"Sunny", "Meadow", "Quiet", "Clear", "Oak", "Pine",
],
"No.",
true,
true,
"School");
private readonly Random _random = random ?? Random.Shared;
/// <summary>
/// A name that is not in <paramref name="taken"/>. Falls back to a numbered name so the
/// button always produces something, even when the pool is exhausted.
/// </summary>
public string Next(IEnumerable<string> taken, SchoolNameLanguage language = SchoolNameLanguage.Russian)
{
var catalog = language == SchoolNameLanguage.English ? English : Russian;
var used = new HashSet<string>(taken, StringComparer.OrdinalIgnoreCase);
for (var attempt = 0; attempt < AttemptsBeforeNumbering; attempt++)
{
var candidate = Compose(catalog);
if (used.Add(candidate))
{
return candidate;
}
}
for (var number = 1; ; number++)
{
var candidate = Numbered(catalog, catalog.FallbackKind, number);
if (!used.Contains(candidate))
{
return candidate;
}
}
}
private string Compose(Catalog catalog)
{
var kind = catalog.Kinds[_random.Next(catalog.Kinds.Length)];
// Half the names are numbered, half are named — both read like a real school.
if (_random.Next(2) == 0)
{
return Numbered(catalog, kind, _random.Next(1, 100));
}
var epithet = catalog.Epithets[_random.Next(catalog.Epithets.Length)];
return catalog.EpithetFirst
? $"{epithet} {kind}"
: $"{kind} «{epithet}»";
}
private static string Numbered(Catalog catalog, string kind, int number) =>
catalog.NumberSpace
? $"{kind} {catalog.NumberSign} {number}"
: $"{kind} {catalog.NumberSign}{number}";
}