mantimon-tcg/backend/app/core
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
..
effects Add engine validation script with attack_coin_status effect handler 2026-01-28 00:15:12 -06:00
models Remove legacy modifier field from WeaknessResistance 2026-01-29 13:16:51 -06:00
__init__.py Move enums to app/core/enums.py and set up clean module exports 2026-01-26 14:45:26 -06:00
AGENTS.md Add core module AGENTS.md documentation (DOCS-001) 2026-01-26 14:50:52 -06:00
config.py Implement GameService.execute_action enhancements (GS-003) 2026-01-29 15:15:34 -06:00
engine.py Add weakness/resistance support to attack damage calculation 2026-01-26 16:04:41 -06:00
enums.py Move enums to app/core/enums.py and set up clean module exports 2026-01-26 14:45:26 -06:00
README.md Add Active Effects system design and module README files 2026-01-26 22:39:02 -06:00
rng.py Add game engine foundation: enums, config, and RNG modules 2026-01-24 22:14:45 -06:00
rules_validator.py Move enums to app/core/enums.py and set up clean module exports 2026-01-26 14:45:26 -06:00
turn_manager.py Implement GameService.execute_action enhancements (GS-003) 2026-01-29 15:15:34 -06:00
visibility.py Move enums to app/core/enums.py and set up clean module exports 2026-01-26 14:45:26 -06:00
win_conditions.py Move enums to app/core/enums.py and set up clean module exports 2026-01-26 14:45:26 -06:00

Core Game Engine Module

The app.core module contains the platform-independent game engine for Mantimon TCG. This module is designed to be completely decoupled from network and database concerns to enable both online multiplayer and a future offline/standalone version.

Architecture Principle

The core engine should remain forkable as a standalone offline game.

See ARCHITECTURE.md for details on this design goal.

Import Boundaries

# ALLOWED in app/core/
from app.core.models import CardDefinition, GameState
from app.core.config import RulesConfig
from app.core.rng import RandomProvider

# FORBIDDEN in app/core/
from app.services import CardService      # NO - DB dependency
from app.api.deps import get_current_user # NO - Auth dependency
from sqlalchemy import Session            # NO - DB dependency

Submodules

Directory/File Description
models/ Pydantic models for cards, game state, actions
effects/ Effect system for abilities and attacks
engine.py GameEngine - main orchestrator
config.py RulesConfig and sub-configs
turn_manager.py Turn/phase state machine
rules_validator.py Action validation
rng.py Random number generation
visibility.py Hidden information filtering
win_conditions.py Win/loss detection
enums.py Shared enumerations

Key Classes

GameEngine (engine.py)

The primary public API for all game operations.

from app.core import GameEngine, RulesConfig

engine = GameEngine(rules=RulesConfig())
result = engine.create_game(
    player_ids=["p1", "p2"],
    decks={"p1": deck1, "p2": deck2},
    card_registry=registry,
)
game = result.game

# Execute actions
action_result = await engine.execute_action(game, "p1", AttackAction(attack_index=0))

RulesConfig (config.py)

Configures all game rules - deck sizes, prize counts, per-turn limits, etc.

from app.core import RulesConfig

rules = RulesConfig(
    deck=DeckConfig(min_size=60, energy_deck_enabled=False),
    prizes=PrizeConfig(count=6),
)

GameState (models/game_state.py)

Complete state of a game in progress. Self-contained and serializable.

from app.core.models import GameState

# Access player zones
player = game.players["player1"]
active = player.get_active_pokemon()
hand_size = len(player.hand)

TurnManager (turn_manager.py)

Handles turn phases and transitions.

from app.core import TurnManager

manager = TurnManager()
manager.advance_phase(game)  # DRAW -> MAIN -> ATTACK -> END
manager.advance_turn(game)   # Switch to next player

RandomProvider (rng.py)

Protocol for random operations. Supports seeded RNG for testing.

from app.core import create_rng, SeededRandom

# Production: cryptographically secure
rng = create_rng()

# Testing: deterministic
rng = SeededRandom(seed=42)

Enumerations (enums.py)

Enum Description
CardType POKEMON, TRAINER, ENERGY
PokemonStage BASIC, STAGE_1, STAGE_2
EnergyType FIRE, WATER, GRASS, LIGHTNING, etc.
TrainerType ITEM, SUPPORTER, STADIUM, TOOL
StatusCondition POISONED, BURNED, ASLEEP, PARALYZED, CONFUSED
TurnPhase SETUP, DRAW, MAIN, ATTACK, END
GameEndReason PRIZES, DECK_OUT, NO_POKEMON, RESIGNATION, etc.
ModifierMode ADDITIVE, MULTIPLICATIVE

Action Types (models/actions.py)

Action Description
AttackAction Use an attack
AttachEnergyAction Attach energy to Pokemon
PlayPokemonAction Play Basic to bench/active
EvolvePokemonAction Evolve a Pokemon
PlayTrainerAction Play a Trainer card
UseAbilityAction Use a Pokemon's ability
RetreatAction Retreat active Pokemon
SelectActiveAction Choose new active (after KO)
PassAction End turn without attacking
ResignAction Forfeit the game

Visibility System (visibility.py)

Filters game state to hide information opponents shouldn't see.

from app.core import get_visible_state, get_spectator_state

# Player sees own hand, opponent sees card count
visible = get_visible_state(game, "player1")

# Spectator sees neither hand
spectator_view = get_spectator_state(game)

Effects System (effects/)

Handles card abilities and attack effects. See effects/README.md.

from app.core.effects import resolve_effect, EffectContext

result = resolve_effect("deal_damage", ctx)

Testing

cd backend && uv run pytest tests/core/ -v

All core module tests are in tests/core/ with ~800+ tests at 97% coverage.


See Also