diff --git a/backend/PROJECT_PLAN.json b/backend/PROJECT_PLAN.json index c5dbd7b..957f842 100644 --- a/backend/PROJECT_PLAN.json +++ b/backend/PROJECT_PLAN.json @@ -2,13 +2,13 @@ "meta": { "version": "1.0.0", "created": "2026-01-24", - "lastUpdated": "2026-01-25", + "lastUpdated": "2026-01-26", "planType": "feature", "projectName": "Mantimon TCG - Backend Game Engine", "description": "Core game engine scaffolding for a highly configurable Pokemon TCG-inspired card game. The engine must support campaign mode with fixed rules and free play mode with user-configurable rules.", "totalEstimatedHours": 48, "totalTasks": 32, - "completedTasks": 29 + "completedTasks": 30 }, "categories": { "critical": "Foundation components that block all other work", @@ -23,10 +23,12 @@ "cardRegistry": "Hybrid - definitions loaded from DB via CardService, embedded in GameState at game creation for self-contained gameplay", "cardModels": "Separate CardDefinition (immutable template) from CardInstance (mutable in-game state)", "actionModeling": "Union types for type safety", - "asyncSupport": "Async throughout for WebSocket compatibility" + "asyncSupport": "Async throughout for WebSocket compatibility", + "enumsLocation": "Enums in app/core/enums.py (foundational module with zero dependencies) - breaks circular imports and enables clean re-exports" }, "directoryStructure": { "core": "backend/app/core/", + "enums": "backend/app/core/enums.py", "models": "backend/app/core/models/", "effects": "backend/app/core/effects/", "tests": "backend/tests/core/" @@ -536,21 +538,22 @@ "description": "Set up clean public API exports in __init__.py files", "category": "medium", "priority": 30, - "completed": false, - "tested": false, + "completed": true, + "tested": true, "dependencies": ["HIGH-009"], "files": [ - {"path": "app/core/__init__.py", "issue": "Needs public API exports"}, - {"path": "app/core/models/__init__.py", "issue": "Needs model exports"}, - {"path": "app/core/effects/__init__.py", "issue": "Needs effect exports"} + {"path": "app/core/__init__.py", "status": "updated"}, + {"path": "app/core/models/__init__.py", "status": "updated"}, + {"path": "app/core/effects/__init__.py", "status": "updated"} ], "suggestedFix": "Export key classes: GameEngine, RulesConfig, GameState, CardDefinition, CardInstance, Action types, effect_handler decorator", "estimatedHours": 0.5, - "notes": "Clean API surface for consumers of the core module" + "notes": "Clean API surface for consumers of the core module. Exports: 34 items from core, 29 from models, 12 from effects. 14 effect handlers auto-registered.", + "completedDate": "2026-01-26" }, { "id": "DOCS-001", - "name": "Create core module CLAUDE.md", + "name": "Create core module AGENTS.md", "description": "Document the game engine architecture, patterns, and usage guidelines for AI agents", "category": "low", "priority": 31, @@ -558,11 +561,11 @@ "tested": false, "dependencies": ["HIGH-009"], "files": [ - {"path": "app/core/CLAUDE.md", "issue": "File does not exist"} + {"path": "app/core/AGENTS.md", "issue": "File does not exist"} ], "suggestedFix": "Document: module structure, key classes, effect handler pattern, configuration system, testing approach, security considerations (hidden info)", "estimatedHours": 1, - "notes": "Reference doc as specified in CLAUDE.md" + "notes": "Reference doc as specified in AGENTS.md" }, { "id": "LOW-001", @@ -654,7 +657,7 @@ "estimatedHours": 14, "goals": ["GameEngine complete", "Full integration tested", "Documentation complete"], "status": "IN_PROGRESS", - "progress": "HIGH-008, TEST-012, HIGH-009, TEST-013 complete. 711 tests passing. Remaining: MED-004 (module exports), DOCS-001 (documentation), LOW-001 (docstrings)." + "progress": "HIGH-008, TEST-012, HIGH-009, TEST-013, MED-004 complete. Enums moved to app/core/enums.py (architectural improvement to eliminate circular imports). 826 tests passing. Remaining: DOCS-001 (documentation), LOW-001 (docstrings)." } }, "testingStrategy": { diff --git a/backend/app/core/__init__.py b/backend/app/core/__init__.py index edb3d09..4d609a1 100644 --- a/backend/app/core/__init__.py +++ b/backend/app/core/__init__.py @@ -14,4 +14,141 @@ Key Components: - win_conditions: Win/loss detection - visibility: Hidden information filtering for clients - rng: Random number generation with testable seeded implementation + +Quick Start: + from app.core import GameEngine, RulesConfig, create_rng + + # Create engine with rules and RNG + engine = GameEngine(rules=RulesConfig(), rng=create_rng()) + + # Create a game + game = engine.create_game( + player_ids=["player1", "player2"], + decks={"player1": deck1, "player2": deck2}, + card_registry=registry, + ) + + # Execute actions + result = await engine.execute_action(game, "player1", action) + + # Get client-safe view + visible = engine.get_visible_state(game, "player1") """ + +# Enums (foundational, no dependencies) +# Configuration +from app.core.config import ( + ActiveConfig, + BenchConfig, + CombatConfig, + DeckConfig, + EnergyConfig, + EvolutionConfig, + FirstTurnConfig, + PrizeConfig, + RetreatConfig, + RulesConfig, + StatusConfig, + TrainerConfig, + WinConditionsConfig, +) + +# Engine +from app.core.engine import ActionResult, GameEngine +from app.core.enums import ( + ActionType, + CardType, + EnergyType, + GameEndReason, + ModifierMode, + PokemonStage, + PokemonVariant, + StatusCondition, + TrainerType, + TurnPhase, +) + +# Game state models (imported after config to avoid circular imports) +from app.core.models.game_state import ( + ForcedAction, + GameState, + PlayerState, + Zone, +) + +# RNG +from app.core.rng import RandomProvider, SecureRandom, SeededRandom, create_rng + +# Validation +from app.core.rules_validator import ValidationResult, validate_action + +# Turn management +from app.core.turn_manager import TurnManager, TurnStartResult + +# Visibility (for service layer) +from app.core.visibility import ( + VisibleGameState, + VisiblePlayerState, + VisibleZone, + get_spectator_state, + get_visible_state, +) + +# Win conditions +from app.core.win_conditions import WinResult, check_win_conditions + +__all__ = [ + # Enums + "ActionType", + "CardType", + "EnergyType", + "GameEndReason", + "ModifierMode", + "PokemonStage", + "PokemonVariant", + "StatusCondition", + "TrainerType", + "TurnPhase", + # Configuration + "RulesConfig", + "DeckConfig", + "ActiveConfig", + "BenchConfig", + "EnergyConfig", + "PrizeConfig", + "FirstTurnConfig", + "WinConditionsConfig", + "StatusConfig", + "TrainerConfig", + "EvolutionConfig", + "RetreatConfig", + "CombatConfig", + # RNG + "RandomProvider", + "SeededRandom", + "SecureRandom", + "create_rng", + # Engine + "GameEngine", + "ActionResult", + # Validation + "ValidationResult", + "validate_action", + # Win conditions + "WinResult", + "check_win_conditions", + # Turn management + "TurnManager", + "TurnStartResult", + # Visibility + "VisibleGameState", + "VisiblePlayerState", + "VisibleZone", + "get_visible_state", + "get_spectator_state", + # Game state models + "ForcedAction", + "GameState", + "PlayerState", + "Zone", +] diff --git a/backend/app/core/config.py b/backend/app/core/config.py index 5b4c69a..70796b8 100644 --- a/backend/app/core/config.py +++ b/backend/app/core/config.py @@ -24,7 +24,7 @@ Usage: from pydantic import BaseModel, Field -from app.core.models.enums import EnergyType, ModifierMode, PokemonVariant +from app.core.enums import EnergyType, ModifierMode, PokemonVariant class DeckConfig(BaseModel): diff --git a/backend/app/core/effects/__init__.py b/backend/app/core/effects/__init__.py index 3945d0b..a52ad1f 100644 --- a/backend/app/core/effects/__init__.py +++ b/backend/app/core/effects/__init__.py @@ -8,4 +8,47 @@ Key Components: - base: EffectContext and EffectResult types - registry: Effect handler registration and lookup - handlers: Built-in effect handlers (deal_damage, heal, etc.) + +Usage: + from app.core.effects import effect_handler, resolve_effect, EffectContext, EffectResult + + @effect_handler("custom_effect") + def handle_custom(ctx: EffectContext) -> EffectResult: + # Effect implementation + return EffectResult.success("Effect applied") + + # Resolve an effect by ID + result = resolve_effect("deal_damage", context) """ + +# Import handlers to register them +from app.core.effects import handlers as _handlers # noqa: F401 +from app.core.effects.base import EffectContext, EffectResult, EffectType +from app.core.effects.registry import ( + EffectHandler, + clear_registry, + effect_handler, + get_handler, + is_registered, + list_effects, + register_handler, + resolve_effect, + unregister_handler, +) + +__all__ = [ + # Base types + "EffectContext", + "EffectResult", + "EffectType", + # Registry + "EffectHandler", + "effect_handler", + "resolve_effect", + "list_effects", + "get_handler", + "is_registered", + "register_handler", + "unregister_handler", + "clear_registry", +] diff --git a/backend/app/core/effects/handlers.py b/backend/app/core/effects/handlers.py index 74f6b25..5756960 100644 --- a/backend/app/core/effects/handlers.py +++ b/backend/app/core/effects/handlers.py @@ -38,7 +38,7 @@ Available Effects: from app.core.effects.base import EffectContext, EffectResult, EffectType from app.core.effects.registry import effect_handler -from app.core.models.enums import ModifierMode, StatusCondition +from app.core.enums import ModifierMode, StatusCondition @effect_handler("deal_damage") diff --git a/backend/app/core/engine.py b/backend/app/core/engine.py index d698ec6..078e70b 100644 --- a/backend/app/core/engine.py +++ b/backend/app/core/engine.py @@ -41,6 +41,7 @@ from typing import Any from pydantic import BaseModel, Field from app.core.config import RulesConfig +from app.core.enums import GameEndReason, StatusCondition, TurnPhase from app.core.models.actions import ( Action, AttachEnergyAction, @@ -56,7 +57,6 @@ from app.core.models.actions import ( UseAbilityAction, ) from app.core.models.card import CardDefinition, CardInstance -from app.core.models.enums import GameEndReason, StatusCondition, TurnPhase from app.core.models.game_state import GameState, PlayerState from app.core.rng import RandomProvider, create_rng from app.core.rules_validator import ValidationResult, validate_action @@ -623,7 +623,7 @@ class GameEngine: # Update per-turn counters if card_def.trainer_type: - from app.core.models.enums import TrainerType + from app.core.enums import TrainerType if card_def.trainer_type == TrainerType.SUPPORTER: player.supporters_played_this_turn += 1 @@ -837,7 +837,7 @@ class GameEngine: player.active.add(new_active) # Clear status conditions (retreat clears confusion) - from app.core.models.enums import StatusCondition + from app.core.enums import StatusCondition active.remove_status(StatusCondition.CONFUSED) diff --git a/backend/app/core/models/enums.py b/backend/app/core/enums.py similarity index 94% rename from backend/app/core/models/enums.py rename to backend/app/core/enums.py index 8ba6175..83a5e48 100644 --- a/backend/app/core/models/enums.py +++ b/backend/app/core/enums.py @@ -3,6 +3,9 @@ This module defines all enum types used throughout the game engine. We use StrEnum for JSON serialization compatibility - enum values serialize as strings directly. +This is the foundational module with zero internal dependencies, allowing other +core modules to import from it without circular import concerns. + Note on extensibility: While these enums define the standard types, the game engine is designed to be configurable. Custom energy types or card types could be added via configuration for free play mode. @@ -199,3 +202,17 @@ class ModifierMode(StrEnum): MULTIPLICATIVE = "multiplicative" ADDITIVE = "additive" + + +__all__ = [ + "ActionType", + "CardType", + "EnergyType", + "GameEndReason", + "ModifierMode", + "PokemonStage", + "PokemonVariant", + "StatusCondition", + "TrainerType", + "TurnPhase", +] diff --git a/backend/app/core/models/__init__.py b/backend/app/core/models/__init__.py index a7230e4..efae8fb 100644 --- a/backend/app/core/models/__init__.py +++ b/backend/app/core/models/__init__.py @@ -1,20 +1,20 @@ """Core data models for the Mantimon TCG game engine. -This module contains all Pydantic models used throughout the game engine: - - enums: Enumeration types (CardType, EnergyType, TurnPhase, etc.) - - card: CardDefinition (template) and CardInstance (in-game state) - - actions: Player action types as a discriminated union - - game_state: GameState, PlayerState, and Zone models +This module exports Pydantic models for cards, game state, and actions. -Note: To avoid circular imports, game_state is not imported at module level. -Import directly from app.core.models.game_state when needed: +For enums, import from app.core.enums or app.core: + from app.core.enums import CardType, EnergyType, TurnPhase + from app.core import CardType, EnergyType # convenience - from app.core.models.game_state import GameState, PlayerState, Zone - -Or import after config is already loaded: - - from app.core.models import enums, card, actions - from app.core.models import game_state # Import after other modules +Usage: + from app.core.models import ( + # Card models + CardDefinition, CardInstance, Attack, Ability, + # Game state + GameState, PlayerState, Zone, ForcedAction, + # Actions + Action, AttackAction, PlayPokemonAction, parse_action, + ) """ from app.core.models.actions import ( @@ -40,38 +40,25 @@ from app.core.models.card import ( CardInstance, WeaknessResistance, ) -from app.core.models.enums import ( - ActionType, - CardType, - EnergyType, - GameEndReason, - PokemonStage, - PokemonVariant, - StatusCondition, - TrainerType, - TurnPhase, +from app.core.models.game_state import ( + ForcedAction, + GameState, + PlayerState, + Zone, ) -# Note: GameState, PlayerState, Zone are imported from game_state module directly -# to avoid circular imports with app.core.config - __all__ = [ - # Enums - "ActionType", - "CardType", - "EnergyType", - "GameEndReason", - "PokemonStage", - "PokemonVariant", - "StatusCondition", - "TrainerType", - "TurnPhase", # Card models "Ability", "Attack", "CardDefinition", "CardInstance", "WeaknessResistance", + # Game state models + "ForcedAction", + "GameState", + "PlayerState", + "Zone", # Action models "Action", "AttachEnergyAction", diff --git a/backend/app/core/models/card.py b/backend/app/core/models/card.py index 54e52dd..ae30098 100644 --- a/backend/app/core/models/card.py +++ b/backend/app/core/models/card.py @@ -33,7 +33,7 @@ from typing import Any from pydantic import BaseModel, Field, model_validator -from app.core.models.enums import ( +from app.core.enums import ( CardType, EnergyType, ModifierMode, diff --git a/backend/app/core/models/game_state.py b/backend/app/core/models/game_state.py index 34c601c..38c1453 100644 --- a/backend/app/core/models/game_state.py +++ b/backend/app/core/models/game_state.py @@ -10,7 +10,8 @@ so that the game can be serialized/deserialized without external dependencies. This supports both live multiplayer and offline/standalone play. Usage: - # Create a new game + from app.core.config import RulesConfig + game = GameState( game_id="match-123", rules=RulesConfig(), @@ -32,8 +33,8 @@ from typing import Any from pydantic import BaseModel, Field from app.core.config import RulesConfig +from app.core.enums import GameEndReason, TurnPhase from app.core.models.card import CardDefinition, CardInstance -from app.core.models.enums import GameEndReason, TurnPhase from app.core.rng import RandomProvider diff --git a/backend/app/core/rules_validator.py b/backend/app/core/rules_validator.py index 709e5d5..b8419a4 100644 --- a/backend/app/core/rules_validator.py +++ b/backend/app/core/rules_validator.py @@ -24,6 +24,12 @@ Example: from pydantic import BaseModel +from app.core.enums import ( + EnergyType, + StatusCondition, + TrainerType, + TurnPhase, +) from app.core.models.actions import ( VALID_PHASES_FOR_ACTION, Action, @@ -40,12 +46,6 @@ from app.core.models.actions import ( UseAbilityAction, ) from app.core.models.card import CardDefinition, CardInstance -from app.core.models.enums import ( - EnergyType, - StatusCondition, - TrainerType, - TurnPhase, -) from app.core.models.game_state import GameState, PlayerState, Zone diff --git a/backend/app/core/turn_manager.py b/backend/app/core/turn_manager.py index d8ac801..612bbfd 100644 --- a/backend/app/core/turn_manager.py +++ b/backend/app/core/turn_manager.py @@ -49,7 +49,7 @@ from typing import TYPE_CHECKING from pydantic import BaseModel -from app.core.models.enums import GameEndReason, StatusCondition, TurnPhase +from app.core.enums import GameEndReason, StatusCondition, TurnPhase from app.core.win_conditions import WinResult, check_cannot_draw if TYPE_CHECKING: diff --git a/backend/app/core/visibility.py b/backend/app/core/visibility.py index 3d50f66..a21c6c6 100644 --- a/backend/app/core/visibility.py +++ b/backend/app/core/visibility.py @@ -37,8 +37,8 @@ from typing import TYPE_CHECKING from pydantic import BaseModel, Field +from app.core.enums import GameEndReason, TurnPhase from app.core.models.card import CardDefinition, CardInstance -from app.core.models.enums import GameEndReason, TurnPhase if TYPE_CHECKING: from app.core.models.game_state import GameState diff --git a/backend/app/core/win_conditions.py b/backend/app/core/win_conditions.py index 6142201..bfa5822 100644 --- a/backend/app/core/win_conditions.py +++ b/backend/app/core/win_conditions.py @@ -39,7 +39,7 @@ from typing import TYPE_CHECKING from pydantic import BaseModel -from app.core.models.enums import GameEndReason +from app.core.enums import GameEndReason if TYPE_CHECKING: from app.core.models.game_state import GameState diff --git a/backend/references/console_testing.py b/backend/references/console_testing.py index 1884749..782e1f1 100644 --- a/backend/references/console_testing.py +++ b/backend/references/console_testing.py @@ -46,19 +46,19 @@ from app.core.config import ( ) from app.core.effects.base import EffectContext from app.core.effects.registry import list_effects -from app.core.models.card import ( - Attack, - CardDefinition, - CardInstance, - WeaknessResistance, -) -from app.core.models.enums import ( +from app.core.enums import ( CardType, EnergyType, PokemonStage, TrainerType, TurnPhase, ) +from app.core.models.card import ( + Attack, + CardDefinition, + CardInstance, + WeaknessResistance, +) from app.core.models.game_state import GameState, PlayerState from app.core.rng import SeededRandom diff --git a/backend/tests/core/conftest.py b/backend/tests/core/conftest.py index 1218932..32d23de 100644 --- a/backend/tests/core/conftest.py +++ b/backend/tests/core/conftest.py @@ -16,14 +16,7 @@ Usage: import pytest from app.core.config import RulesConfig -from app.core.models.card import ( - Ability, - Attack, - CardDefinition, - CardInstance, - WeaknessResistance, -) -from app.core.models.enums import ( +from app.core.enums import ( CardType, EnergyType, PokemonStage, @@ -31,6 +24,13 @@ from app.core.models.enums import ( TrainerType, TurnPhase, ) +from app.core.models.card import ( + Ability, + Attack, + CardDefinition, + CardInstance, + WeaknessResistance, +) from app.core.models.game_state import GameState, PlayerState from app.core.rng import SeededRandom @@ -937,7 +937,7 @@ def game_over_state(extended_card_registry, card_instance_factory) -> GameState: - Player1 has won """ - from app.core.models.enums import GameEndReason + from app.core.enums import GameEndReason player1 = PlayerState(player_id="player1") player2 = PlayerState(player_id="player2") diff --git a/backend/tests/core/test_config.py b/backend/tests/core/test_config.py index 0c4144c..4325730 100644 --- a/backend/tests/core/test_config.py +++ b/backend/tests/core/test_config.py @@ -23,7 +23,7 @@ from app.core.config import ( TrainerConfig, WinConditionsConfig, ) -from app.core.models.enums import EnergyType, PokemonVariant +from app.core.enums import EnergyType, PokemonVariant class TestDeckConfig: diff --git a/backend/tests/core/test_coverage_gaps.py b/backend/tests/core/test_coverage_gaps.py index 624fc21..0064a99 100644 --- a/backend/tests/core/test_coverage_gaps.py +++ b/backend/tests/core/test_coverage_gaps.py @@ -16,6 +16,7 @@ import pytest from app.core.config import RulesConfig from app.core.effects.base import EffectContext from app.core.effects.handlers import handle_attack_damage, handle_coin_flip_damage +from app.core.enums import TurnPhase from app.core.models.actions import ( AttackAction, PassAction, @@ -24,7 +25,6 @@ from app.core.models.actions import ( SelectPrizeAction, ) from app.core.models.card import CardInstance -from app.core.models.enums import TurnPhase from app.core.models.game_state import ForcedAction, GameState, PlayerState from app.core.rng import SeededRandom from app.core.rules_validator import validate_action diff --git a/backend/tests/core/test_effects/test_base.py b/backend/tests/core/test_effects/test_base.py index 2c3e451..ae29e59 100644 --- a/backend/tests/core/test_effects/test_base.py +++ b/backend/tests/core/test_effects/test_base.py @@ -10,8 +10,8 @@ These tests verify: from app.core.config import RulesConfig from app.core.effects.base import EffectContext, EffectResult, EffectType +from app.core.enums import CardType, EnergyType, PokemonStage from app.core.models.card import CardDefinition, CardInstance -from app.core.models.enums import CardType, EnergyType, PokemonStage from app.core.models.game_state import GameState, PlayerState from app.core.rng import SeededRandom diff --git a/backend/tests/core/test_effects/test_handlers.py b/backend/tests/core/test_effects/test_handlers.py index a4e2fc7..45cad46 100644 --- a/backend/tests/core/test_effects/test_handlers.py +++ b/backend/tests/core/test_effects/test_handlers.py @@ -16,8 +16,8 @@ import app.core.effects.handlers # noqa: F401 from app.core.config import RulesConfig from app.core.effects.base import EffectContext, EffectType from app.core.effects.registry import resolve_effect +from app.core.enums import CardType, EnergyType, PokemonStage, StatusCondition from app.core.models.card import CardDefinition, CardInstance, WeaknessResistance -from app.core.models.enums import CardType, EnergyType, PokemonStage, StatusCondition from app.core.models.game_state import GameState, PlayerState from app.core.rng import SeededRandom @@ -549,7 +549,7 @@ class TestAttackDamage: Verify attack_damage uses additive weakness when configured in RulesConfig. """ from app.core.config import CombatConfig - from app.core.models.enums import ModifierMode + from app.core.enums import ModifierMode # Configure game to use additive weakness (+20) game_state.rules.combat = CombatConfig( @@ -593,7 +593,7 @@ class TestAttackDamage: This test verifies the mode switching works with integer values. """ from app.core.config import CombatConfig - from app.core.models.enums import ModifierMode + from app.core.enums import ModifierMode # Configure game to use multiplicative resistance (x1 = no change) game_state.rules.combat = CombatConfig( @@ -631,7 +631,7 @@ class TestAttackDamage: Verify a card can override the game's default weakness mode. Even if game uses multiplicative x2, card can specify additive +40. """ - from app.core.models.enums import ModifierMode + from app.core.enums import ModifierMode # Game uses default multiplicative x2, but card overrides to additive +40 game_state.card_registry["charmander-001"] = CardDefinition( diff --git a/backend/tests/core/test_effects/test_registry.py b/backend/tests/core/test_effects/test_registry.py index 04cdc66..db2502e 100644 --- a/backend/tests/core/test_effects/test_registry.py +++ b/backend/tests/core/test_effects/test_registry.py @@ -22,8 +22,8 @@ from app.core.effects.registry import ( resolve_effect, unregister_handler, ) +from app.core.enums import CardType, EnergyType, PokemonStage from app.core.models.card import CardDefinition, CardInstance -from app.core.models.enums import CardType, EnergyType, PokemonStage from app.core.models.game_state import GameState, PlayerState from app.core.rng import SeededRandom diff --git a/backend/tests/core/test_engine.py b/backend/tests/core/test_engine.py index 27f3204..ff66b0a 100644 --- a/backend/tests/core/test_engine.py +++ b/backend/tests/core/test_engine.py @@ -17,6 +17,16 @@ import pytest from app.core.config import RulesConfig from app.core.engine import GameEngine +from app.core.enums import ( + CardType, + EnergyType, + GameEndReason, + PokemonStage, + PokemonVariant, + StatusCondition, + TrainerType, + TurnPhase, +) from app.core.models.actions import ( AttachEnergyAction, AttackAction, @@ -30,16 +40,6 @@ from app.core.models.actions import ( UseAbilityAction, ) from app.core.models.card import Ability, Attack, CardDefinition, CardInstance -from app.core.models.enums import ( - CardType, - EnergyType, - GameEndReason, - PokemonStage, - PokemonVariant, - StatusCondition, - TrainerType, - TurnPhase, -) from app.core.models.game_state import ForcedAction, GameState from app.core.rng import SeededRandom diff --git a/backend/tests/core/test_evolution_stack.py b/backend/tests/core/test_evolution_stack.py index 223a544..bc4be71 100644 --- a/backend/tests/core/test_evolution_stack.py +++ b/backend/tests/core/test_evolution_stack.py @@ -24,9 +24,7 @@ from app.core.config import RulesConfig from app.core.effects.base import EffectContext from app.core.effects.registry import resolve_effect from app.core.engine import GameEngine -from app.core.models.actions import EvolvePokemonAction -from app.core.models.card import Attack, CardDefinition, CardInstance -from app.core.models.enums import ( +from app.core.enums import ( CardType, EnergyType, PokemonStage, @@ -35,6 +33,8 @@ from app.core.models.enums import ( TrainerType, TurnPhase, ) +from app.core.models.actions import EvolvePokemonAction +from app.core.models.card import Attack, CardDefinition, CardInstance from app.core.models.game_state import GameState, PlayerState from app.core.rng import SeededRandom from app.core.turn_manager import TurnManager diff --git a/backend/tests/core/test_models/test_card.py b/backend/tests/core/test_models/test_card.py index 2e36758..d13d80c 100644 --- a/backend/tests/core/test_models/test_card.py +++ b/backend/tests/core/test_models/test_card.py @@ -8,14 +8,7 @@ These tests verify: 5. JSON serialization round-trips work """ -from app.core.models.card import ( - Ability, - Attack, - CardDefinition, - CardInstance, - WeaknessResistance, -) -from app.core.models.enums import ( +from app.core.enums import ( CardType, EnergyType, PokemonStage, @@ -23,6 +16,13 @@ from app.core.models.enums import ( StatusCondition, TrainerType, ) +from app.core.models.card import ( + Ability, + Attack, + CardDefinition, + CardInstance, + WeaknessResistance, +) class TestAttack: diff --git a/backend/tests/core/test_models/test_enums.py b/backend/tests/core/test_models/test_enums.py index 1a18221..09a4d5f 100644 --- a/backend/tests/core/test_models/test_enums.py +++ b/backend/tests/core/test_models/test_enums.py @@ -12,7 +12,7 @@ import json import pytest from pydantic import BaseModel -from app.core.models.enums import ( +from app.core.enums import ( ActionType, CardType, EnergyType, diff --git a/backend/tests/core/test_models/test_game_state.py b/backend/tests/core/test_models/test_game_state.py index d8404bf..dd5b7d8 100644 --- a/backend/tests/core/test_models/test_game_state.py +++ b/backend/tests/core/test_models/test_game_state.py @@ -8,8 +8,8 @@ These tests verify: """ from app.core.config import RulesConfig +from app.core.enums import CardType, EnergyType, GameEndReason, PokemonStage, TurnPhase from app.core.models.card import CardDefinition, CardInstance -from app.core.models.enums import CardType, EnergyType, GameEndReason, PokemonStage, TurnPhase from app.core.models.game_state import GameState, PlayerState, Zone from app.core.rng import SeededRandom diff --git a/backend/tests/core/test_rules_validator.py b/backend/tests/core/test_rules_validator.py index 8e601f7..188fc68 100644 --- a/backend/tests/core/test_rules_validator.py +++ b/backend/tests/core/test_rules_validator.py @@ -15,6 +15,10 @@ from app.core.config import ( RetreatConfig, TrainerConfig, ) +from app.core.enums import ( + StatusCondition, + TurnPhase, +) from app.core.models.actions import ( AttachEnergyAction, AttackAction, @@ -29,10 +33,6 @@ from app.core.models.actions import ( UseAbilityAction, ) from app.core.models.card import CardInstance -from app.core.models.enums import ( - StatusCondition, - TurnPhase, -) from app.core.models.game_state import ForcedAction, GameState from app.core.rules_validator import validate_action diff --git a/backend/tests/core/test_turn_manager.py b/backend/tests/core/test_turn_manager.py index 6f98d88..ea87913 100644 --- a/backend/tests/core/test_turn_manager.py +++ b/backend/tests/core/test_turn_manager.py @@ -16,8 +16,7 @@ import pytest from app.core.config import ( RulesConfig, ) -from app.core.models.card import CardDefinition, CardInstance -from app.core.models.enums import ( +from app.core.enums import ( CardType, EnergyType, GameEndReason, @@ -26,6 +25,7 @@ from app.core.models.enums import ( StatusCondition, TurnPhase, ) +from app.core.models.card import CardDefinition, CardInstance from app.core.models.game_state import GameState, PlayerState from app.core.rng import SeededRandom from app.core.turn_manager import ( @@ -1497,7 +1497,7 @@ class TestPrizeCardModeKnockout: EX/GX Pokemon are worth 2 prize cards. """ - from app.core.models.enums import PokemonVariant + from app.core.enums import PokemonVariant # Add EX pokemon definition ex_def = CardDefinition( diff --git a/backend/tests/core/test_visibility.py b/backend/tests/core/test_visibility.py index 97f8f0a..281c0ab 100644 --- a/backend/tests/core/test_visibility.py +++ b/backend/tests/core/test_visibility.py @@ -15,8 +15,7 @@ Test categories: import pytest from app.core.config import RulesConfig -from app.core.models.card import CardDefinition, CardInstance -from app.core.models.enums import ( +from app.core.enums import ( CardType, EnergyType, GameEndReason, @@ -25,6 +24,7 @@ from app.core.models.enums import ( TrainerType, TurnPhase, ) +from app.core.models.card import CardDefinition, CardInstance from app.core.models.game_state import ForcedAction, GameState, PlayerState from app.core.visibility import ( VisibleGameState, diff --git a/backend/tests/core/test_win_conditions.py b/backend/tests/core/test_win_conditions.py index 73cdf59..ce7c124 100644 --- a/backend/tests/core/test_win_conditions.py +++ b/backend/tests/core/test_win_conditions.py @@ -16,7 +16,7 @@ Each test includes a docstring explaining the "what" and "why" of the test. import pytest from app.core.config import PrizeConfig, RulesConfig, WinConditionsConfig -from app.core.models.enums import GameEndReason, TurnPhase +from app.core.enums import GameEndReason, TurnPhase from app.core.models.game_state import GameState, PlayerState from app.core.win_conditions import ( WinResult,