Commit Graph

9 Commits

Author SHA1 Message Date
Cal Corum
934aa4c443 Add CardService and card data conversion pipeline
- 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.
2026-01-27 14:16:40 -06:00
Cal Corum
e7431e2d1f Move enums to app/core/enums.py and set up clean module exports
Architectural refactor to eliminate circular imports and establish clean
module boundaries:

- Move enums from app/core/models/enums.py to app/core/enums.py
  (foundational module with zero dependencies)
- Update all imports across 30 files to use new enum location
- Set up clean export structure:
  - app.core.enums: canonical source for all enums
  - app.core: convenience exports for full public API
  - app.core.models: exports models only (not enums)
- Add module exports to app/core/__init__.py and app/core/effects/__init__.py
- Remove circular import workarounds from game_state.py

This enables app.core.models to export GameState without circular import
issues, since enums no longer depend on the models package.

All 826 tests passing.
2026-01-26 14:45:26 -06:00
Cal Corum
8e084d250a Fix per-ability usage tracking (Issue #9)
Changed ability_uses_this_turn from int to dict[int, int] to track each
ability's usage independently:

- ability_uses_this_turn: dict[int, int] maps ability index to use count
- can_use_ability() now requires ability_index parameter
- Added get_ability_uses() and increment_ability_uses() helper methods
- reset_turn_state() clears the dict instead of setting to 0

This fixes the bug where using one ability could incorrectly block
another ability on the same Pokemon from being used.

789 tests passing.
2026-01-26 11:11:23 -06:00
Cal Corum
7fae1c61e8 Add CardDefinition validation for required fields (Issue #2)
- Add model_validator to enforce card-type-specific required fields
- Pokemon: require hp (positive), stage, pokemon_type
- Pokemon Stage 1/2 and VMAX/VSTAR: require evolves_from
- Trainer: require trainer_type
- Energy: require energy_type (auto-fills energy_provides)
- Update all test fixtures to include required fields
- Mark Issue #2 as FIXED in SYSTEM_REVIEW.md

765 tests passing
2026-01-26 10:28:37 -06:00
Cal Corum
2b8fac405f Implement energy/tools as CardInstance + evolution stack + devolve effect
Major refactor to properly track attached cards and evolution history:

Model Changes (app/core/models/card.py):
- Change attached_energy from list[str] to list[CardInstance]
- Change attached_tools from list[str] to list[CardInstance]
- Add cards_underneath field for evolution stack tracking
- Update attach_energy/detach_energy to work with CardInstance
- Add attach_tool/detach_tool methods
- Add get_all_attached_cards helper

Engine Changes (app/core/engine.py):
- _execute_attach_energy: Pass full CardInstance to attach_energy
- _execute_evolve: Build evolution stack, transfer attachments, clear status
- _execute_retreat: Detached energy goes to discard pile
- Fix: Evolution now clears status conditions (Pokemon TCG standard)

Game State (app/core/models/game_state.py):
- find_card_instance now searches attached_energy, attached_tools, cards_underneath

Turn Manager (app/core/turn_manager.py):
- process_knockout: Discard all attached energy, tools, and evolution stack

Effects (app/core/effects/handlers.py):
- discard_energy: Find owner's discard pile and move detached energy there
- NEW devolve effect: Remove evolution stages with configurable destination
- Fix: Use EffectType.SPECIAL instead of non-existent EffectType.ZONE

Rules Validator (app/core/rules_validator.py):
- Update energy type checking to iterate CardInstance objects

Tests:
- Update existing tests for new CardInstance-based energy attachment
- NEW test_evolution_stack.py with 28 comprehensive tests covering:
  - Evolution stack building (Basic -> Stage 1 -> Stage 2)
  - Energy/tool transfer and damage carryover on evolution
  - Devolve effect (single/multi stage, hand/discard destination, KO check)
  - Knockout processing with all attachments going to discard
  - find_card_instance for attached cards and evolution stack

All 765 tests pass.
2026-01-25 23:09:40 -06:00
Cal Corum
dba2813f80 Add effects system with configurable weakness/resistance
Effects System (Week 3):
- EffectContext: helper methods for player/card access, params, coin flips
- EffectResult: success, message, effect_type, details for logging
- @effect_handler decorator with sync/async support and introspection
- resolve_effect() for executing effects by ID

Built-in Handlers (13 total):
- deal_damage: raw damage primitive (poison, burn, recoil)
- attack_damage: combat damage with modifiers, weakness, resistance
- heal, draw_cards, discard_from_hand, shuffle_deck
- apply_status, remove_status
- coin_flip_damage, bench_damage
- discard_energy, modify_hp, modify_retreat_cost

Configurable Weakness/Resistance:
- ModifierMode enum: MULTIPLICATIVE (x2) or ADDITIVE (+20)
- CombatConfig in RulesConfig for game-wide defaults
- WeaknessResistance supports per-card mode/value overrides
- Legacy 'modifier' field maintained for backwards compatibility

Test Coverage: 98% (418 tests)
- 84 tests for effects system (base, registry, handlers)
- Comprehensive edge case coverage for all handlers
- CardDefinition helper methods tested for non-Pokemon cards
- Zone edge cases (draw_bottom empty, peek_bottom overflow)
2026-01-25 00:25:38 -06:00
Cal Corum
092f493cc8 Add stat modifiers and attack cost overrides to CardInstance
Support card effects that modify Pokemon stats:
- hp_modifier: Additive HP change (e.g., +20 from Giant Cape tool)
- retreat_cost_modifier: Additive retreat cost change (e.g., -99 for Float Stone)
- damage_modifier: Additive damage change for attacks
- attack_cost_overrides: dict[int, list[EnergyType]] for per-attack cost changes

Added helper methods:
- effective_hp(base_hp) - returns max(1, base_hp + hp_modifier)
- effective_retreat_cost(base_cost) - returns max(0, base_cost + modifier)
- effective_attack_cost(attack_index, base_cost) - returns override or base

Updated is_knocked_out() and remaining_hp() to use effective HP.

Also updated PROJECT_PLAN.json:
- Marked HIGH-003, TEST-006, HIGH-004 as completed (Week 2 complete)
- Updated test counts and notes to reflect stat modifier coverage

289 tests passing.
2026-01-24 23:52:20 -06:00
Cal Corum
325f1e8af5 Refactor turn action tracking from booleans to counters for RulesConfig support
Replace hardcoded boolean flags with integer counters to support configurable
per-turn limits from RulesConfig. This enables custom game modes with different
rules (e.g., 2 energy attachments per turn, unlimited items, etc.).

PlayerState changes:
- energy_attached_this_turn -> energy_attachments_this_turn (int)
- supporter_played_this_turn -> supporters_played_this_turn (int)
- stadium_played_this_turn -> stadiums_played_this_turn (int)
- retreated_this_turn -> retreats_this_turn (int)
- Added items_played_this_turn (int)
- Added can_play_stadium() and can_play_item() methods
- Renamed reset_turn_flags() to reset_turn_state()

Ability/CardInstance changes:
- Ability.once_per_turn (bool) -> uses_per_turn (int|None)
- CardInstance.ability_used_this_turn -> ability_uses_this_turn (int)
- Added CardInstance.can_use_ability(ability) method

All methods now properly compare counters against RulesConfig or Ability limits.
270 tests passing.
2026-01-24 23:16:37 -06:00
Cal Corum
32541af682 Add card/action models with stage/variant separation
- Add CardDefinition and CardInstance models for card templates and in-game state
- Add Attack, Ability, and WeaknessResistance models for Pokemon card components
- Add 11 action types as discriminated union (PlayPokemon, Evolve, Attack, etc.)
- Split PokemonStage (BASIC, STAGE_1, STAGE_2) from PokemonVariant (NORMAL, EX, GX, V, VMAX, VSTAR)
- Stage determines evolution mechanics, variant determines knockout points
- Update PrizeConfig to use variant for knockout point calculation
- VSTAR and VMAX both worth 3 points; EX, GX, V worth 2 points; NORMAL worth 1 point

Tests: 204 passing, all linting clean
2026-01-24 22:35:31 -06:00