Commit Graph

124 Commits

Author SHA1 Message Date
Cal Corum
55e02ceb21 Replace silent error hiding with explicit failures
Three changes to fail fast instead of silently degrading:

1. GameService.create_game: Raise GameCreationError when energy card
   definition not found instead of logging warning and continuing.
   A deck with missing energy cards is fundamentally broken.

2. CardService.load_all: Collect all card file load failures and raise
   CardServiceLoadError at end with comprehensive error report. Prevents
   startup with partial card data that causes cryptic runtime errors.
   New exceptions: CardLoadError, CardServiceLoadError

3. GameStateManager.recover_active_games: Return RecoveryResult dataclass
   with recovered count, failed game IDs with error messages, and total.
   Enables proper monitoring and alerting for corrupted game state.

Tests added for energy card error case. Existing tests updated for
new RecoveryResult return type.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 18:48:06 -06:00
Cal Corum
cd3cc892f4 Implement GameService.execute_action enhancements (GS-003)
Add forced action handling, turn boundary detection, and DB persistence:
- Check for pending forced actions before allowing regular actions
- Only specified player can act during forced action (except resign)
- Only specified action type allowed during forced action
- Detect turn boundaries (turn number OR current player change)
- Persist to Postgres at turn boundaries for durability
- Include pending_forced_action in GameActionResult for client

New exceptions: ForcedActionRequiredError

Tests: 11 new tests covering forced actions, turn boundaries, and
pending action reporting. Total 47 tests for GameService.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 15:15:34 -06:00
Cal Corum
3c75ee0e00 Implement GameService.create_game (GS-002)
- Add create_game method that loads decks via DeckService, converts
  CardDefinitions to CardInstances, and persists to Redis/Postgres
- Build card registry from only the cards in play (not all cards)
- Add GameCreationError exception and GameCreateResult dataclass
- Add creation_engine_factory for DI-based testing (no monkey patching)
- Add helper methods: _cards_to_instances, _build_card_registry
- Update tests with proper mocks for success, deck failure, engine failure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 14:09:11 -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
f512c7b2b3 Refactor to dependency injection pattern - no monkey patching
- ConnectionManager: Add redis_factory constructor parameter
- GameService: Add engine_factory constructor parameter
- AuthHandler: New class replacing standalone functions with
  token_verifier and conn_manager injection
- Update all tests to use constructor DI instead of patch()
- Update CLAUDE.md with factory injection patterns
- Update services README with new patterns
- Add socketio README documenting AuthHandler and events

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:54:57 -06:00
Cal Corum
0c810e5b30 Add Phase 4 WebSocket infrastructure (WS-001 through GS-001)
WebSocket Message Schemas (WS-002):
- Add Pydantic models for all client/server WebSocket messages
- Implement discriminated unions for message type parsing
- Include JoinGame, Action, Resign, Heartbeat client messages
- Include GameState, ActionResult, Error, TurnStart server messages

Connection Manager (WS-003):
- Add Redis-backed WebSocket connection tracking
- Implement user-to-sid mapping with TTL management
- Support game room association and opponent lookup
- Add heartbeat tracking for connection health

Socket.IO Authentication (WS-004):
- Add JWT-based authentication middleware
- Support token extraction from multiple formats
- Implement session setup with ConnectionManager integration
- Add require_auth helper for event handlers

Socket.IO Server Setup (WS-001):
- Configure AsyncServer with ASGI mode
- Register /game namespace with event handlers
- Integrate with FastAPI via ASGIApp wrapper
- Configure CORS from application settings

Game Service (GS-001):
- Add stateless GameService for game lifecycle orchestration
- Create engine per-operation using rules from GameState
- Implement action-based RNG seeding for deterministic replay
- Add rng_seed field to GameState for replay support

Architecture verified:
- Core module independence (no forbidden imports)
- Config from request pattern (rules in GameState)
- Dependency injection (constructor deps, method config)
- All 1090 tests passing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 22:21:20 -06:00
Cal Corum
c00ee87f25 Switch to testcontainers for automatic test container management
- Create tests/conftest.py with testcontainers for Postgres and Redis
- Auto-detect Docker Desktop socket and disable Ryuk for compatibility
- Update tests/db/conftest.py and tests/services/conftest.py to use shared fixtures
- Fix test_resolve_effect_logs_exceptions: logger was disabled by pytest
- Fix test_save_and_load_with_real_redis: use redis_url fixture
- Minor lint fix in engine_validation.py

Tests now auto-start containers on run - no need for `docker compose up`
All 1199 tests passing.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 16:49:11 -06:00
Cal Corum
0a7c35c262 Add detailed Phase 4 (Game Service + WebSocket) project plan
18 tasks covering:
- WebSocket setup with python-socketio
- GameService lifecycle (create, join, execute, resume, end)
- Message protocol specification
- Connection management with Redis
- Turn timeout system
- Reconnection handling
- REST endpoints for game management
- Unit and integration tests
- Spectator mode (stretch goal)

Includes architecture decisions, sequence diagrams, and acceptance criteria.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 15:50:57 -06:00
Cal Corum
9e14ab906f Add services layer README documentation
Documents:
- Architecture principles (stateless, DI, repository protocol)
- Services overview table
- Key patterns (config from request, UNSET sentinel, repository injection)
- Service details with usage examples
- Testing approach with examples

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 15:40:56 -06:00
Cal Corum
ebe776d54d Update project plans and documentation for Phase 3 completion
Project plan updates:
- Mark all 14 Phase 3 tasks as completed
- Update acceptance criteria to met
- Update master plan status to Phase 4 next
- Add detailed deliverables list for Phase 3

Documentation updates:
- Add UNSET sentinel pattern to CLAUDE.md
- Document when to use UNSET vs None for nullable fields

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 15:26:01 -06:00
Cal Corum
7d397a2e22 Fix medium priority issues from code review
UNSET sentinel pattern:
- Add UNSET sentinel in protocols.py for nullable field updates
- Fix inability to clear deck description (UNSET=keep, None=clear)
- Fix repository inability to clear validation_errors

Starter deck improvements:
- Remove unused has_starter_deck from CollectionService
- Add deprecation notes to old starter deck methods

Validation improvements:
- Add energy type validation in deck_validator.py
- Add energy type validation in deck schemas
- Add VALID_ENERGY_TYPES constant

Game loading fix:
- Fix get_deck_for_game silently skipping invalid cards
- Now raises ValueError with clear error message

Tests:
- Add TestEnergyTypeValidation test class
- Add TestGetDeckForGame test class
- Add tests for validate_energy_types utility function

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 14:32:08 -06:00
Cal Corum
3ec670753b Fix security and validation issues from code review
Critical fixes:
- Add admin API key authentication for admin endpoints
- Add race condition protection via unique partial index for starter decks
- Make starter deck selection atomic with combined method

Moderate fixes:
- Fix DI pattern violation in validate_deck_endpoint
- Add card ID format validation (regex pattern)
- Add card quantity validation (1-99 range)
- Fix exception chaining with from None (B904)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 14:16:07 -06:00
Cal Corum
58349c126a Phase 3: Collections + Decks - Services and DI architecture
Implemented with Repository Protocol pattern for offline fork support:
- CollectionService with PostgresCollectionRepository
- DeckService with PostgresDeckRepository
- DeckValidator with DeckConfig + CardService injection
- Starter deck definitions (5 types: grass, fire, water, psychic, lightning)
- Pydantic schemas for collection and deck APIs
- Unit tests for DeckValidator (32 tests passing)

Architecture follows pure dependency injection - no service locator patterns.
Added CLAUDE.md documenting DI requirements and patterns.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 11:27:14 -06:00
Cal Corum
4859b2a9cb Add detailed Phase 3 (Collections + Decks) project plan
14 tasks covering card ownership, deck building, validation, and
starter deck selection. 29 estimated hours total.

Key components:
- CollectionService for card ownership CRUD
- DeckService with slot limits and validation
- DeckValidator for rule enforcement
- 5 starter deck definitions
- REST API endpoints for collections and decks
- ~80-90 tests planned

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 00:40:25 -06:00
Cal Corum
f82bc8aa1f Fix OAuth callback redirect and deps import
- Discord OAuth callback now redirects to frontend with tokens in URL fragment
  (more secure than query params - fragment not sent to server)
- Error responses also redirect with error in query params
- Fix deps.py import: get_session_dependency -> get_session

Part of Phase 2 Authentication completion.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 00:17:27 -06:00
Cal Corum
5cf2198542 Add engine validation script with attack_coin_status effect handler
- Add attack_coin_status effect handler for coin-flip status conditions
  (e.g., Thunder Shock paralysis on heads)
- Create comprehensive engine_validation.py script (~1250 lines) that
  validates game engine behavior with 29 test cases:
  - Illegal moves (attack without energy, wrong turn, evolution rules)
  - Energy mechanics (attachment limits, cost validation)
  - Weakness calculation (+20 additive mode)
  - Status conditions (paralysis blocks actions, poison damage)
  - Knockout flow (points, forced actions, state cleanup)
  - Win conditions (4 points triggers game over)
- Update game_walkthrough.py Thunder Shock to use new effect handler
- Interactive prompts between sections (Enter to continue, q to quit)
- Uses seed=42 for deterministic, reproducible coin flips

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 00:15:12 -06:00
Cal Corum
4cdb544162 Merge branch 'backend-phase2' - Complete Phase 2 Authentication 2026-01-27 22:08:42 -06:00
Cal Corum
3ad79a4860 Fix OAuth absolute URLs and add account linking endpoints
- Add base_url config setting for OAuth callback URLs
- Change OAuth callbacks from relative to absolute URLs
- Add account linking OAuth flow (GET /auth/link/{provider})
- Add unlink endpoint (DELETE /users/me/link/{provider})
- Add AccountLinkingError and service methods for linking
- Add 14 new tests for linking functionality
- Update Phase 2 plan to mark complete (1072 tests passing)
2026-01-27 22:06:22 -06:00
Cal Corum
996c43fbd9 Implement Phase 2: Authentication system
Complete OAuth-based authentication with JWT session management:

Core Services:
- JWT service for access/refresh token creation and verification
- Token store with Redis-backed refresh token revocation
- User service for CRUD operations and OAuth-based creation
- Google and Discord OAuth services with full flow support

API Endpoints:
- GET /api/auth/{google,discord} - Start OAuth flows
- GET /api/auth/{google,discord}/callback - Handle OAuth callbacks
- POST /api/auth/refresh - Exchange refresh token for new access token
- POST /api/auth/logout - Revoke single refresh token
- POST /api/auth/logout-all - Revoke all user sessions
- GET/PATCH /api/users/me - User profile management
- GET /api/users/me/linked-accounts - List OAuth providers
- GET /api/users/me/sessions - Count active sessions

Infrastructure:
- Pydantic schemas for auth/user request/response models
- FastAPI dependencies (get_current_user, get_current_premium_user)
- OAuthLinkedAccount model for multi-provider support
- Alembic migration for oauth_linked_accounts table

Dependencies added: email-validator, fakeredis (dev), respx (dev)

84 new tests, 1058 total passing
2026-01-27 21:49:59 -06:00
Cal Corum
4ddc9b8c30 Add detailed Phase 2 (Authentication) project plan
Defines 15 tasks covering OAuth login (Google/Discord), JWT session
management, user services, and API endpoints for player authentication
at play.mantimon.com.

Key components:
- JWT utilities with access/refresh token pattern
- Redis-backed refresh token storage for revocation
- Google and Discord OAuth services
- FastAPI auth dependencies (get_current_user, etc.)
- Account linking support (multiple OAuth providers per user)
- Premium subscription tracking

Estimated: 24 hours across 1-2 weeks
2026-01-27 16:25:57 -06:00
Cal Corum
b78236ac49 Simplify card image URLs to use set-based paths
- Derive image_path from card ID instead of raw data image_file field
- Use simplified CDN paths: /<set>/<card>.webp (e.g., a1/033-charmander.webp)
- Energy cards use basic/<type>.webp paths
- Fix undefined variable bug in transform_trainer_card
- Update tests to match new path format
- Regenerate all 382 card definitions with correct image_url fields
2026-01-27 16:16:51 -06:00
Cal Corum
e237a0deb6 Add image_url to card definitions from source data
The converter now falls back to using the source image_url from raw
data when image_file is not available (images not downloaded locally).

- Pokemon cards: use source image_url when image_file is null
- Trainer cards: same fallback behavior
- Regenerated all 382 card definitions with image URLs

Images point to pokemon-zone.com assets for now. When we host our
own images, cards with image_file will use our CDN instead.
2026-01-27 15:55:10 -06:00
Cal Corum
b95453569b Update Phase 1 plan with gap fixes and Phase 2 prerequisites 2026-01-27 15:38:49 -06:00
Cal Corum
2a95316f04 Add FastAPI lifespan hooks and fix Phase 1 gaps
- Add lifespan context manager to app/main.py with startup/shutdown hooks
- Wire startup: init_db(), init_redis(), CardService.load_all()
- Wire shutdown: close_db(), close_redis()
- Add /health/ready endpoint for readiness checks
- Add CORS middleware with configurable origins
- Disable docs in production (only available in dev)

- Export get_session_dependency from app/db/__init__.py for FastAPI DI
- Add game_cache_ttl_seconds to Settings (configurable, was hardcoded)
- Fix datetime.utcnow() deprecation (4 occurrences) -> datetime.now(UTC)
- Update test to match S3 image URL (was placeholder CDN)

All 974 tests passing.
2026-01-27 15:37:19 -06:00
Cal Corum
c3c0a310a7 Mark Phase 1 Database complete - all 18 tasks done 2026-01-27 15:22:43 -06:00
Cal Corum
adb55dec12 Fix scraper to preserve energy types in effect text
The source website uses <span class='energy-text energy-text--type-fire'>
to render inline energy icons. BeautifulSoup's get_text() was stripping
these spans, losing the energy type information and causing merged text
like 'Discard aEnergy' instead of 'Discard a Fire Energy'.

Changes:
- Add ENERGY_TEXT_TYPES mapping for inline energy references
- Add replace_energy_text_spans() to convert spans to text before extraction
- Add extract_effect_text() helper with proper text joining (separator=' ')
- Update parse_attack(), parse_ability(), _parse_trainer_details() to use it
- Fix JSON encoding in convert_cards.py to use UTF-8 (ensure_ascii=False)

Before: 'Discard an Energy from this Pokémon'
After:  'Discard a Fire Energy from this Pokémon'

Re-scraped all 372 cards and regenerated 382 definitions.
2026-01-27 15:10:02 -06:00
Cal Corum
c6e3695760 Fix card data pipeline: fossil cards and text artifacts
Scraper fixes:
- Detect fossil cards (Helix/Dome Fossil, Old Amber) as Trainer/Item cards
- Add text artifact cleaning for stripped energy icons:
  - 'aEnergy' -> 'an Energy'
  - 'extraEnergy' -> 'extra Energy'
  - 'BenchedPokémon' -> 'Benched Pokémon'
  - And 20+ other common patterns

Converter improvements:
- Add evolution chain validation to detect broken evolves_from references
- Track conversion errors and validation warnings in _index.json
- Return errors from convert_set() for better debugging

Data fixes:
- Fixed 4 fossil cards (now correctly typed as trainer/item)
- Fixed text artifacts in 46 raw card files
- Regenerated all 382 card definitions
- All evolution chains now valid

Added fix_raw_text.py utility script for batch text cleanup.
2026-01-27 14:37:03 -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
29ab0b3d84 Add GameStateManager service with Redis/Postgres dual storage
- Implement GameStateManager with Redis-primary, Postgres-backup pattern
- Cache operations: save_to_cache, load_from_cache, delete_from_cache
- DB operations: persist_to_db, load_from_db, delete_from_db
- High-level: load_state (cache-first), delete_game, recover_active_games
- Query helpers: get_active_game_count, get_player_active_games
- Add 22 tests for GameStateManager (87% coverage)
- Add 6 __repr__ tests for all DB models (100% model coverage)
2026-01-27 10:59:58 -06:00
Cal Corum
50684a1b11 Add database infrastructure with SQLAlchemy models and test suite
Phase 1 Database Implementation (DB-001 through DB-012):

Models:
- User: OAuth support (Google/Discord), premium subscriptions
- Collection: Card ownership with CardSource enum
- Deck: JSONB cards/energy_cards, validation state
- CampaignProgress: One-to-one with User, medals/NPCs as JSONB
- ActiveGame: In-progress games with GameType enum
- GameHistory: Completed games with EndReason enum, replay data

Infrastructure:
- Alembic migrations with sync psycopg2 (avoids async issues)
- Docker Compose for Postgres (5433) and Redis (6380)
- App config with Pydantic settings
- Redis client helper

Test Infrastructure:
- 68 database tests (47 model + 21 relationship)
- Async factory pattern for test data creation
- Sync TRUNCATE cleanup (solves pytest-asyncio event loop mismatch)
- Uses dev containers instead of testcontainers for reliability

Key technical decisions:
- passive_deletes=True for ON DELETE SET NULL relationships
- NullPool for test sessions (no connection reuse)
- expire_on_commit=False with manual expire() for relationship tests
2026-01-27 10:17:30 -06:00
Cal Corum
e82b204849 Complete card scrape for Genetic Apex (A1) and Mythical Island (A1a)
- Fix is_ex detection to only match ' ex' suffix (fixes Exeggutor false positive)
- Fix trainer card detection with specific 'Trainer | Type' pattern
- Improve trainer effect text extraction from content body
- Scrape full A1 set: 286 cards
- Scrape full A1a set: 86 cards
- Total: 372 cards with images (some images failed due to rate limiting)
- Re-scraped affected cards to fix is_ex and trainer type issues
2026-01-26 23:21:08 -06:00
Cal Corum
5e1229aa7c Add image download support to card scraper
- Add --images flag to download images during scraping
- Add --download-images command to fetch images for existing card data
- Images saved to data/images/{set}/ directory
- Card JSON updated with image_file field (relative path)
- Uses Referer header for asset server compatibility
- Supports skip-if-exists for incremental downloads
2026-01-26 22:55:13 -06:00
Cal Corum
2517d241ac Add Pokemon Pocket card scraper for offline card data
- Add scrape_pokemon_pocket.py script to fetch card data from pokemon-zone.com
- Scrapes Pokemon, Trainer, and Energy cards with full metadata
- Includes image URLs for offline caching support
- Supports --set, --card, --limit, and --reindex CLI options
- Add beautifulsoup4 and requests as dev dependencies
- Create data/cards/ directory structure for card JSON files
2026-01-26 22:52:20 -06:00
Cal Corum
3ed67ea16b Add Active Effects system design and module README files
Design Documentation:
- docs/ACTIVE_EFFECTS_DESIGN.md: Comprehensive design for persistent effect system
  - Data model (ActiveEffect, EffectTrigger, EffectScope, StackingMode)
  - Core operations (register, remove, query effects)
  - Integration points (damage calc, energy counting, retreat, lifecycle)
  - Effect categories from Pokemon Pocket card research (~372 cards)
  - Example implementations (Serperior, Greninja, Mr. Mime, Victreebel)
  - Post-launch TODO for generic modifier system

Module README Files:
- backend/app/core/README.md: Core engine overview and key classes
- backend/app/core/effects/README.md: Effects module index and quick reference
- backend/app/core/models/README.md: Models module with relationship diagram

Minor cleanup:
- Revert Bulbasaur weakness to Fire (was test change for Lightning)
- Clean up debug output in game walkthrough
2026-01-26 22:39:02 -06:00
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
9564916c87 Fix async handling in game walkthrough script
- Add await to engine.execute_action() calls (energy attach and attack)
- Update entry point to use asyncio.run()
- Fix game creation result handling (use game_creation.game)
- Fix AttachEnergyAction parameter name (energy_card_id)
2026-01-26 15:03:57 -06:00
Cal Corum
cd8226c4e6 Complete LOW-001: Verify comprehensive docstring coverage
Audit of all 14 core module files confirms they already have
comprehensive Google-style documentation:
- Module docstrings with usage examples
- Class docstrings with Attributes sections
- Method/function docstrings with Args, Returns, Raises

Files verified:
- enums.py, config.py, rng.py
- models/card.py, models/actions.py, models/game_state.py
- effects/base.py, effects/registry.py, effects/handlers.py
- rules_validator.py, turn_manager.py, win_conditions.py
- visibility.py, engine.py

PROJECT_PLAN.json: 32/32 tasks complete - game engine scaffolding done!
2026-01-26 14:55:03 -06:00
Cal Corum
2252931fb8 Add core module AGENTS.md documentation (DOCS-001)
Creates comprehensive documentation for AI agents working with app/core/:
- Architecture overview with component diagram
- Import patterns (correct enum imports from app.core.enums)
- Core patterns: game creation, action execution, effects, config
- Key classes: GameState, PlayerState, CardDefinition, CardInstance, Zone
- Testing patterns with SeededRandom for determinism
- Security rules for hidden information
- Module independence guidelines for offline fork support
- File organization and quick commands

Updates PROJECT_PLAN.json: 31/32 tasks complete.
2026-01-26 14:50:52 -06:00
Cal Corum
f807a4a940 Add interactive game walkthrough script for engine demonstration
Creates a comprehensive interactive demo that walks through:
- Card definition creation
- Rules configuration
- Deck building
- Game initialization via GameEngine
- Setup phase with Basic Pokemon placement
- Full turn cycle (draw, main, attack, end phases)

Uses colored terminal output and 'press Enter' prompts for
step-by-step exploration of the core game engine.
2026-01-26 14:48:49 -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
c3623d9541 Consolidate CLAUDE.md into AGENTS.md
Merged unique content from CLAUDE.md into AGENTS.md:
- Project Overview section (campaign mode description)
- Full Tech Stack tables (frontend and backend)
- WebSocket Events pattern
- Game Engine Patterns (Card Effect System, Turn State Machine)

Removed CLAUDE.md to standardize on AGENTS.md for agent guidelines.
2026-01-26 14:21:29 -06:00
Cal Corum
11e244d7e9 Move legacy docs to project root docs/legacy
Relocated SYSTEM_REVIEW.md and PROJECT_PLAN_ENERGY_EVOLUTION.md from
backend/docs/legacy to project-level docs/legacy. Added docs/README.md
indexing all documentation including ARCHITECTURE.md and GAME_RULES.md.
2026-01-26 14:19:19 -06:00
Cal Corum
6ce625a747 Move completed review docs to docs/legacy
Relocated SYSTEM_REVIEW.md and PROJECT_PLAN_ENERGY_EVOLUTION.md to
docs/legacy/ now that all issues are resolved. Added docs/README.md
with index of documentation files.
2026-01-26 14:18:29 -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
939ae421aa Add exception logging to effect registry (Issue #14)
Effect handler exceptions now logged at ERROR level with full context:
- effect_id, source_player_id, source/target card IDs, params
- Full traceback via logger.exception()

Game still returns safe EffectResult.failure() to prevent crashes,
but debugging information is now preserved in logs.
2026-01-26 13:32:43 -06:00
Cal Corum
1fbd3d1cfa Add knockout detection to damage effect handlers (Issue #13)
Both deal_damage and attack_damage now check if the target is knocked out
after applying damage. If KO'd, EffectResult includes:
- details['knockout'] = True
- details['knockout_pokemon_id'] = target's instance_id
- Message includes 'knocked out!' notification

Knockout check correctly respects HP modifiers via effective_hp().

Added 9 tests covering knockout detection, HP modifier behavior,
weakness-triggered knockouts, and resistance preventing knockouts.
2026-01-26 11:44:38 -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