PlotDirector/PlotLine/Services/CharacterSexHintService.cs

120 lines
4.8 KiB
C#

namespace PlotLine.Services;
public static class CharacterSexHintService
{
public const string Unknown = "Unknown";
public const string Female = "Female";
public const string Male = "Male";
public const string NonBinaryOrOther = "NonBinaryOrOther";
private static readonly HashSet<string> FemaleNames = new(StringComparer.OrdinalIgnoreCase)
{
"abby", "abigail", "ada", "adele", "alice", "alison", "allison", "amanda", "amelia", "amy",
"angela", "anna", "anne", "annie", "barbara", "becky", "belinda", "beth", "bethany", "brenda",
"carol", "caroline", "catherine", "charlotte", "chloe", "chrissy", "christine", "cindy", "claire", "clara",
"daisy", "debbie", "debby", "deborah", "diana", "diane", "donna", "dorothy", "elaine", "elizabeth",
"ella", "ellie", "emily", "emma", "eva", "eve", "fiona", "gemma", "georgina", "grace", "hannah",
"harriet", "heather", "helen", "isabel", "isabella", "jacqueline", "jane", "janet", "janice",
"jean", "jennifer", "jenny", "jessica", "joanne", "judy", "julia", "julie", "karen", "kate", "katie",
"katherine", "kathryn", "kelly", "laura", "lesley", "linda", "lisa", "lily", "louise", "lucy",
"maggie", "margaret", "maria", "marie", "marion", "mary", "michelle", "molly", "nicola", "olivia",
"pamela", "patricia", "paula", "rachel", "rebecca", "rose", "sally", "samantha", "sandra", "sarah",
"shirley", "sophie", "stephanie", "susan", "suzanne", "tracey", "tracy", "valerie", "victoria",
"wendy", "zoe"
};
private static readonly HashSet<string> MaleNames = new(StringComparer.OrdinalIgnoreCase)
{
"adam", "alan", "albert", "alexander", "andrew", "anthony", "arthur", "barry", "ben", "benjamin",
"bernard", "brian", "bruce", "charles", "chris", "christopher", "colin", "craig", "daniel", "darren",
"david", "dennis", "douglas", "edward", "eric", "frank", "gary", "geoff", "geoffrey", "george",
"graham", "grant", "greg", "harry", "henry", "howard", "ian", "jack", "james", "john", "jonathan", "joseph",
"keith", "kenneth", "kevin", "liam", "martin", "mark", "matthew", "michael", "neil", "nick", "nicolas",
"nicholas", "nigel", "oliver", "paul", "peter", "phil", "philip", "raymond", "richard", "robert", "roger",
"ronald", "samuel", "simon", "stephen", "steven", "stuart", "terence", "thomas", "tim", "timothy",
"tom", "tony", "william"
};
private static readonly HashSet<string> AmbiguousNames = new(StringComparer.OrdinalIgnoreCase)
{
"alex", "andy", "ashley", "billie", "blair", "bobbie", "casey", "charlie", "drew", "frankie",
"jamie", "jessie", "jo", "jordan", "lee", "leslie", "morgan", "pat", "riley", "robyn", "robin",
"sam", "sandy", "shannon", "taylor", "terry"
};
private static readonly Dictionary<string, string> TitleHints = new(StringComparer.OrdinalIgnoreCase)
{
["mr"] = Male,
["mister"] = Male,
["sir"] = Male,
["mrs"] = Female,
["miss"] = Female,
["ms"] = Female,
["madam"] = Female,
["lady"] = Female
};
public static string Suggest(string? name)
{
var token = FirstNameToken(name, out var titleHint);
if (!string.IsNullOrWhiteSpace(titleHint))
{
return titleHint;
}
if (string.IsNullOrWhiteSpace(token) || AmbiguousNames.Contains(token))
{
return Unknown;
}
if (FemaleNames.Contains(token))
{
return Female;
}
return MaleNames.Contains(token) ? Male : Unknown;
}
public static string Normalise(string? value, string? fallbackName = null)
=> value?.Trim() switch
{
Female => Female,
Male => Male,
NonBinaryOrOther => NonBinaryOrOther,
Unknown => Unknown,
_ => Suggest(fallbackName)
};
private static string FirstNameToken(string? name, out string? titleHint)
{
titleHint = null;
var clean = name?.Trim();
if (string.IsNullOrWhiteSpace(clean))
{
return string.Empty;
}
var tokens = clean
.Split([' ', '-', '_'], StringSplitOptions.RemoveEmptyEntries)
.Select(CleanToken)
.Where(token => !string.IsNullOrWhiteSpace(token))
.ToList();
if (tokens.Count == 0)
{
return string.Empty;
}
if (TitleHints.TryGetValue(tokens[0], out titleHint))
{
return tokens.Skip(1).FirstOrDefault() ?? string.Empty;
}
return tokens[0];
}
private static string CleanToken(string token)
{
return token.Trim('.', ',', ';', ':', '\'', '"', '(', ')', '[', ']').ToLowerInvariant();
}
}