DecisionExtractor requires verbose Forge output to reconstruct board
states. When quiet=true, Forge runs with -q which strips the verbose
log lines, causing 0 decisions to be extracted. This change passes
quiet=false to BatchRunner when mlLogEnabled is true.
- 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.
Issue #1 — Winner's life total now captured:
- Parse verbose Forge log for life-total-change events
- Track last-known life per seat via 3 regex patterns
- Winner's final life set from log data (not left at default 40)
- Losers who died to mill/poison retain their actual life total
Issue #2 — Win condition classification formalized:
- New WinCondition enum (schema/WinCondition.java) with 8 categories
- Centralized Forge loss-reason-string → enum mapping (regex-based)
- UNKNOWN fallback with logging for unrecognized loss reasons
- All classification now goes through WinCondition.classify()
Issue #3 — JVM startup overhead reduced:
- Added -XX:+UseSerialGC (lighter GC for batch processes)
- Added -XX:TieredStopAtLevel=1 (faster JIT, less compilation)
- ForgeWorker stub class for future persistent batch mode
- Estimated reduction: ~2-3s → ~1-1.5s per game startup
Issue #4 — Single-threaded throughput optimization:
- New CLI flags: --ai-simplified, --ai-think-time, --benchmark
- JVM flags passed through to Forge subprocess
- Benchmark mode: run 1 game before batch, report wall/Forge/overhead times
- API support: aiSimplified, aiThinkTimeMs in StartRequest
Issue #5 — Threading, progress, and backpressure:
- --threads 0 or -1 auto-detects optimal count from CPU topology
- detectOptimalThreads() estimates physical cores (excludes HT)
- ProgressCallback interface for real-time sims/sec reporting
- Per-game progress emitted as [PROGRESS] lines, parsed by lab_api.py
- --max-queue flag for graceful backpressure (limits concurrent JVMs)
- Final throughput summary printed at batch completion
- StatusResponse now includes simsPerSec field
Also: .gitignore restored, lab_api.py updated with all new API fields