Commit Graph
114 Commits
Author SHA1 Message Date
Claude Code ea2cac6597 fix: default Ollama endpoints to localhost instead of 192.168.0.240
The hardcoded 192.168.0.240 IP was a developer-specific LAN address
that breaks out-of-the-box for anyone running Ollama locally or via
WSL2 on Windows. Switch every default and docstring reference to
localhost so the project works without manual configuration.

Files changed:
- coach/config.py: LLM_URL and DECK_GEN_BASE_URL defaults
- ui/simulator.html: API endpoint input value and placeholder
- lab_api.py: remove LAN IP from default CORS origins
- routes/coach.py: warning message text
- src/commander_ai_lab/sim/deepseek_brain.py: docstrings
- src/commander_ai_lab/sim/deepseek_engine.py: docstring example
- src/commander_ai_lab/sim/validator_brain.py: docstring example

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-24 00:32:08 +00:00
56e5bff496 fix: correct indentation of last_err in DeepSeekBrain.choose_action() (#97)
`last_err = None` was at module-level indentation (4 spaces) instead of
method-body indentation (8 spaces), causing an IndentationError on import.

Co-authored-by: Claude Agent <claude-agent@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 20:01:11 -04:00
KoalaTrapLord 62463dfa4a fix: correct indentation in deepseek_brain.py (choose_action + _parse_action) 2026-03-23 19:34:14 -04:00
KoalaTrapLord 7e0aba78ba fix: add ML logging support to MultiThreadBatchRunner 2026-03-23 19:01:43 -04:00
KoalaTrapLord 7036c9568f fix: enable ML decision logging in multi-thread batch path 2026-03-23 18:58:14 -04:00
KoalaTrapLord d424ccb74c feat(86): add combat priority checkpoints with CombatState and split engine methods (#96) 2026-03-23 16:52:19 -04:00
KoalaTrapLord f3bd188a8b fix(#95): implement retry loop and bump max_tokens/max_retries defaults
max_retries was configured but never used — choose_action() made
exactly one attempt. Now wraps _call_llm() in a retry loop.
Bumped max_retries default to 2 and max_tokens to 4096 (R1 needs
room for <think> blocks + JSON). Fixes issue #95 item 3.
2026-03-23 14:29:22 -04:00
KoalaTrapLord c1ad9e953c fix(#95): replace regex JSON extraction with outermost-brace approach
_parse_action() used r'\{[^{}]*\}' regex which fails on nested
JSON (e.g. oracle text with braces). Now uses text.find/rfind to
extract the outermost { } block, handling nested structures.
Fixes issue #95 item 2.
2026-03-23 14:24:57 -04:00
KoalaTrapLord 25e064d265 fix(#95): prevent check_connection from overwriting configured model
check_connection() was auto-overwriting self.config.model with the
first model returned by /v1/models, causing deepseek-r1:8b to be
replaced by deepseek-r1:14b (or whatever Ollama listed first).

Now logs available models without changing the configured model.
Fixes issue #95 item 1.
2026-03-23 10:37:39 -04:00
eee773b6f9 Forge Card Enrichment — Full Implementation (Issue #80) (#81)
* feat(issue-80): Phase 1 — add services/forge_card_loader.py

* feat(issue-80): Phase 2 — add 5 Forge fields to Card dataclass

* feat(issue-80): Phase 3+4 — wire Forge into enrich_card/score_card

* feat(issue-80): Phase 5 — add tests/sim/test_forge_enrichment.py

* fix(issue-80): resolve PR #81 blocking issues — import paths, indentation, partial Forge fallback

- Move forge_card_loader.py from services/ into src/commander_ai_lab/sim/
  so the import in rules.py uses a proper intra-package path instead of
  crossing package boundaries via services.forge_card_loader
- Fix indentation of Forge enrichment comment in models.py (8→4 spaces)
- Fix enrich_card() early return on partial Forge data: re-check
  has_real_data after _apply_forge_data so cards with incomplete Forge
  entries still fall through to known-cards / default heuristics
- Fix flawed test assertion in test_etb_via_forge_mode_equals_text_detection:
  oracle text "draw a card" was adding a +3 spell_draw bonus, making the
  score comparison invalid; replaced with side-effect-free ETB text

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Code <claude-code@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 09:37:05 -04:00
0f8d1382e0 feat: explicit turn-phase structure with Phase enum and main2 support (#86) (#93)
Refactor the headless engine to walk through all 12 MTG turn phases in
order (untap → upkeep → draw → main1 → begin_combat → declare_attackers
→ declare_blockers → combat_damage → end_combat → main2 → end_step →
cleanup) instead of the previous flat per-player block.

Key changes:
- Add Phase str enum and PHASE_ORDER/SORCERY_PHASES constants to models
- Track current_phase and active_player_index on SimState
- Sync sim.turn with the loop turn counter (fixes latent summoning-
  sickness bug where turn_played was always compared against 0)
- Add second main phase (main2) with shared 2-spell budget across
  main1 + main2
- _play_spells now accepts max_spells param and returns count played
- Unify turn_manager.py PHASES/ACTION_PHASES/RESPONSE_PHASES to derive
  from the canonical Phase enum in models.py
- 33 regression tests covering phase enum, phase progression, spell
  budget, game log structure, backward compat, and turn manager
  constant consistency

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 23:46:09 -04:00
KoalaTrapLord b72d5fba83 fix(#92): bump READ_TIMEOUT_MS from 5s to 300s in PolicyServerClient.java 2026-03-22 23:44:47 -04:00
KoalaTrapLord 5d34d07bb8 fix(#92): bump validator_brain.py timeouts from 60s to 300s 2026-03-22 23:43:30 -04:00
KoalaTrapLord 5e9685ba70 fix(#92): read BRAIN_TIMEOUT env for ai_decision_timeout + add import os in turn_manager.py 2026-03-22 23:42:28 -04:00
KoalaTrapLord c2b3d937a4 fix(#92): read BRAIN_TIMEOUT env for request_timeout in deepseek_brain.py 2026-03-22 23:39:33 -04:00
5e9c568bcc feat: implement London mulligan and opening-hand validation (#86) (#91) (#91)
Add London mulligan rule to the headless game engine:

- GameEngine now accepts mulligan_rule param ("london" default, "none" for legacy)
- AI heuristic: mulligan 0-1 land or 6-7 land hands; relax after 2+ mulligans
- After keeping, bottom N lowest-scored cards (London mulligan rule)
- Cap at MAX_MULLIGANS=4 to prevent infinite loops on degenerate decks
- Track mulligans and opening_hand_lands in PlayerStats
- Serialize both fields in to_dict() for batch-result schema compatibility
- 28 targeted regression tests covering unit helpers, integration,
  N-player, commander interaction, serialization, and legacy mode

Co-authored-by: Claude Agent <claude-agent@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 23:28:01 -04:00
KoalaTrapLordandClaude Code 85aa15445b feat: route direct-damage spells through centralized deal_damage() (#86) (#90)
Add centralized deal_damage() method to GameEngine that handles life
reduction, stat tracking, commander damage (both per-seat and per-card),
and elimination checks. Route both burn spells and combat damage through
this unified path, building on the dual-dict commander damage model
merged in PR #89.

- Add is_direct_damage / direct_damage_amount fields to Card
- Add burn spell detection via known-card table + oracle regex fallback
- Route burn spells in _play_spells() through deal_damage()
- Refactor _resolve_combat() to route per-attacker damage through deal_damage()
- Add 38 regression tests covering deal_damage(), burn detection, spell routing,
  combat routing, and full-game integration

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-03-22 22:27:37 -04:00
KoalaTrapLordandClaude e265b1e15b feat: commander damage source identity and per-card tracking (#86) (#89)
Track commander damage at combat-damage time by checking `is_commander`
on each attacking creature.  Damage from commander creatures is now
recorded in both a per-seat aggregate (`commander_damage_received`) and
a per-card breakdown (`commander_damage_by_card`), enabling correct
21-damage lethal checks for partner/multi-commander games.

Changes:
- engine._resolve_combat: identify commander attackers, accumulate their
  damage (including trample overflow and double strike), write to both
  tracking dicts, and check commander-damage elimination after combat.
- models.Player: add commander_damage_received, commander_damage_by_card,
  and is_dead_to_commander_damage() so the headless engine can track
  commander damage without the full CommanderGameState wrapper.
- game_state.CommanderPlayer: replace the separate
  commander_damage_received field with a property delegating to
  base.Player to avoid split-brain state between engine and wrapper.
- game_state.CommanderGameState.deal_commander_damage: accept optional
  commander_name for per-card tracking.
- 22 new regression tests covering unblocked, blocked, trample, double
  strike, lifelink, partner, cumulative, and lethal scenarios.

Partial fix for Issue #86 item 2.

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-22 22:04:19 -04:00
KoalaTrapLord ae8d14c19e chore: point deepseek brain at deepseek-r1:8b on localhost 2026-03-22 21:49:45 -04:00
KoalaTrapLord f090a36f79 chore: switch deck builder default model to qwen2.5:7b 2026-03-22 21:40:26 -04:00
cf4e2981e6 fix: resolve 78 test failures via shared stub cleanup and dependent fixes (#85) (#88)
Centralize and enhance test stubs to implement the full CommanderGameState
interface (battlefield(), living_players(), stack_is_empty(), sim_state,
active_player(), priority_player(), to_dict()). Fix all dependent test
failures across 12 files:

- conftest.py: Enhanced StubCard/StubPlayer/StubGameState with full API
  compatibility; added basic_creature fixture; fixed fresh_app singleton leak
- test_phase3.py: Added battlefield(), active_player(), prompt_builder compat
- test_integration.py: Fixed ThreatAssessor import (class→function), added
  threat_fn dict wrapper, sim_state delegate
- test_phase6.py/test_phase8.py: Replaced deprecated get_event_loop() with
  asyncio.run()
- test_benchmark.py: Same async fix + ThreatAssessor functional API
- sim/test_engine.py: Removed nonexistent seed= param, use run() method
- sim/test_models.py: basic_creature fixture now in conftest
- deck_builder/test_models.py: Updated ValidationError regex match
- state_logger.py: Added monotonic counter for unique snapshot filenames
- ai_opponent.py: Moved _CANNED dict to module-level constant (dataclass fix)
- web/app.py: Mount lobby router (fixes 6 LobbyAPI test failures)
- test_phase5.py: SessionStore + api._store reset fixture

Result: 76 failures + 2 errors → 0 failures, 0 errors (331 passed, 30 skipped)

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 17:32:30 -04:00
KoalaTrapLord cffd8d8109 fix: add creature damage accumulation to combat engine
Previously _resolve_combat() used a binary power-vs-toughness kill check:
creatures only died if a single source had power >= their toughness in one
swing. This meant a 2/2 wouldn't die to two 1/1 tokens blocking it, and
repeated smaller hits across combats never accumulated.

Changes:
- models.py: add damage_marked field to Card (default 0)
- engine.py: accumulate damage via damage_marked += power instead of
  binary a_pow >= toughness checks in _resolve_combat()
- engine.py: clear damage_marked = 0 in untap phase (MTG rule 514.2)

This enables multi-blocker lethality, incremental chip damage within a
combat step, and proper creature death-by-accumulated-damage interactions.
2026-03-22 04:52:28 +00:00
KoalaTrapLord f86a4af86f feat(ml): add ML decision logging to base GameEngine
- Add ml_log param, ml_decisions list, flush_ml_decisions() to GameEngine
- Capture ML snapshots at each player turn in run_n()
- Stamp game_outcome (win/loss per seat) on all decisions
- Add _capture_ml_snapshot() matching DeepSeekGameEngine format
- Add game_id and archetype params to run_n() for ML tracking
- Import logging, uuid; add logger
2026-03-21 13:54:33 -04:00
KoalaTrapLord 9c0fa64a69 feat: Phase 7 — add validation field to decision log schema
Add "validation" key to _log_decision() entry dict in deepseek_brain.py.
Captures result.get("validation") (None if validator disabled)
for R1-14B output preservation and future analysis.
2026-03-21 13:31:08 -04:00
KoalaTrapLord b1996a8deb fix: indent __init__ into DeepSeekGameEngine class body 2026-03-20 22:01:25 -04:00
KoalaTrapLord c44d0b32bb Update DeepSeek port from 1234 to 11434 in deepseek_engine.py 2026-03-20 21:55:10 -04:00
KoalaTrapLord 9020d29745 Update DeepSeek port from 1234 to 11434 in deepseek_brain.py 2026-03-20 21:53:27 -04:00
KoalaTrapLord 7284b8617d feat(sim): wire DeepSeek-R1-14B validator into deepseek_engine 2026-03-20 21:16:16 -04:00
KoalaTrapLord c53e82bfeb feat(sim): add DeepSeek-R1-14B validator brain module 2026-03-20 17:20:30 -04:00
KoalaTrapLord f8b13703b8 fix: CAST_PATTERN now matches both 'cast' and 'casts' for Forge compatibility 2026-03-20 15:04:37 -04:00
KoalaTrapLord ff6040b53e Remove stray closing brace that would break compilation 2026-03-20 13:31:50 -04:00
KoalaTrapLord db7bc62d7e Fix commander damage tracking, add PLAYER_DAMAGE_SOURCE_PATTERN and diagnostic logging 2026-03-20 13:28:54 -04:00
KoalaTrapLord fbffe81846 feat: auto-detect commander card names from .dck files for commander cast tracking 2026-03-20 12:48:48 -04:00
KoalaTrapLord 9369cf5450 docs: fix adapter.py docstring to reference lab_api.py instead of deleted lab_api_new.py 2026-03-20 12:19:07 -04:00
KoalaTrapLord a2fbc30b2b fix: resolve forge_dir to forge-gui sibling when res/ not found in forge-gui-desktop 2026-03-20 11:22:44 -04:00
KoalaTrapLord 9f1277f233 fix: update DeepSeek endpoint IP in deepseek_engine.py 2026-03-20 11:08:32 -04:00
KoalaTrapLord 66ce887fe3 fix: update DeepSeek endpoint IP in deepseek_brain.py docstring 2026-03-20 11:07:38 -04:00
KoalaTrapLord 93a421b90a fix: make colons optional in all regex patterns to match Forge output format (no colons after Game Outcome, Life, Add To Stack, Land, Zone Change, etc.); fix VERBOSE_TURN_PATTERN for 'Turn Turn N AiX' format; fix LIFE_CHANGE_PATTERN to use space instead of > separator 2026-03-20 09:26:02 -04:00
KoalaTrapLord ee95f26bc4 fix: restore double-backslash escaping in Ai regex patterns (fixes compilation errors) 2026-03-20 09:03:55 -04:00
KoalaTrapLord 2ee574f678 diag: add [COMBAT-LINE] logging to identify Forge verbose output format for combat stats parsing 2026-03-20 08:15:03 -04:00
KoalaTrapLord 473a2bdcc7 fix: make Ai regex patterns support both Ai(N) and AiN formats for Forge output compatibility 2026-03-19 23:29:53 -04:00
KoalaTrapLord a6e50ee0f0 feat: aggregate combat stats in StatsAggregator 2026-03-19 22:45:05 -04:00
KoalaTrapLord e2a6a75fd9 feat: add combat stat fields to DeckSummary schema 2026-03-19 22:39:51 -04:00
KoalaTrapLord 030bdf2b81 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.
2026-03-19 22:06:17 -04:00
KoalaTrapLord 103a74a0e1 fix: update DeepSeek brain default api_base to 192.168.0.240:1234 2026-03-19 17:11:56 -04:00
KoalaTrapLord 1f93b5510d fix: add missing load_weights() to rules.py 2026-03-19 15:49:00 -04:00
KoalaTrapLord 6135feb644 feat: add run_substitution stub + clean up adapter 2026-03-19 13:49:13 -04:00
KoalaTrapLord a8ef10d5b7 feat: add DeckBuilderAdapter to bridge Ollama pipeline to legacy API 2026-03-19 13:24:29 -04:00
KoalaTrapLord 70dfa3d3e0 fix: robust JSON extraction for LLM responses (_extract_json) 2026-03-19 12:29:49 -04:00
KoalaTrapLord f970b6d571 fix: api __init__ imports modules not classes 2026-03-19 12:08:26 -04:00