{"id":43049686,"url":"https://github.com/wboayue/pacman","last_synced_at":"2026-01-31T10:15:43.479Z","repository":{"id":331428104,"uuid":"715350011","full_name":"wboayue/pacman","owner":"wboayue","description":"Pacman clone in C++20 with SDL2. Features ghost AI with chase/scatter/scared modes, state machine architecture, and async audio.","archived":false,"fork":false,"pushed_at":"2026-01-02T08:41:28.000Z","size":1557,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-08T05:02:51.676Z","etag":null,"topics":["arcade-game","cmake","cpp","game","game-development","pacman","retro-game","sdl2"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wboayue.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2023-11-07T00:54:55.000Z","updated_at":"2026-01-02T08:38:56.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/wboayue/pacman","commit_stats":null,"previous_names":["wboayue/pacman"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/wboayue/pacman","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wboayue%2Fpacman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wboayue%2Fpacman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wboayue%2Fpacman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wboayue%2Fpacman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wboayue","download_url":"https://codeload.github.com/wboayue/pacman/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wboayue%2Fpacman/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28938004,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-31T08:53:31.997Z","status":"ssl_error","status_checked_at":"2026-01-31T08:51:38.521Z","response_time":128,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arcade-game","cmake","cpp","game","game-development","pacman","retro-game","sdl2"],"created_at":"2026-01-31T10:15:42.802Z","updated_at":"2026-01-31T10:15:43.467Z","avatar_url":"https://github.com/wboayue.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pacman\n\nA clean C++20 Pac-Man implementation using SDL2, designed for readability and extensibility.\n\n![Gameplay Screenshot](assets/screenshots/gameplay.png)\n\n## Table of Contents\n\n- [Prerequisites](#prerequisites)\n- [Building and Running](#building-and-running)\n- [Controls](#controls)\n- [Asset Configuration](#asset-configuration)\n- [Architecture Overview](#architecture-overview)\n- [Core Systems](#core-systems)\n- [Entities](#entities)\n- [Ghost AI](#ghost-ai)\n- [Extending the Game](#extending-the-game)\n- [Known Limitations](#known-limitations)\n\n## Prerequisites\n\n**Required:**\n- CMake 3.16+\n- C++20 compiler (GCC 10+, Clang 10+, or MSVC 2019+)\n- SDL2, SDL2_image, SDL2_mixer\n\n**Optional:**\n- clang-tidy (auto-enabled if found)\n- clang-format\n\n### macOS (Homebrew)\n\n```bash\nbrew install cmake sdl2 sdl2_image sdl2_mixer\nbrew install llvm  # For clang-tidy and clang-format\n```\n\n### Ubuntu/Debian\n\n```bash\nsudo apt-get install cmake build-essential \\\n    libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev \\\n    clang-tidy clang-format\n```\n\n## Building and Running\n\n```bash\n# Clone and build\ngit clone https://github.com/wboayue/pacman.git\ncd pacman\nmkdir -p build \u0026\u0026 cd build\ncmake .. -DCMAKE_BUILD_TYPE=Release\nmake -j4\n\n# Run (from build directory)\n./pacman\n```\n\n**Important:** Run the executable from the `build/` directory so asset paths resolve correctly (default: `../assets`).\n\n### Build Options\n\n```bash\n# Debug build\ncmake .. -DCMAKE_BUILD_TYPE=Debug\n\n# Disable clang-tidy\ncmake .. -DENABLE_CLANG_TIDY=OFF\n\n# Format source code\ncmake --build . --target clang-format\n\n# Check formatting\ncmake --build . --target clang-format-check\n```\n\n## Controls\n\n| Key | Action |\n|-----|--------|\n| Arrow Keys | Move Pacman |\n| P | Pause/Resume |\n| ESC | Quit |\n\n## Asset Configuration\n\nAssets are loaded from `../assets` relative to the executable by default.\n\nTo use a custom asset directory, set the `ASSET_PATH` environment variable:\n\n```bash\n# Unix/macOS\nexport ASSET_PATH=/path/to/custom/assets\n./pacman\n\n# Or inline\nASSET_PATH=/path/to/custom/assets ./pacman\n```\n\n### Asset Directory Structure\n\n```\nassets/\n├── maze.txt                 # Level layout (28x36 grid)\n├── sounds/\n│   ├── game_start.wav       # Intro music\n│   ├── munch_1.wav          # Pellet eating sound 1\n│   ├── munch_2.wav          # Pellet eating sound 2\n│   ├── power_pellet.wav     # Power pellet loop\n│   └── death_1.wav          # Death sound\n└── sprites/\n    ├── pacman.png           # Pacman animation frames\n    ├── blinky.png           # Red ghost\n    ├── pinky.png            # Pink ghost\n    ├── inky.png             # Cyan ghost\n    ├── clyde.png            # Orange ghost\n    ├── scared-ghost.png     # Frightened ghost\n    ├── ghost-eyes.png       # Respawning ghost\n    ├── pellet.png           # Regular pellet\n    ├── power-pellet.png     # Power pellet\n    ├── maze.png             # Maze background\n    ├── fruits.png           # Bonus fruits\n    └── white-text.png       # Score/UI text\n```\n\n## Architecture Overview\n\n```mermaid\ngraph LR\n    subgraph Game[Game Loop - 60 FPS]\n        Input --\u003e Update --\u003e Render\n    end\n\n    subgraph Subsystems\n        Renderer\n        Audio[Audio - Async]\n        Assets[Asset Manager]\n    end\n\n    subgraph Entities\n        Pacman\n        Ghosts\n        Grid[Grid + Pellets]\n    end\n\n    Game --\u003e Subsystems\n    Game --\u003e Entities\n```\n\n**Game States:**\n\n```mermaid\nstateDiagram-v2\n    Ready --\u003e Play: start\n    Play --\u003e Paused: P key\n    Paused --\u003e Play: P key\n    Play --\u003e Dying: ghost collision\n    Play --\u003e LevelComplete: all pellets\n    Dying --\u003e Ready: lives \u003e 0\n    LevelComplete --\u003e Ready: next level\n```\n\n### Design Patterns\n\n- **State Pattern**: Game states (Ready, Play, Paused, Dying, LevelComplete) and ghost behavior states\n- **Manager Pattern**: Dedicated managers for assets, board rendering, and audio\n- **Factory Pattern**: Ghost state creation via factory methods\n\n## Core Systems\n\n### Game Loop (`src/game.cpp`)\n\nThe `Game` class orchestrates the main loop at 60 FPS:\n\n1. **Input**: Process SDL events and keyboard state\n2. **Update**: Update entities, animations, and wave manager\n3. **Render**: Clear → maze → pellets → ghosts → pacman → present\n\n### Game States\n\n| State | Duration | Description |\n|-------|----------|-------------|\n| Ready | 4s | Reset positions, play intro sound |\n| Play | - | Main gameplay, processes input |\n| Paused | - | Freezes all entities, waits for P key |\n| Dying | 2s | Death animation, decrement lives |\n| LevelComplete | 2s | Reset for next level |\n\n### Audio System (`src/audio-system.cpp`)\n\nThread-safe asynchronous audio using SDL_mixer:\n\n- Dedicated audio thread processes a request queue\n- 16 concurrent audio channels\n- Sound handle system for cancellation\n- Mutex-protected queue operations\n\n### Asset Manager (`src/asset-manager.cpp`)\n\nCentralized resource loading with caching:\n\n- `AssetRegistry`: Maps enums to file paths\n- `AssetManager`: Loads and caches textures/sounds\n- Automatic cleanup in destructor\n\n### Renderer (`src/renderer.cpp`)\n\nSDL2 rendering wrapper:\n\n- Hardware-accelerated rendering\n- Maintains 224:288 aspect ratio on resize\n- Logical resolution scaling\n\n## Entities\n\n### Pacman (`src/pacman.cpp`)\n\nPlayer-controlled character:\n\n- **Speed**: 75.76 × 0.8 = 60.6 pixels/second\n- **Home Position**: Cell (13, 26)\n- **Energizer Duration**: 6 seconds\n- Tunnel wrapping at row 17\n\n### Ghost (`src/ghost.cpp`)\n\nAI-controlled enemies with individual personalities:\n\n| Ghost | Color | Personality | Scatter Corner |\n|-------|-------|-------------|----------------|\n| Blinky | Red | Direct chase | Top-right |\n| Pinky | Pink | Ambush (4 cells ahead) | Top-left |\n| Inky | Cyan | Unpredictable (uses Blinky's position) | Bottom-right |\n| Clyde | Orange | Shy (retreats when close) | Bottom-left |\n\n### Pellets (`src/pellet.cpp`)\n\n- **Regular Pellet**: 10 points\n- **Power Pellet**: 50 points, enables ghost eating for 6s\n- **Total**: 244 pellets per level\n\n## Ghost AI\n\n### Ghost States\n\n```mermaid\nstateDiagram-v2\n    Penned --\u003e ExitingPen: activated\n    ExitingPen --\u003e Scatter: exit pen\n    Scatter --\u003e Chase: wave change\n    Chase --\u003e Scatter: wave change\n    Scatter --\u003e Scared: power pellet\n    Chase --\u003e Scared: power pellet\n    Scared --\u003e Scatter: timer expires (scatter wave)\n    Scared --\u003e Chase: timer expires (chase wave)\n    Scared --\u003e Respawning: eaten\n    Respawning --\u003e ExitingPen: reached pen\n```\n\n| State | Behavior |\n|-------|----------|\n| Penned | Bounce in ghost pen until activated |\n| ExitingPen | Move upward out of pen |\n| Scatter | Move to corner, ignore Pacman |\n| Chase | Pursue Pacman using personality targeting |\n| Scared | Flee randomly, can be eaten |\n| Respawning | Return to pen at 2x speed |\n\n### Wave Pattern\n\nGhosts alternate between Scatter and Chase modes:\n\n| Wave | Mode | Duration |\n|------|------|----------|\n| 1 | Scatter | 7s |\n| 2 | Chase | 20s |\n| 3 | Scatter | 7s |\n| 4 | Chase | 20s |\n| 5 | Scatter | 5s |\n| 6 | Chase | 20s |\n| 7 | Scatter | 5s |\n| 8 | Chase | ∞ |\n\n## Extending the Game\n\n### Add a New Sound\n\n1. Add enum to `Sounds` in `src/asset-registry.h`\n2. Map path in `GetSoundPath()` in `src/asset-registry.cpp`\n3. Place WAV file in `assets/sounds/`\n4. Play with `game.PlaySound(Sounds::kNewSound)`\n\n### Add a New Ghost\n\n1. Create config class inheriting `GhostConfig` in `src/ghost.cpp`\n2. Implement `GetTargeter()` with targeting logic\n3. Set start position, scatter cell, and sprite\n4. Instantiate in `Game::createGhosts()`\n\n### Modify Wave Timing\n\nEdit `kWaves` array in `src/game-context.h`:\n\n```cpp\nstatic constexpr std::array\u003cstd::pair\u003cGhostMode, float\u003e, 8\u003e kWaves{{\n  {GhostMode::kScatter, 7.0f},   // Duration in seconds\n  {GhostMode::kChase, 20.0f},\n  // ...\n}};\n```\n\n### Adjust Game Constants\n\nKey values in `src/constants.h`:\n\n| Constant | Default | Description |\n|----------|---------|-------------|\n| `kMaxSpeed` | 75.76 | Base movement speed |\n| `kPacmanSpeedMultiplier` | 0.8 | Pacman speed factor |\n| `kGhostSpeedMultiplier` | 0.73 | Ghost speed factor |\n| `kEnergizerDuration` | 6.0 | Power pellet duration (seconds) |\n| `kPelletPoints` | 10 | Points per pellet |\n| `kGhostPoints` | 200 | Points per ghost eaten |\n\n### Add a New Game State\n\n1. Add to `GameStates` enum in `src/game.h`\n2. Create state struct with `Enter()` and `Tick()` methods\n3. Register in `initializeStates()` map\n\n## Project Structure\n\n```\npacman/\n├── CMakeLists.txt          # Build configuration\n├── assets/                 # Game assets\n│   ├── maze.txt\n│   ├── sounds/\n│   └── sprites/\n└── src/\n    ├── main.cpp            # Entry point\n    ├── game.h/cpp          # Game loop and state machine\n    ├── renderer.h/cpp      # SDL2 rendering\n    ├── audio-system.h/cpp  # Async audio\n    ├── asset-manager.h/cpp # Resource loading\n    ├── asset-registry.h/cpp# Asset path mapping\n    ├── board-manager.h/cpp # UI rendering (score, lives)\n    ├── pacman.h/cpp        # Player entity\n    ├── ghost.h/cpp         # Ghost AI and states\n    ├── grid.h/cpp          # Game board\n    ├── pellet.h/cpp        # Collectibles\n    ├── sprite.h/cpp        # Animated sprites\n    ├── game-context.h/cpp  # Game state and wave manager\n    ├── vector2.h/cpp       # 2D vector math\n    └── constants.h         # Game constants\n```\n\n## Known Limitations\n\nThis implementation focuses on core gameplay. Not yet implemented:\n\n- **Cut scenes**: No intermission animations between levels\n- **Level progression**: Levels don't increase in difficulty (speed, ghost behavior)\n- **Fruit bonus**: Bonus fruits appear but scoring not fully implemented\n- **High score**: No persistent high score tracking\n- **Multiple lives animation**: No extra life indicator animation\n- **Sound variations**: Limited sound variety compared to original arcade\n\n## License\n\nThis project is licensed under the [MIT License](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwboayue%2Fpacman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwboayue%2Fpacman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwboayue%2Fpacman/lists"}