From 547a84d8c220ee0ffe1e87057ef91f721f84db12 Mon Sep 17 00:00:00 2001 From: KoalaTrapLord Date: Sun, 5 Apr 2026 17:24:34 +0000 Subject: [PATCH] fix: DecisionExtractor regex patterns to match actual Forge verbose output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forge verbose log uses different verb forms and prefixes than the patterns expected: - Turn: 'Turn: Turn 5 (Ai(1)' not 'Turn 5 (Ai(1)' - Cast: 'cast' (no s) not 'casts', 'Add To Stack:' prefix, no period - Land: 'played' not 'plays', 'Land:' prefix, card ID suffix '(76)' - Draw: 'drew' variant, card ID suffix Updated all four patterns to match both old and new Forge output formats. This is the final fix for the training data pipeline — DecisionExtractor will now find cast/land/draw/attack events in the Forge game log and emit JSONL training snapshots. --- .../commanderailab/ml/DecisionExtractor.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/main/java/commanderailab/ml/DecisionExtractor.java b/src/main/java/commanderailab/ml/DecisionExtractor.java index 4a1edca..e32b24c 100644 --- a/src/main/java/commanderailab/ml/DecisionExtractor.java +++ b/src/main/java/commanderailab/ml/DecisionExtractor.java @@ -31,25 +31,28 @@ public class DecisionExtractor { // ── Patterns ────────────────────────────────────────────── - // Turn boundary: "Turn 5 (Ai(1)-Name)" or "== Turn 5 (Ai(1)-Name) ==" + // Turn boundary: "Turn 5 (Ai(1)-Name)" or "Turn: Turn 5 (Ai(1)-Name)" + // Forge verbose output prefixes with "Turn: " private static final Pattern TURN_PATTERN = - Pattern.compile("Turn\\s+(\\d+)\\s+\\(Ai\\((\\d+)\\)-", Pattern.CASE_INSENSITIVE); + Pattern.compile("Turn(?::\\s*Turn)?\\s+(\\d+)\\s+\\(Ai\\((\\d+)\\)-", Pattern.CASE_INSENSITIVE); // Phase indicator: various Forge phase strings private static final Pattern PHASE_PATTERN = Pattern.compile("(?:Phase|Step):\\s*(.*?)(?:\\s*$|\\s*-)", Pattern.CASE_INSENSITIVE); - // Cast: "Ai(1)-Name casts CardName." + // Cast: "Ai(1)-Name casts CardName." or "Add To Stack: Ai(1)-Name cast CardName" + // Forge verbose output uses "cast" (no s) and may have "Add To Stack:" prefix private static final Pattern CAST_PATTERN = - Pattern.compile("Ai\\((\\d+)\\)-[^\\s].*?\\s+casts\\s+(.+?)\\.", Pattern.CASE_INSENSITIVE); + Pattern.compile("Ai\\((\\d+)\\)-[^\\s].*?\\s+casts?\\s+(.+?)(?:\\s*\\(\\d+\\))?(?:\\.|$)", Pattern.CASE_INSENSITIVE); - // Land play: "Ai(1)-Name plays LandName." + // Land play: "Ai(1)-Name plays LandName." or "Land: Ai(1)-Name played LandName (76)" + // Forge verbose output uses "played" and may have "Land:" prefix + card ID suffix private static final Pattern LAND_PATTERN = - Pattern.compile("Ai\\((\\d+)\\)-[^\\s].*?\\s+plays\\s+(.+?)\\.", Pattern.CASE_INSENSITIVE); + Pattern.compile("Ai\\((\\d+)\\)-[^\\s].*?\\s+play(?:s|ed)\\s+(.+?)(?:\\s*\\(\\d+\\))?(?:\\.|$)", Pattern.CASE_INSENSITIVE); - // Draw: "Ai(1)-Name draws CardName." + // Draw: "Ai(1)-Name draws CardName." or "Ai(1)-Name drew CardName" private static final Pattern DRAW_PATTERN = - Pattern.compile("Ai\\((\\d+)\\)-[^\\s].*?\\s+draws\\s+(.+?)\\.", Pattern.CASE_INSENSITIVE); + Pattern.compile("Ai\\((\\d+)\\)-[^\\s].*?\\s+(?:draws|drew)\\s+(.+?)(?:\\s*\\(\\d+\\))?(?:\\.|$)", Pattern.CASE_INSENSITIVE); // Attack: "Ai(1)-Name attacks with" or "declares attackers" private static final Pattern ATTACK_PATTERN =