Commit Graph

11 Commits

Author SHA1 Message Date
Cal Corum
1123d61067 Extend core card models with rarity tiers and card subtypes
Add CardRarityTier enum for pull rate calculations (common through
crown_rare). Add CardSubtype enum for Pokemon classifications (basic,
stage1, stage2, ex, etc.). Update CardDefinition model with new fields
for subtypes and rarity display.
2026-01-31 15:43:07 -06:00
Cal Corum
f93f5b617a Remove legacy modifier field from WeaknessResistance
Migrate all usages to the proper mode/value fields:
- Weakness: mode=MULTIPLICATIVE, value=2
- Resistance: mode=ADDITIVE, value=-30

Remove backwards compatibility code and legacy test.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 13:16:51 -06:00
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