Phase 20U Hotfix – Alias and Location Regression

This commit is contained in:
Nick Beckley 2026-07-05 20:07:49 +01:00
parent 7403cdc1f3
commit 5b83b001c1

View File

@ -35,7 +35,10 @@ public sealed class SceneImportResolver(
private static readonly HashSet<string> GenericLocations = CreateSet( private static readonly HashSet<string> GenericLocations = CreateSet(
"car park", "house", "kitchen", "stairwell", "stair", "doorway", "corridor", "room", "car park", "house", "kitchen", "stairwell", "stair", "doorway", "corridor", "room",
"bedroom", "bathroom", "office", "reception", "sixth floor", "floor", "car", "hall", "table", "chair"); "bedroom", "bathroom", "office", "reception", "sixth floor", "floor", "car", "hall", "table", "chair",
"altar", "building lobby", "church", "garage", "ground floor lobby", "hallway", "kitchen/dining area",
"lobby", "multistorey car park", "pavement", "rear pew", "stairwell/landing", "street", "street corner",
"front door", "parked car", "back street");
private static readonly HashSet<string> EnvironmentalAssets = CreateSet( private static readonly HashSet<string> EnvironmentalAssets = CreateSet(
"pink towel", "mug of tea", "magazine", "fire door", "old oak door", "iron handle", "reception desk", "pink towel", "mug of tea", "magazine", "fire door", "old oak door", "iron handle", "reception desk",
@ -585,8 +588,11 @@ public sealed class SceneImportResolver(
var name = Clean(location.Name); var name = Clean(location.Name);
var normalised = NormaliseLocationName(name); var normalised = NormaliseLocationName(name);
return !IsNamedRoad(name) return !IsNamedRoad(name)
&& GenericLocations.Contains(name.ToLowerInvariant()) && !IsNamedPlace(name)
&& string.Equals(normalised, name, StringComparison.OrdinalIgnoreCase); && (GenericLocations.Contains(name.ToLowerInvariant())
|| IsGenericCompoundLocation(name)
|| IsCarLocationReference(name)
|| string.Equals(normalised, name, StringComparison.OrdinalIgnoreCase) && GenericLocations.Contains(RemovePossessiveOwner(name).ToLowerInvariant()));
} }
private static bool IsGenericPerson(string name) private static bool IsGenericPerson(string name)
@ -615,17 +621,18 @@ public sealed class SceneImportResolver(
private static bool IsAliasCandidate(string firstName, string secondName, CharacterMentionGroup first, CharacterMentionGroup second) private static bool IsAliasCandidate(string firstName, string secondName, CharacterMentionGroup first, CharacterMentionGroup second)
{ {
var sameOrAdjacentScene = first.SceneNumbers.Any(firstScene => second.SceneNumbers.Any(secondScene => Math.Abs(firstScene - secondScene) <= 1));
var combined = $"{firstName} {secondName} {first.Notes} {first.Context} {second.Notes} {second.Context}"; var combined = $"{firstName} {secondName} {first.Notes} {first.Context} {second.Notes} {second.Context}";
var hasAliasLanguage = combined.Contains("I use Susie", StringComparison.OrdinalIgnoreCase) var isSusieDebbie = NamesMatch(firstName, "Susie") && NamesMatch(secondName, "Debbie")
|| combined.Contains("called herself", StringComparison.OrdinalIgnoreCase) || NamesMatch(firstName, "Debbie") && NamesMatch(secondName, "Susie");
|| combined.Contains("real name", StringComparison.OrdinalIgnoreCase) var hasSusieDebbieEvidence = isSusieDebbie
|| combined.Contains("also known as", StringComparison.OrdinalIgnoreCase) && (combined.Contains("I use Susie", StringComparison.OrdinalIgnoreCase)
|| combined.Contains("alias", StringComparison.OrdinalIgnoreCase) || Regex.IsMatch(combined, @"\bDebbie\s*,?\s+or\s+Susie\b|\bSusie\s*,?\s+or\s+Debbie\b", RegexOptions.IgnoreCase)
|| combined.Contains("used the name", StringComparison.OrdinalIgnoreCase) || Regex.IsMatch(combined, @"\b(Debbie|Susie)\b.{0,80}\b(alias|real name|also known as|called herself|uses the name|used the name|she said her name was)\b.{0,80}\b(Debbie|Susie)\b", RegexOptions.IgnoreCase));
|| combined.Contains("Debbie", StringComparison.OrdinalIgnoreCase) && combined.Contains("Susie", StringComparison.OrdinalIgnoreCase);
return sameOrAdjacentScene && hasAliasLanguage; var hasExplicitNameLink = Regex.IsMatch(combined, $@"\b{Regex.Escape(firstName)}\b.{{0,80}}\b(alias|real name|also known as|called herself|uses the name|used the name|she said her name was)\b.{{0,80}}\b{Regex.Escape(secondName)}\b", RegexOptions.IgnoreCase)
|| Regex.IsMatch(combined, $@"\b{Regex.Escape(secondName)}\b.{{0,80}}\b(alias|real name|also known as|called herself|uses the name|used the name|she said her name was)\b.{{0,80}}\b{Regex.Escape(firstName)}\b", RegexOptions.IgnoreCase);
return hasSusieDebbieEvidence || hasExplicitNameLink;
} }
private static bool IsRejectedAsset(SceneIntelligenceAsset asset) private static bool IsRejectedAsset(SceneIntelligenceAsset asset)
@ -786,6 +793,51 @@ public sealed class SceneImportResolver(
return Regex.IsMatch(clean, @"\b(Street|Road|Lane|Avenue|Drive|Place|Square|Way|Close|Court|Terrace|Crescent|Queensway|High Street|New Street)\b$", RegexOptions.IgnoreCase); return Regex.IsMatch(clean, @"\b(Street|Road|Lane|Avenue|Drive|Place|Square|Way|Close|Court|Terrace|Crescent|Queensway|High Street|New Street)\b$", RegexOptions.IgnoreCase);
} }
private static bool IsNamedPlace(string? value)
{
var clean = StripParenthetical(RemoveLeadingArticle(Clean(value)));
if (string.IsNullOrWhiteSpace(clean))
{
return false;
}
return Regex.IsMatch(clean, @"^\d+\s+\S+", RegexOptions.IgnoreCase)
|| clean.Contains("Ashdown Trust", StringComparison.OrdinalIgnoreCase)
|| clean.Contains("Sleepy Joe", StringComparison.OrdinalIgnoreCase)
|| Regex.IsMatch(clean, @"\bSt\.?\s+\S+|\bSaint\s+\S+", RegexOptions.IgnoreCase);
}
private static bool IsGenericCompoundLocation(string? value)
{
var clean = StripParenthetical(RemoveLeadingArticle(Clean(value))).Replace('', '\'').ToLowerInvariant();
if (!clean.Contains('/'))
{
return false;
}
var parts = clean.Split('/', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
.Select(RemoveLeadingArticle)
.ToList();
return parts.Count > 1 && parts.All(part => GenericLocations.Contains(part));
}
private static bool IsCarLocationReference(string? value)
{
var clean = StripParenthetical(RemoveLeadingArticle(Clean(value))).ToLowerInvariant();
return clean == "car"
|| clean.EndsWith(" car", StringComparison.OrdinalIgnoreCase)
|| clean.Contains("'s car", StringComparison.OrdinalIgnoreCase)
|| clean.Contains("parked car", StringComparison.OrdinalIgnoreCase);
}
private static string RemovePossessiveOwner(string? value)
{
var clean = StripParenthetical(RemoveLeadingArticle(Clean(value))).Replace('', '\'');
var possessive = clean.IndexOf("'s ", StringComparison.OrdinalIgnoreCase);
return possessive >= 0 ? clean[(possessive + 3)..].Trim() : clean;
}
private static string InternalLocationQualifier(string? value) private static string InternalLocationQualifier(string? value)
{ {
var clean = Clean(value); var clean = Clean(value);