feat: AI bug fixes, deck/collection migration, and enrichment pipeline

- Fix AI turn pipeline: add cmc to Scryfall cache, tag isRamp/isRemoval/
  isBoardWipe from oracle text during enrichment, enable post-combat spell
  casting for all difficulty levels
- Migrate deck builder, deck generator, and collection from ui/ to
  lan-client/ as iframe-embedded panels with LAB_API_BASE patching
- Fix ai-bridge.js: turnNumber, deck_name, battlefield serialization,
  5-playstyle mapping, window exports for cross-script access
- Fix engine.js: battlefieldCards flat array (not player.battlefield),
  aiDeclareAttacks extraction, combat overlay fixes
- Fix collection.js: template literal, column visibility alignment,
  set_code key, oracle text column, sortable category
- Add exile/graveyard last-card image rendering in ui.js
- Make state_encoder.py resilient to mixed input types
- Enrich type_line from Scryfall at card insert in deckbuilder.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
rgriffithandClaude Opus 4.6 committed 2026-04-05 00:05:23 -04:00
1 parent 30c3ab5a89
commit 409aba26f4
18 files changed
+16726 -36

No files matched your search

+16 -4
View File
@@ -141,8 +141,17 @@ class CardEmbeddingIndex:
logger.error("Auto-download failed: %s", e)
return None
def get_embedding(self, card_name: str) -> np.ndarray:
"""Get embedding vector for a card name. Returns zero vector if not found."""
def get_embedding(self, card_name) -> np.ndarray:
"""Get embedding vector for a card name. Returns zero vector if not found.
Accepts either a plain string or a dict with a 'name' key (defensive
against the LAN client sending structured battlefield objects).
"""
# Handle dict objects from the LAN client's battlefield_detail
if isinstance(card_name, dict):
card_name = card_name.get('name', '')
if not card_name or not isinstance(card_name, str):
return self._zero_vec
if not self._loaded:
return self._zero_vec
idx = self._name_to_idx.get(card_name.lower())
@@ -155,8 +164,11 @@ class CardEmbeddingIndex:
return self._embeddings[idx]
return self._zero_vec
def mean_pool_zone(self, card_names: List[str]) -> np.ndarray:
"""Mean-pool embeddings for a list of card names (a game zone)."""
def mean_pool_zone(self, card_names: list) -> np.ndarray:
"""Mean-pool embeddings for a list of card names (a game zone).
Each entry may be a plain string or a dict with a 'name' key.
"""
if not card_names:
return self._zero_vec
vectors = [self.get_embedding(name) for name in card_names]