Commit Graph

10 Commits

Author SHA1 Message Date
Cal Corum
72bd1102df Add weakness/resistance support to attack damage calculation
- Add DamageCalculationResult model for transparent damage breakdown
- Implement _calculate_attack_damage with W/R modifiers (additive/multiplicative)
- Add _execute_attack_effect for future effect system integration
- Add _build_attack_message for detailed damage breakdown in messages
- Update _execute_attack to use new calculation pipeline
- Bulbasaur now properly weak to Lightning in walkthrough demo

New features:
- Weakness applies bonus damage (additive +X or multiplicative xN)
- Resistance reduces damage (minimum 0)
- State changes include weakness/resistance details for UI
- Messages show damage breakdown (e.g. 'base 10 +20 weakness')

Tests: 7 new tests covering additive/multiplicative W/R, type matching,
minimum damage floor, knockout triggers, and state change details
2026-01-26 16:04:41 -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
5f1eb11344 Add test coverage for validation and confusion, document effect knockout handling
Issue #2 gap: Added 14 CardDefinition validation tests covering all
required field checks (hp, stage, pokemon_type, evolves_from, trainer_type,
energy_type) with both negative and positive test cases.

Issue #7 gap: Added 4 confusion attack engine tests covering heads/tails
outcomes, self-damage, self-KO with opponent scoring, and configurable
damage from RulesConfig.

Issue #13 documentation: Added TODO comments in engine.py and handlers.py
documenting the expected pattern for knockout detection when effect
execution is implemented. Effect handlers set knockout flags; engine
should process knockouts after all effects resolve.

825 tests passing (+17 new tests)
2026-01-26 14:16:15 -06:00
Cal Corum
554178dc6e Track stadium ownership for correct discard (Issue #12)
Added stadium_owner_id field to GameState to track who played the stadium:

- stadium_owner_id: str | None tracks the player who played the current stadium
- When a stadium is replaced, old stadium discards to OWNER's pile (not current player)
- Added stadium_owner_id to VisibleGameState for client visibility
- Updated existing test and added 2 new tests for stadium ownership

This fixes the bug where replacing an opponent's stadium would discard
to the current player's pile instead of the opponent's.

797 tests passing.
2026-01-26 11:38:38 -06:00
Cal Corum
9432499018 Add forced action queue for double knockouts (Issue #10)
Changed forced_action from single item to FIFO queue to support
scenarios where multiple forced actions are needed simultaneously:

- forced_actions: list[ForcedAction] replaces forced_action: ForcedAction | None
- Added queue management methods:
  - has_forced_action() - check if queue has pending actions
  - get_current_forced_action() - get first action without removing
  - add_forced_action(action) - add to end of queue
  - pop_forced_action() - remove and return first action
  - clear_forced_actions() - clear all pending actions
- Updated engine, turn_manager, rules_validator, and visibility filter
- Added 8 new tests for forced action queue including double knockout scenario

This fixes the bug where simultaneous knockouts (e.g., mutual poison damage)
would lose one player's select_active action due to overwriting.

795 tests passing.
2026-01-26 11:33:47 -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
0534c57430 Add SelectPrizeAction executor and turn limit check (Issues #11, #15)
Issue #11 - SelectPrizeAction:
- Add _execute_select_prize() method to GameEngine
- Add prize card mode support to process_knockout()
- Add _award_prize_cards() helper for random/player-choice selection
- Support multi-prize selection (EX/VMAX worth 2-3 prizes)

Issue #15 - Turn Limit Check:
- Add turn limit check at start of start_turn()
- Add GameEndReason.TURN_LIMIT enum value (distinct from TIMEOUT)
- Game ends when turn limit exceeded, winner determined by score
- Equal scores result in DRAW

Added 12 new tests for prize card mode and turn limit functionality.
788 tests passing.
2026-01-26 11:04:03 -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
fe2e1091f9 Add comprehensive engine tests and fix action field name bugs
Bug fixes in engine.py:
- PlayPokemonAction: card_id -> card_instance_id
- PlayTrainerAction: card_id -> card_instance_id
- UseAbilityAction: pokemon_card_id -> pokemon_id
- SelectActiveAction: card_id -> pokemon_id
- record_ability_use() -> ability_uses_this_turn += 1

Added 26 new tests covering:
- Energy deck setup (Pokemon Pocket style)
- Prize card mode
- Deck size validation (too large)
- PlayPokemonAction (to active, to bench, not found)
- EvolvePokemonAction (success, not in hand, target not found)
- PlayTrainerAction (item, supporter, stadium, replacement)
- UseAbilityAction (success, not found, invalid index)
- SelectActiveAction (forced action, not on bench)
- Deck-out on turn start
- Attack edge cases (no energy, invalid index)
- Retreat without bench
- Attach energy from energy zone

Test count: 711 -> 737 (+26)
Coverage: 89% -> 94% overall, engine.py 55% -> 81%
2026-01-25 13:34:42 -06:00
Cal Corum
3f830b25b7 Add GameEngine orchestrator with full game lifecycle support
Implements the main public API for the core game engine:
- create_game(): deck validation, shuffling, dealing hands
- execute_action(): validates and executes all 11 action types
- start_turn()/end_turn(): turn management integration
- get_visible_state(): hidden info filtering for clients
- handle_timeout(): timeout handling for turn limits

Integrates turn_manager, rules_validator, win_conditions, and
visibility filter into a cohesive orchestration layer.

22 integration tests covering game creation, action execution,
visibility filtering, and error handling.

711 tests passing (29/32 tasks complete)
2026-01-25 13:21:41 -06:00