# AI Agent Guidelines for MineSeeker This document provides guidelines and context for AI coding agents working on the MineSeeker project. ## Project Overview **MineSeeker** is a real-time multiplayer 1v1 minesweeper game built with Symfony (PHP) and React. Players compete to claim mines on a shared 16×16 grid, with the first to reach 26 mines winning. ### Tech Stack - **Backend:** Symfony 7.2 (PHP 8.3) - **Frontend:** React 18, Vite 6 - **Database:** PostgreSQL 17 with materialized views - **Storage:** MinIO (S3-compatible) - **Real-time:** Mercure (Server-Sent Events) - **Styling:** SCSS, MUI (Material-UI), Emotion - **Fonts:** @fontsource packages (web), Carlito-Bold.ttf (server-side images) ### Key Features **Core Gameplay:** - **Multiplayer-focused:** 1v1 competitive gameplay where players race to claim more mines than their opponent - **Win condition:** First player to claim 26 out of 51 mines wins - **Real-time updates:** WebSocket-like gameplay using Mercure (Server-Sent Events) - **Game restoration:** Players can resume unfinished games from where they left off - **Bonus points system:** Rewards skilled play (blind hits, chain combos, edge mines, endgame mines, safe cell reveals) **User Features:** - **Authentication:** Password + optional TOTP + optional WebAuthn passkeys - **Anonymous play:** Guest players can play without creating an account - **Profile statistics:** Detailed stats including wins, losses, draws, win rate, average score, total mines hit, and bonus points - **Battle history:** View and replay past games move-by-move **Sharing & Social:** - **Battle reports:** Shareable public pages for each completed game (`/battle/{uuid}`) - **OG image generation:** Automatic creation of 1200×630 PNG images for social media sharing (using PHP GD) - **Open Graph tags:** Battle share pages include rich preview cards with player names, avatars, scores, and bonus points --- ## Architecture Overview ### Backend (Symfony) ``` src/ ├── Controller/ # HTTP endpoints (game, profile, battle sharing) ├── Entity/ # Doctrine ORM entities ├── Repository/ # Database queries (uses QueryBuilder, not raw SQL) ├── Service/ # Business logic (BattleCardGenerator, WebAuthn, Email) ├── Dto/ # Data Transfer Objects (immutable, readonly) ├── Util/ # Game logic (TopicManager for Mercure) └── Migrations/ # Database schema changes ``` **Important patterns:** - Use Doctrine ORM QueryBuilder (not raw SQL) in repositories - DTOs are `final readonly` classes with constructor property promotion - Services use dependency injection via `config/services.yaml` - Materialized views for performance (auto-refreshed via triggers) ### Frontend (React) ``` assets/ ├── js/ │ ├── mine-seeker/ # Main game bundle (self-contained) │ │ ├── MineSeeker.jsx # Root component, wraps GameProvider + QueryClientProvider │ │ ├── components/ # Game-specific components │ │ │ ├── GameBoard.jsx # Main game board grid │ │ │ ├── GameTimer.jsx # Game timer display │ │ │ ├── BonusBox.jsx # Bonus points indicator │ │ │ ├── BonusStatsDialog.jsx # Bonus statistics modal │ │ │ ├── CaptchaOverlay.jsx # Captcha challenge overlay │ │ │ ├── ChallengeCountdown.jsx # Challenge timer │ │ │ ├── OnlinePlayersDialog.jsx # Online players list │ │ │ ├── WaitingOverlayContent.jsx # Waiting for opponent │ │ │ ├── grid/ # Grid-related components (cells, mines) │ │ │ ├── profile/ # In-game profile components (PlayerColumn) │ │ │ ├── timer/ # Timer-related components │ │ │ └── user/ # User-related components │ │ ├── contexts/ # React Context API │ │ │ ├── GameContext.jsx # Game state context │ │ │ └── GameProvider.jsx # Context provider with state logic │ │ ├── hooks/ # Custom React hooks │ │ │ ├── useGameDataProvider.js # React Query data provider │ │ │ ├── useGameRefs.jsx # Refs for DOM elements │ │ │ ├── useGameState.jsx # Game state management │ │ │ ├── useServerCommunication.jsx # Mercure SSE connection │ │ │ └── useStepTimer.jsx # Step-by-step timer │ │ └── utils/ # Game-specific utilities │ │ └── constants.jsx # Game constants, colors, defaults │ ├── components/ # Shared UI components │ ├── utils/ # Shared utilities │ ├── profile.jsx # Profile page entry │ ├── passkey.jsx # Passkey management entry │ └── contact.jsx # Contact form entry ├── css/ │ └── homepage/ # SCSS partials (imported by style.homepage.scss) └── fonts/ └── Carlito-Bold.ttf # TTF font for PHP GD image generation ``` **Important patterns:** - Vite aliases: `@mine-components`, `@mine-contexts`, `@mine-hooks`, `@mine-utils`, `@global-components`, `@global-utils` - React Query only available inside `mine-seeker` bundle - Avoid circular dependencies (e.g., don't import from `@global-components` inside `components/` directory) - PropTypes required on all components --- ## Common Tasks ### Adding a New Feature 1. **Backend:** - Create migration for schema changes - Add/update entities and repositories - Create DTOs for data transfer - Add controller endpoints - Update service configuration if needed 2. **Frontend:** - Create components in appropriate bundle - Add PropTypes to all components - Use existing hooks and utilities - Follow styled-components pattern for MUI customization 3. **Documentation:** - Update relevant docs in `docs/` folder - Add examples if introducing new patterns ### Database Changes - Always create migrations: `bin/console make:migration` - Use Doctrine QueryBuilder in repositories (not raw SQL) - For PostgreSQL-specific features (materialized views, triggers), use raw SQL in migrations only - Materialized views should auto-refresh via triggers ### Styling - **Web fonts:** Use `@fontsource` packages (WOFF/WOFF2) - **Server-side images:** Use TTF fonts in `assets/fonts/` (PHP GD requires TTF) - **CSS:** Create SCSS partials in `assets/css/homepage/`, import in main file - **Components:** Use Emotion styled-components or CSS classes ### File Headers All PHP and JS/JSX files should have this header: ```php ` - **Formatting:** 4-space indentation, opening braces on same line for methods/classes **Example DTO:** ```php final readonly class ProfileGameDto implements JsonSerializable { public function __construct( public ?int $id, public string $redName, public string $blueName, public bool $bothRegistered, ) {} } ``` ### JavaScript/React - **Components:** Functional components with hooks - **PropTypes:** Required on all components - **Imports:** Use aliases (`@global-components`, `@mine-hooks`, etc.) - **State:** Use `useState`, `useEffect`, `useCallback`, `useMemo` appropriately - **Avoid:** Circular dependencies, especially with barrel exports **Example component:** ```javascript import React, { useState } from 'react'; import { string, number } from 'prop-types'; export const MyComponent = ({ title, count }) => { const [value, setValue] = useState(0); return