# 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
{title}: {count + value}
; }; MyComponent.propTypes = { title: string.isRequired, count: number.isRequired, }; ``` --- ## Important Files & Locations ### Configuration - `config/services.yaml` - Service definitions and parameters - `vite.config.js` - Vite build config, aliases - `.env` - Environment variables (not in git) - `composer.json` - PHP dependencies - `package.json` - Node dependencies ### Key Services - `BattleCardGenerator` - Generates 1200×630 PNG OG images using PHP GD - `TopicManager` - Mercure topic management and game logic - `WebAuthnService` - Passkey authentication - `Email services` - Various email senders in `src/Service/Email/` ### Important Entities - `User` - Registered users - `PlayedGame` - Game records with moves, grid, scores - `RecentBattle` - Read-only entity from materialized view - `UserStats` - Read-only entity from materialized view ### Documentation - `docs/game-mechanics/BONUS_POINTS_SYSTEM.md` - Bonus points reference - `docs/FONTS.md` - Font usage and management - `AGENTS.md` - This file - `CHANGELOG.md` - Project changelog --- ## Common Pitfalls ### ❌ Don't Do - **Don't use raw SQL in repositories** (use Doctrine QueryBuilder) - **Don't create circular imports** (e.g., importing `@global-components` from within `components/`) - **Don't use system fonts directly** (bundle TTF in `assets/fonts/`) - **Don't skip PropTypes** on React components - **Don't use `var`** in JavaScript (use `const` or `let`) - **Don't define variables after using them** (hoisting issues with `const`) ### ✅ Do - **Use Doctrine QueryBuilder** with `expr()` methods - **Import components directly** to avoid circular dependencies - **Bundle fonts in project** for portability - **Add PropTypes** to all components - **Use `const` for immutable values**, `let` for mutable - **Define variables before using them** --- ## Keeping Components in Sync ### Battle Report Display The battle report/statistics appear in **two places** that must be kept synchronized: #### 1. BattleDialog Component (React) **File:** `assets/js/components/BattleDialog.jsx` - React dialog component shown on profile page - Uses Material-UI and styled-components - Displays game stats, bonus points, winner information #### 2. Battle Share Page (Twig) **File:** `templates/Game/battle_share.html.twig` - Public battle share page (accessible via `/battle/{uuid}`) - Server-rendered HTML with SCSS styling - Shows same information as BattleDialog ### Synchronization Rules **When updating BattleDialog.jsx, also update battle_share.html.twig:** ✅ Adding new stats or fields ✅ Changing display logic (winner calculation, formatting) ✅ Modifying bonus points display ✅ Updating labels or text **Keep in sync:** - Game outcome logic (win/loss/draw/abandoned) - Bonus points formatting - Player name display - Score display - Stats and metadata shown **Example:** If you add a new stat to BattleDialog showing "fastest mine claim time", you must also add it to battle_share.html.twig so both displays show the same information. --- ## Testing & Building ### Backend ```bash # Run migrations bin/console doctrine:migrations:migrate # Clear cache bin/console cache:clear # Refresh materialized views bin/console dbal:run-sql "REFRESH MATERIALIZED VIEW CONCURRENTLY recent_battles" ``` ### Frontend ```bash # Development build with watch npm run dev # Production build npm run build # After changing fonts rm -rf var/og-cache/* ``` --- ## Git Workflow ### Commit Messages Follow conventional commits: - `feat: add bonus points to battle cards` - `fix: resolve circular dependency in BattleDialog` - `refactor: use Doctrine QueryBuilder in RecentBattleRepository` - `docs: add AGENTS.md for AI coding agents` - `chore: update dependencies` ### Creating Pull Requests When creating PRs, include: 1. **Summary** - What was changed and why 2. **Changes** - List of modified files/components 3. **Testing** - How to verify the changes 4. **Screenshots** - For UI changes --- ## Performance Considerations ### Database - Use materialized views for expensive queries (profile stats, recent battles) - Auto-refresh materialized views via triggers on source table changes - Index frequently queried columns - Use `COALESCE()` for nullable aggregates ### Frontend - Lazy load large components - Use React Query for data fetching and caching - Avoid unnecessary re-renders (use `useMemo`, `useCallback`) - Bundle code per entry point (profile, passkey, contact, game) ### Images - Battle card images are cached in `var/og-cache/` - Images regenerated when games change (via deterministic UUID) - Use appropriate image sizes (1200×630 for OG images) --- ## Security ### Authentication - Password + TOTP (optional) + WebAuthn passkeys (optional) - Backup codes for TOTP recovery - Session-based authentication with `IS_AUTHENTICATED_REMEMBERED` ### Data Access - Controllers check `denyAccessUnlessGranted()` - Materialized views filter by `user_id` - Guest players use separate `Gamer` entity (no `User` account) ### Input Validation - Symfony forms for user input - File upload validation (size, MIME type) - WebAuthn challenge validation --- ## Useful Commands ```bash # Symfony bin/console debug:container ServiceName # Inspect service configuration bin/console debug:router # List all routes bin/console make:migration # Create new migration bin/console doctrine:migrations:list # List migrations # Database bin/console dbal:run-sql "SELECT * FROM ..." # Run SQL query # Assets npm run build # Build production assets npm run dev # Build with watch mode # Git git log --oneline --graph # View commit history git diff origin/main...HEAD # See changes since main ``` --- ## Need Help? - **Bonus Points:** See `docs/game-mechanics/BONUS_POINTS_SYSTEM.md` - **Fonts:** See `docs/FONTS.md` - **Symfony Docs:** https://symfony.com/doc/current/ - **React Docs:** https://react.dev/ - **Doctrine ORM:** https://www.doctrine-project.org/projects/doctrine-orm/en/current/ --- ## Version History - **2026-04-21:** Initial AGENTS.md created - Document common patterns, pitfalls, and project structure - Include coding standards and examples --- **Happy coding! 🚀**