From 030bdf2b81c26bc78753770c96818136d7d91bd3 Mon Sep 17 00:00:00 2001 From: KoalaTrapLord <265133236+KoalaTrapLord@users.noreply.github.com> Date: Thu, 19 Mar 2026 22:06:17 -0400 Subject: [PATCH] fix: CAST_PATTERN and LAND_PLAY_PATTERN regex to match Forge verbose log prefixes - CAST_PATTERN: Add optional (?:Add To Stack:\s*)? prefix to match Forge's verbose log format - LAND_PLAY_PATTERN: Add optional (?:Land:\s*)? prefix to match Forge's verbose log format - Add [PARSE] diagnostic logging after combat stats parsing to confirm spells/lands/cmdr stats per seat Root cause: Forge verbose output prefixes cast lines with "Add To Stack: " and land lines with "Land: " but the regexes expected Ai(N) at the start of the line, causing 0 matches for all combat stats. --- src/main/java/commanderailab/batch/BatchRunner.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/commanderailab/batch/BatchRunner.java b/src/main/java/commanderailab/batch/BatchRunner.java index d9f7030..9c4bd5e 100644 --- a/src/main/java/commanderailab/batch/BatchRunner.java +++ b/src/main/java/commanderailab/batch/BatchRunner.java @@ -117,12 +117,12 @@ public class BatchRunner { // Format: "Add To Stack: Ai(N)-DeckName cast CardName" // Note: Forge uses past tense "cast" not "casts" private static final Pattern CAST_PATTERN = - Pattern.compile("Ai\\((\\d+)\\)-[^\\s].*?\\s+cast\\s+(.+?)(?:\\s+targeting.*)?$", Pattern.CASE_INSENSITIVE); + Pattern.compile("(?:Add To Stack:\\s*)?Ai\\((\\d+)\\)-[^\\s].*?\\s+cast\\s+(.+?)(?:\\s+targeting.*)?$", Pattern.CASE_INSENSITIVE); // "Land: Ai(1)-Name played LandName (SET)" or "Ai(1)-Name plays LandName." // Forge verbose log uses "played" (past tense) with set info in parens, no trailing period private static final Pattern LAND_PLAY_PATTERN = - Pattern.compile("Ai\\((\\d+)\\)-[^\\s].*?\\s+play(?:s|ed)\\s+(.+?)(?:\\s+\\(\\d+\\))?(?:\\.|$)", Pattern.CASE_INSENSITIVE); + Pattern.compile("(?:Land:\\s*)?Ai\\((\\d+)\\)-[^\\s].*?\\s+play(?:s|ed)\\s+(.+?)(?:\\s+\\(\\d+\\))?(?:\\.|$)", Pattern.CASE_INSENSITIVE); // "Zone Change: CardName (N) was put into Graveyard from Battlefield." — creature/permanent death // Also: "is destroyed" / "dies" from older format @@ -879,6 +879,13 @@ public class BatchRunner { // Leave at 0 rather than fabricating data } + // ── Diagnostic: log parsed combat stats ────────────────────── + for (int seat = 0; seat < result.playerResults.size(); seat++) { + PlayerResult pr = result.playerResults.get(seat); + System.out.printf("[PARSE] Game %d Seat %d: spells=%d, lands=%d, cmdrCasts=%d, cmdrDmg=%d, life=%d%n", + gameIndex, seat, pr.spellsCast, pr.landsPlayed, pr.commanderCasts, pr.commanderDamageDealt, pr.finalLife); + } + // ── Build per-card stats from verbose output (for coach analytics) ── if (!quiet) { try {