- Rename data/cards/ to data/raw/ for scraped data - Add data/definitions/ as authoritative card data source - Add convert_cards.py script to transform raw -> definitions - Generate 378 card definitions (344 Pokemon, 24 Trainers, 10 Energy) - Add CardService for loading and querying card definitions - In-memory indexes for fast lookups by type, set, pokemon_type - search() with multiple filter criteria - get_all_cards() for GameEngine integration - Add SetInfo model for set metadata - Update Attack model with damage_display field for variable damage - Update CardDefinition with image_path, illustrator, flavor_text - Add 45 tests (21 converter + 24 CardService) - Update scraper output path to data/raw/ Card data is JSON-authoritative (no database) to support offline fork goal. |
||
|---|---|---|
| .. | ||
| __init__.py | ||
| actions.py | ||
| card.py | ||
| game_state.py | ||
| README.md | ||
Models Module
Pydantic models for game state, card definitions, and player actions.
Files
| File | Description |
|---|---|
__init__.py |
Module exports |
card.py |
Card-related models |
game_state.py |
Game state and player state |
actions.py |
Player action types |
Card Models (card.py)
CardDefinition
Immutable template defining a card's properties. Stored in the card registry.
CardDefinition(
id="pikachu-001",
name="Pikachu",
card_type=CardType.POKEMON,
stage=PokemonStage.BASIC,
hp=60,
pokemon_type=EnergyType.LIGHTNING,
attacks=[
Attack(name="Thunder Shock", damage=20, cost=[EnergyType.LIGHTNING]),
],
weakness=WeaknessResistance(energy_type=EnergyType.FIGHTING, value=20),
retreat_cost=1,
)
CardInstance
A specific card in play, referencing a CardDefinition. Tracks runtime state.
CardInstance(
instance_id="p1-pikachu-0", # Unique per-game
definition_id="pikachu-001", # References CardDefinition
damage=30, # Current damage
attached_energy=[...], # Energy cards attached
status_conditions=[StatusCondition.PARALYZED],
)
Attack
A Pokemon's attack with cost, damage, and optional effect.
Attack(
name="Crimson Storm",
cost=[EnergyType.FIRE, EnergyType.FIRE, EnergyType.COLORLESS],
damage=200,
effect_id="discard_energy",
effect_params={"count": 2},
effect_description="Discard 2 Energy from this Pokemon.",
)
Ability
A Pokemon's special ability.
Ability(
name="Psy Shadow",
effect_id="energy_acceleration",
effect_params={"energy_type": "psychic", "target": "active"},
effect_description="Attach Psychic Energy from Energy Zone to Active.",
uses_per_turn=1,
)
WeaknessResistance
Weakness or resistance to an energy type.
# Additive weakness (+20)
WeaknessResistance(energy_type=EnergyType.FIRE, mode=ModifierMode.ADDITIVE, value=20)
# Multiplicative weakness (x2)
WeaknessResistance(energy_type=EnergyType.FIRE, mode=ModifierMode.MULTIPLICATIVE, value=2)
# Resistance (-30)
WeaknessResistance(energy_type=EnergyType.WATER, mode=ModifierMode.ADDITIVE, value=-30)
Game State Models (game_state.py)
GameState
Complete state of a game. Self-contained with embedded card registry.
GameState(
game_id="match-123",
rules=RulesConfig(),
card_registry={...},
players={"p1": PlayerState(...), "p2": PlayerState(...)},
current_player_id="p1",
turn_number=3,
phase=TurnPhase.MAIN,
)
Key Methods:
get_current_player()- Current player's stateget_opponent(player_id)- Opponent's stateget_card_definition(card_id)- Lookup card definitionadvance_turn()- Move to next player's turn
PlayerState
All zones and state for a single player.
player = game.players["p1"]
# Zones (all are Zone objects)
player.deck # Draw pile
player.hand # Cards in hand
player.active # Active Pokemon (0-1 cards)
player.bench # Benched Pokemon
player.discard # Discard pile
player.prizes # Prize cards
player.energy_deck # Energy deck (Pocket-style)
player.energy_zone # Available energy this turn
# State tracking
player.score # Points scored
player.energy_attachments_this_turn
player.supporters_played_this_turn
Key Methods:
get_active_pokemon()- Returns active Pokemon or Noneget_all_pokemon_in_play()- Active + benchcan_attach_energy(rules)- Check turn limitsreset_turn_state()- Clear per-turn counters
Zone
A collection of cards (deck, hand, bench, etc.).
zone = player.hand
len(zone) # Card count
"card-id" in zone # Check if card present
zone.add(card) # Add to end
zone.add_to_top(card) # Add to top (for deck)
zone.remove("card-id") # Remove and return card
zone.draw() # Remove and return from top
zone.shuffle(rng) # Randomize order
zone.get("card-id") # Get without removing
zone.clear() # Remove all cards
ForcedAction
Represents a required action (e.g., "select new active after KO").
ForcedAction(
player_id="p1",
action_type="select_active",
reason="Your active Pokemon was knocked out",
params={"from_zones": ["bench"]},
)
Action Models (actions.py)
All actions implement a common interface with type discriminator field.
Attack Actions
AttackAction(attack_index=0) # Use first attack
Energy Actions
AttachEnergyAction(
energy_card_id="p1-lightning-0",
target_pokemon_id="p1-pikachu-0",
from_energy_zone=True,
)
Pokemon Actions
PlayPokemonAction(card_id="p1-bulbasaur-0", to_active=False)
EvolvePokemonAction(card_id="p1-ivysaur-0", target_pokemon_id="p1-bulbasaur-0")
Trainer Actions
PlayTrainerAction(card_instance_id="p1-potion-0", targets=["p1-pikachu-0"])
Ability Actions
UseAbilityAction(pokemon_id="p1-greninja-0", ability_index=0, targets=["p2-charmander-0"])
Movement Actions
RetreatAction(new_active_id="p1-squirtle-0", energy_to_discard=["p1-water-0"])
SelectActiveAction(new_active_id="p1-squirtle-0") # After KO
Turn Control
PassAction() # End turn without attacking
ResignAction() # Forfeit the game
Action Union Type
Action = (
AttackAction | AttachEnergyAction | PlayPokemonAction |
EvolvePokemonAction | PlayTrainerAction | UseAbilityAction |
RetreatAction | SelectActiveAction | PassAction | ResignAction |
SelectPrizeAction
)
Model Relationships
GameState
├── card_registry: dict[str, CardDefinition]
├── players: dict[str, PlayerState]
│ └── PlayerState
│ ├── deck: Zone[CardInstance]
│ ├── hand: Zone[CardInstance]
│ ├── active: Zone[CardInstance]
│ │ └── CardInstance
│ │ ├── definition_id -> CardDefinition
│ │ ├── attached_energy: list[CardInstance]
│ │ └── evolution_stage: list[CardInstance]
│ ├── bench: Zone[CardInstance]
│ ├── discard: Zone[CardInstance]
│ └── ...
├── stadium_in_play: CardInstance | None
└── forced_actions: list[ForcedAction]
See Also
- ../README.md - Core module overview
- /docs/ARCHITECTURE.md - System architecture