From 839241ea6212e816b7a6b176233d506ad4432e7c Mon Sep 17 00:00:00 2001 From: Frank Tovar Date: Sat, 14 Mar 2026 13:11:27 +0100 Subject: [PATCH] Fix puncture prose boundary splitting --- src/RolemasterDb.App/rolemaster.db | Bin 3149824 -> 3170304 bytes ...dardCriticalTableParserIntegrationTests.cs | 43 +++++++++++++++ .../Parsing/CriticalTableParserSupport.cs | 49 ++++++++++++++---- 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/src/RolemasterDb.App/rolemaster.db b/src/RolemasterDb.App/rolemaster.db index 8934083500e72bc3a79701794e70339fecd27040..598b2edbd19c8c7a0e34cc9fe755c735ec28fc16 100644 GIT binary patch delta 39895 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 diff --git a/src/RolemasterDb.ImportTool.Tests/StandardCriticalTableParserIntegrationTests.cs b/src/RolemasterDb.ImportTool.Tests/StandardCriticalTableParserIntegrationTests.cs index 71cd0f2..3c1b018 100644 --- a/src/RolemasterDb.ImportTool.Tests/StandardCriticalTableParserIntegrationTests.cs +++ b/src/RolemasterDb.ImportTool.Tests/StandardCriticalTableParserIntegrationTests.cs @@ -136,6 +136,25 @@ public sealed class StandardCriticalTableParserIntegrationTests Assert.Equal(1, bleedEffects.Single().PerRound); } + [Fact] + public async Task Puncture_91_95_keeps_a_and_b_prose_in_separate_columns() + { + var entry = LoadManifest().Tables.Single(item => string.Equals(item.Slug, "puncture", StringComparison.Ordinal)); + var parseResult = await LoadParseResultAsync(entry); + var row91A = parseResult.Table.Results.Single(item => + item.GroupKey is null && + string.Equals(item.RollBandLabel, "91-95", StringComparison.Ordinal) && + string.Equals(item.ColumnKey, "A", StringComparison.Ordinal)); + var row91B = parseResult.Table.Results.Single(item => + item.GroupKey is null && + string.Equals(item.RollBandLabel, "91-95", StringComparison.Ordinal) && + string.Equals(item.ColumnKey, "B", StringComparison.Ordinal)); + + Assert.Equal("Strike to foe's ear. Foe hears at -50.", row91A.DescriptionText); + Assert.DoesNotContain("Strike to foe's hip.", row91A.DescriptionText, StringComparison.Ordinal); + Assert.Contains("Strike to foe's hip.", row91B.DescriptionText, StringComparison.Ordinal); + } + [Fact] public async Task Slash_boundary_repair_keeps_56_60_a_prose_first() { @@ -460,6 +479,30 @@ public sealed class StandardCriticalTableParserIntegrationTests Assert.Contains(result.Branches.SelectMany(item => item.Effects), item => item.EffectCode == CriticalEffectCodes.BleedPerRound); } + [Fact] + public async Task Loader_persists_puncture_b_91_95_description_text() + { + var entry = LoadManifest().Tables.Single(item => string.Equals(item.Slug, "puncture", StringComparison.Ordinal)); + var parseResult = await LoadParseResultAsync(entry); + var databasePath = CreateTemporaryDatabaseCopy(); + var loader = new CriticalImportLoader(databasePath); + + await loader.LoadAsync(parseResult.Table); + + await using var dbContext = CreateDbContext(databasePath); + var result = await dbContext.CriticalResults + .Include(item => item.CriticalTable) + .Include(item => item.CriticalColumn) + .Include(item => item.CriticalRollBand) + .SingleAsync(item => + item.CriticalTable.Slug == "puncture" && + item.CriticalColumn.ColumnKey == "B" && + item.CriticalRollBand.Label == "91-95"); + + Assert.Contains("Strike to foe's hip.", result.DescriptionText, StringComparison.Ordinal); + Assert.StartsWith("Strike to foe's hip.", result.RawCellText, StringComparison.Ordinal); + } + [Fact] public async Task Lookup_service_returns_effects_for_results_and_branches() { diff --git a/src/RolemasterDb.ImportTool/Parsing/CriticalTableParserSupport.cs b/src/RolemasterDb.ImportTool/Parsing/CriticalTableParserSupport.cs index a6666ab..acc92d3 100644 --- a/src/RolemasterDb.ImportTool/Parsing/CriticalTableParserSupport.cs +++ b/src/RolemasterDb.ImportTool/Parsing/CriticalTableParserSupport.cs @@ -15,6 +15,7 @@ internal static class CriticalTableParserSupport internal const int TopGroupingTolerance = 2; private static readonly Regex MultiFragmentSplitRegex = new(@"\S(?:.*?\S)?(?=(?:\s{2,}|$))", RegexOptions.Compiled); + private static readonly Regex SentenceFragmentSplitRegex = new(@"\S.*?(?:[.!?](?:['"")\]]*)|$)", RegexOptions.Compiled); private static readonly Regex NumericAffixLineRegex = new(@"^\d+(?:H|∑|∏|π|∫|\s*[–-])", RegexOptions.Compiled); private static readonly Regex StandaloneModifierAffixLineRegex = new(@"^(?:\d+)?\((?:\+|-|–)\d+\)$", RegexOptions.Compiled); @@ -373,7 +374,7 @@ internal static class CriticalTableParserSupport supportsPowerPointModifier: footerText.Contains("powerpoint modification", StringComparison.OrdinalIgnoreCase)); } - internal static List SplitBoundaryCrossingAffixFragments( + internal static List SplitBoundaryCrossingFragments( IReadOnlyList bodyFragments, IReadOnlyList<(string Key, double CenterX)> columnCenters, IReadOnlySet affixLegendSymbols) @@ -382,7 +383,7 @@ internal static class CriticalTableParserSupport foreach (var fragment in bodyFragments) { - splitFragments.AddRange(SplitBoundaryCrossingAffixFragment(fragment, columnCenters, affixLegendSymbols)); + splitFragments.AddRange(SplitBoundaryCrossingFragment(fragment, columnCenters, affixLegendSymbols)); } return splitFragments; @@ -467,7 +468,7 @@ internal static class CriticalTableParserSupport !excludedFragments.Contains(item)) .ToList(); - return SplitBoundaryCrossingAffixFragments(bodyFragments, columnCenters, affixLegendSymbols); + return SplitBoundaryCrossingFragments(bodyFragments, columnCenters, affixLegendSymbols); } internal static void RepairLeadingAffixLeakage(List cellEntries, IReadOnlySet affixLegendSymbols) @@ -612,17 +613,17 @@ internal static class CriticalTableParserSupport return true; } - private static IReadOnlyList SplitBoundaryCrossingAffixFragment( + private static IReadOnlyList SplitBoundaryCrossingFragment( XmlTextFragment fragment, IReadOnlyList<(string Key, double CenterX)> columnCenters, IReadOnlySet affixLegendSymbols) { - if (!LooksLikeBoundaryCrossingAffixFragment(fragment, columnCenters, affixLegendSymbols)) + if (!TryGetBoundaryCrossingPattern(fragment, columnCenters, affixLegendSymbols, out var splitPattern)) { return [fragment]; } - var matches = MultiFragmentSplitRegex.Matches(fragment.Text); + var matches = splitPattern.Matches(fragment.Text); if (matches.Count < 2) { return [fragment]; @@ -667,17 +668,40 @@ internal static class CriticalTableParserSupport : [fragment]; } - private static bool LooksLikeBoundaryCrossingAffixFragment( + private static bool TryGetBoundaryCrossingPattern( XmlTextFragment fragment, IReadOnlyList<(string Key, double CenterX)> columnCenters, - IReadOnlySet affixLegendSymbols) + IReadOnlySet affixLegendSymbols, + out Regex splitPattern) { - if (!IsAffixLikeLine(fragment.Text, affixLegendSymbols) || - !fragment.Text.Contains(" ", StringComparison.Ordinal)) + splitPattern = null!; + + if (!CrossesColumnBoundary(fragment, columnCenters)) { return false; } + if (IsAffixLikeLine(fragment.Text, affixLegendSymbols) && + fragment.Text.Contains(" ", StringComparison.Ordinal)) + { + splitPattern = MultiFragmentSplitRegex; + return true; + } + + if (!IsAffixLikeLine(fragment.Text, affixLegendSymbols) && + CountSentenceLikeSegments(fragment.Text) >= 2) + { + splitPattern = SentenceFragmentSplitRegex; + return true; + } + + return false; + } + + private static bool CrossesColumnBoundary( + XmlTextFragment fragment, + IReadOnlyList<(string Key, double CenterX)> columnCenters) + { var fragmentRight = fragment.Left + fragment.Width; for (var index = 0; index < columnCenters.Count - 1; index++) @@ -692,6 +716,11 @@ internal static class CriticalTableParserSupport return false; } + private static int CountSentenceLikeSegments(string text) => + SentenceFragmentSplitRegex.Matches(text) + .Select(match => CollapseWhitespace(match.Value)) + .Count(value => !string.IsNullOrWhiteSpace(value)); + private static void AddLegendMatch( IDictionary symbolEffects, string value,