| """ |
| game/deck.py — the curated duel deck (cards with real ground truth). |
| |
| Phase-1 deck reuses the bundled validated example photos; each card's truth tier |
| is resolved from the same SPECIES_METADATA the live pipeline uses, so the game and |
| the model are graded against one source of truth. Grow the deck by dropping more |
| labelled images in examples/ and adding rows to deck.json. |
| """ |
|
|
| import json |
| import os |
| import random |
|
|
| from pipeline.metadata import SPECIES_METADATA, UNKNOWN_META |
|
|
| _HERE = os.path.dirname(__file__) |
| _IMAGES_DIR = os.path.join(_HERE, "deck_images") |
|
|
|
|
| def _load_deck() -> list[dict]: |
| with open(os.path.join(_HERE, "deck.json")) as f: |
| raw = json.load(f) |
| deck = [] |
| for c in raw: |
| path = os.path.join(_IMAGES_DIR, c["file"]) |
| if not os.path.exists(path): |
| continue |
| meta = SPECIES_METADATA.get(c["species"], UNKNOWN_META) |
| deck.append({ |
| "file": c["file"], |
| "path": path, |
| "species": c["species"], |
| "tier": meta["safety"], |
| "scientific": meta["scientific"], |
| }) |
| return deck |
|
|
|
|
| DECK = _load_deck() |
|
|
|
|
| def random_card(exclude_file: str | None = None) -> dict: |
| """Return a random card; avoid repeating the immediately-previous one when possible.""" |
| pool = [c for c in DECK if c["file"] != exclude_file] or DECK |
| return random.choice(pool) |
|
|