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.
This commit is contained in:
Cal Corum 2026-01-26 14:45:26 -06:00
parent c3623d9541
commit e7431e2d1f
30 changed files with 311 additions and 123 deletions

View File

@ -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": {

View File

@ -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",
]

View File

@ -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):

View File

@ -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",
]

View File

@ -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")

View File

@ -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)

View File

@ -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",
]

View File

@ -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",

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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,

View File

@ -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,