Swapped CSS grid-area, zone-row anchoring, commander-row anchoring,
label corners, and life-panel positions for .p3/.p4 so the turn
sequence P1→P2→P3→P4 reads clockwise around the battlefield.
Also added PLAYER_INDEX_TO_QUADRANT lookup in player-panels.js.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Left action panel revised to show triggers list + Create Token/Auto-Arrange.
CMD DMG badges added to center life-panel score areas (click opens matrix).
Removed renderEnhancedPanels calls that split the battlefield on damage adjust.
Added encoding='utf-8' to all bare open() calls across 17 Python files to
prevent cp1252 crashes on MTG card text with smart quotes/em-dashes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
executeReset() references #game-board which doesn't exist in this HTML,
so the function threw a TypeError and silently did nothing. Swap the
exit button's onclick to location.reload() — the browser-refresh path
the user confirmed works to return to the setup screen.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AI hand privacy (rendering layer only):
- renderHandZone(): when player.isAI, render face-down violet card backs
(dark purple diagonal hatch + ornate ring) instead of face-up thumbnails.
Count stays public (standard MTG). No image, name, preview, drag, or
click-to-open-modal on face-down thumbs.
- renderZoneModal(): filter the "Hand" tab out of the zone modal when
inspecting an AI opponent; reset activeZoneTab to 'graveyard' if it was
'hand' when an AI modal opens.
- New CSS: .hand-thumb.face-down (+ .overflow variant) with violet hatch
pattern, radial glow, and centered purple ring pseudo-element.
Scryfall image cache persistence (eliminates per-game refetching):
- _scryfallCache now saves to localStorage under commanderLab.scryfallCache.v1
- 30-day TTL, 20,000-entry cap, graceful quota-exceeded handling
- Debounced auto-save (750ms) after each batch enrichment
- Cache-hit logging: logs hit ratio when refetching, or "from cache" when
a whole game's card pool is already in localStorage
- clearScryfallCache() helper exposed on window for manual purges
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Battlefield (charcoal felt):
- Darken --felt / --felt-light / --felt-dark to #181818 / #202020 / #0d0d0d
- Tighter --felt-line opacities (0.06 / 0.12) on the darker surface
- Finer-grain noise texture (baseFrequency 1.2, tile 180px) at 0.14 alpha
with mix-blend-mode: overlay so the grain rides on the felt
- Deeper vignette (35% center, rgba(0,0,0,0.6) at edges)
Top bar:
- .top-bar-logo: 14px → 28px, align-self: stretch, flex-centered so
"COMMANDER" fills the full 44px height
- .phase-step: 10px → 11px, roomier padding (2px 8px), gap 2→4px,
letter-spacing 0.02em
- .btn-util: 22×22 → 32×32, font 10px → 15px (readable icons again)
- New .btn-util.btn-util-exit ✕ button (red-tinted, wired to
confirmReset() → return to setup screen)
- Remove deck generator (⚡) and collection (📚) util buttons from the
top bar (those panels are still reachable from other entry points)
Setup screen:
- Swap .setup-title from MTG gold to the same neon violet sign effect
as the top-bar logo (4-layer text-shadow, uppercase, ls 0.08em)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apply the same 5-step visual redesign to the LAN client's MTG battlefield
theme while preserving functional game colors (mana, felt table, player
quadrants p1-p4).
- Swap Google Fonts import from Cinzel+Inter to Barlow Condensed + Barlow
- Pure black surfaces (#080808 / 0e / 11 / 16 / 1a)
- Swap primary accent from blue #5b8dd9 to electric violet #8b5cf6
- Square industrial radii (2/4/6/8px)
- Replace all hardcoded 'Cinzel' / 'Inter' with var(--font-display) /
var(--font-body)
- Pure black top bar with violet underglow shadow
- Convert .top-bar-logo "Commander" title from gold to 4-layer violet
neon sign with hover brightening
- Sweep 42 hardcoded rgba(91,141,217,...) blue values downstream to
rgba(139,92,246,...) violet across buttons, zone tabs, stack items,
combat UI, etc.
MTG functional colors (mana W/U/B/R/G, felt table, player quadrants)
remain intact.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The phase track (BEGIN · MAIN 1 · COMBAT · END) was losing labels to
ellipsis on narrow top bars, and the primary action buttons were
clipping "NEXT PHASE"/"NEXT TURN" because they had white-space: nowrap
and a fixed height.
.phase-step:
- tighter padding (1px 5px) and gap (2px)
- no letter-spacing, smaller border-radius, flex-shrink: 1 so pills
compress uniformly when the top bar is crowded
- merged duplicate .phase-step.active rule and made it bolder so the
active step still reads well after the shrink
.btn-phase (NEXT PHASE / NEXT TURN):
- white-space: normal + word-break: break-word enable two-line wrap
- height: auto + min-height: 34px lets the label breathe inside
the 44px top bar
- min-width: 64px / max-width: 80px keeps the buttons square-ish
- display: flex centers the wrapped text vertically & horizontally
- align-items: stretch on .top-bar-actions so both buttons match
the tallest sibling's height
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Addresses the three root causes in commander-ai-lab-land-only-bug-analysis.md
plus several collateral fixes discovered along the way.
P0 — Land-per-turn enforcement (engine.py, deepseek_engine.py, turn_manager.py)
* Guard _play_land() against double plays by tracking
_lands_played_this_turn vs _land_plays_this_turn_limit on each player
* Reset per-turn counters at the start of each player's turn
* Ramp cards that grant additional plays can bump the limit
P0 — Legal move pipeline (new legal_move_generator.py, game_state.py)
* New generate_legal_moves() emits the full macro-action set
(play_land, cast_creature, cast_removal, cast_ramp, cast_draw,
cast_commander, attack, block, pass_priority) based on phase,
active player, land-drop state, mana, and commander tax
* get_legal_moves() delegates to the new generator instead of the
pass-only stub that blocked the turn_manager/policy-server path
* apply_move() gains handlers for play_land, cast_spell,
cast_commander, and declare_attacker — spells resolve to
battlefield or graveyard, stats update correctly
P1 — Policy exploration (LearnedPolicy.java, scope.py)
* Default greedy=false so the neural policy samples its
distribution instead of always taking argmax
* Raise ppo_entropy_coeff 0.01 → 0.04 to keep the action
distribution flat during PPO self-play and prevent the
pass/land collapse
Collateral fixes pulled in during the same session:
* lan-client/precon-loader.js: await enrichAllZonesWithScryfall()
so opening-hand cards have typeLine/cmc before the AI's first turn
* lan-client/ui.js: closeZoneModal auto-shuffles the library
whenever the zone modal closes
* routes/game.py: BatchState() was missing total_games/threads,
crashing POST /api/game/start
* routes/deckgen.py: dead code after raise left headers undefined,
breaking the Anthropic API path
All sim/phase/integration tests pass (650+).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add _enrichCardFromCache() in precon-loader.js for synchronous card
enrichment at draw time (typeLine, cmc, oracle roles available before
async Scryfall batch completes)
- Call _enrichCardFromCache in drawCard() so AI has complete card data
on its first turn
- Improve _canAfford in ai-bridge.js: check actual mana pool before
falling back to untapped land count; free spells (cmc 0) always castable
- Fix totalCards field name mismatch in ui.js and engine.js (lab.py
returns totalCards, not card_count/total_cards)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add user-decks/ directory and USER_DECKS_DIR config for exported decks
- Update export-to-sim to write to user-decks/ with static mount in lab_api
- Replace import/paste UI with API-backed "My Decks" dropdown in LAN client
and AI Lab, fetching from GET /api/lab/decks with source grouping
- Fix GET /api/lab/decks to JOIN deck_cards for totalCards count
- Fix deckgenerator: add missing DOM elements (dg-strategy-bar, dg-stats-bar,
dg-card-groups) inside #dg-results, null guards on render functions,
fix setLoading always hiding results
- Replace gpt-oss:20b with claude-opus-4-20250514 across all files
- Add user-decks/ to _BUILTIN_DECK_DIRS in deck_service.py
- Enrich missing type_line from Forge card cache in DB deck path
- Collapse import bar behind <details> toggle in AI Lab
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Static method was referencing bare gameState which is always null.
Now accepts gameState as parameter, and all 5 callers pass it through.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Was reading gameState.battlefield (doesn't exist) causing AI to
receive empty board state. Now reads gameState.battlefieldCards
and matches by ownerIndex.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add bright silver 3D smoky border around commander card zones
- Wrap all 4 life panels in a single bordered container
- Add drag-and-drop from hand zone to battlefield with ghost preview
- Update card placement to center of battlefield, avoiding overlaid zones
- Fix card dimensions in placement calc (110x150 -> 63x84)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move life/cmd-dmg/poison into 3-box life panels as a top-level
overlay (z-index 50) centered at quadrant intersections
- Remove center emblem
- Match battlefield card size to hand cards (63x84px)
- Overlay zone-row and commander-row on battlefield edges to
eliminate dead space and maximize battlefield area
- Restructure poison badges from per-player absolute positioning
into life-panel counters box
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Scale .hand-thumb from 32x45px to 63x84px so cards fill the
hand zone vertically while maintaining MTG card proportions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Mount lan-client/ as static files at /lan with html=True
- Mount precon-decks/ at /precon-decks/ for direct .dck access
- ai-bridge.js: use same-origin (empty URLs, auto-detect from window.location)
- engine.js: simplify deck fetch to use /api/lab/precons/deck endpoint + /precon-decks/ static fallback
- Eliminates cross-origin issues by serving everything from localhost:8080
Access the LAN battlefield at http://localhost:8080/lan/
Three fixes that prevented precon decks from loading into actual gameplay:
1. ui.js: window.startGame() now calls startGameWithDecks() when
setupDeckChoices has entries (the 3-tab selector stores choices there).
Previously it called initGame() which looked for old deck-sel-{i}
dropdowns that no longer exist.
2. engine.js: loadPreconForPlayer() now fetches .dck file content from
the backend (/api/lab/precons/deck?fileName=...) when precon.decklist
is empty. Falls back to fetching precon-decks/{file} directly.
PRECON_DATA entries only have metadata (name, commander, set) — the
actual card list lives in the .dck files.
3. engine.js: parseDeckList() now handles .dck bracket-style section
headers like [Commander], [Main], [metadata]. Previously only matched
bare text like 'COMMANDER:' without brackets. Also skips [metadata]
lines (e.g. 'Name=Elven Empire').
4. routes/precon.py: Added GET /api/lab/precons/deck?fileName=... endpoint
to serve raw .dck file content for the LAN client.
- Add max-height: 92vh and overflow-y: auto to .setup-card
- Widen .setup-card to 860px to fit 2-column deck grid
- Change .deck-assignment-grid from vertical stack to 2-column grid
so 4 player panels render in 2 rows instead of 4
- Tighten .deck-tab-content (170px) and .deck-precon-list (140px)
- Make top gradient bar sticky on scroll
- Responsive fallback to single column below 640px
- Generate lan-client/precons.js from precon-decks/precon-index.json so
PRECON_DATA is always available for local/offline mode (163 precons)
- Add scripts/generate-precons-js.js for regeneration when precons update
- Fix _loadBackendPrecons() to re-render the precon tab UI when backend
precons load successfully (was only updating status indicator text,
not populating the dropdown)
Without precons.js, PRECON_DATA was undefined and the dropdown showed
'No precon data loaded' even though 163 .dck files exist in the repo.
* feat: LAN Battlefield App integration with Commander-AI-Lab AI pipeline
Add the LAN Battlefield App as a browser client (lan-client/) that
connects to the Commander-AI-Lab backend for ML-powered AI opponents,
3-category deck selection, and enhanced player panels.
Phase 1 - Foundation:
- Remove floating mana pool display (keep internal tracking)
- Add energy, experience, rad, emblems fields to game state
- No-op renderManaPool(), showManaPool(), setupManaPoolDrag()
Phase 2 - Backend Connection (ai-bridge.js):
- BackendConnector: REST + WebSocket connectivity with retry/timeout
- GameStateBridge: Serializes engine.js state for /api/policy/decide
- MacroActionMapper: Translates 8 ML macro-actions to engine.js calls
- DeckSourceClient: Fetches precon/EDHREC decks from backend API
Phase 3 - Deck System:
- 3-tab deck selector (Precons / EDHREC / My Decks) replaces dropdown
- Precon grouping by set with collapsible sections and filtering
- EDHREC search integration (stub, ready for backend implementation)
- My Decks textarea import placeholder
Phase 4 - AI Integration:
- runAiTurn() priority chain: ML Pipeline > Forge > Local Heuristic
- _runMlAiTurn() async turn loop with multi-phase decision requests
- Backend session initialization on game start
- Automatic fallback to local AI on any ML pipeline error
Phase 5 - Enhanced Player Panels (player-panels.js):
- Collapsible panels with localStorage persistence
- Commander damage progress bars with warning/critical thresholds
- Poison, energy, experience, rad counter widgets
- Emblem tracking with add/remove
Phase 6 - Polish:
- Script loading order: precons > engine > ai-bridge > panels > ui
- Backend auto-detection on page load with status indicator
- Connection status display on setup screen
Closes#184, Closes#185, Closes#186, Closes#187, Closes#188, Closes#189
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: refine LAN battlefield integration with full JSDoc, proper phase IDs
- Add comprehensive JSDoc comments to all ai-bridge.js classes
- Use optional chaining for PHASES lookup in GameStateBridge
- Direct addLogEntry calls in player-panels.js (loaded after engine.js)
- Remove leftover mana pool CSS that was missed in initial pass
- Minor style.css cleanup for consistency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>