Skip to content
brendonbachiePublic

About

Pac-Man clone in Python — Ghosts! More ghosts! 42 project.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

This project has been created as part of the 42 curriculum by and .

Pac-Man — Ghosts! More ghosts!

Python lint

A complete, playable recreation of the classic 1980 arcade game Pac-Man, written in modern, object-oriented Python. Levels are built from an external A-Maze-ing maze generator, the game is fully configurable through a commented JSON file, and it ships with a persistent highscore table and a cheat mode for peer review.


Description

Guide Pac-Man through a maze, eating every pacgum while dodging four ghosts — Blinky, Pinky, Inky and Clyde, each with its own chase behaviour. Grab a super-pacgum in a corner to turn the tables and eat the ghosts for bonus points. Clear all the pacgums to advance; survive 10+ levels to win.

The goal of the project is a modular, reusable architecture: the entire rule set lives in a headless, unit-tested engine, completely decoupled from a thin pygame rendering layer.

Features

  • 10+ procedurally generated levels (level 1 uses a fixed seed).
  • Four ghosts with distinct chase behaviours, fright mode and respawn.
  • Pacgums, super-pacgums (power pellets), scoring and lives.
  • Per-level time limit, pause/resume.
  • Polished UI: main menu with live highscores, instructions, in-game HUD, pause menu, game-over and victory screens with name entry.
  • Persistent top-10 highscore table.
  • Cheat mode to make peer review easy.
  • Commented-JSON configuration with robust defaults — never crashes.
  • flake8- and mypy-clean, fully type-hinted and documented.
  • Desktop packaging (PyInstaller) ready for Itch.io / Steam.

Requirements

  • Python 3.10+
  • pygame (installed by make install)
  • The assigned A-Maze-ing package wheel (mazegenerator-*.whl), placed in the project root. It is installed by make install and re-installed by your reviewer.

Instructions

Linux / macOS (with make)

make install                 # create .venv, install deps + the maze wheel
make run                     # runs: python pac-man.py config.json
make run CONFIG=my.json      # run with a custom config
make debug                   # run under pdb
make lint                    # flake8 . + mypy . (mandatory flags)
make lint-strict             # flake8 . + mypy . --strict
make test                    # run the unit test suite
make package                 # build a standalone desktop app (see packaging/)
make clean                   # remove caches / build artifacts

Windows (PowerShell, no make needed)

python -m venv .venv
.venv\Scripts\pip install -r requirements-dev.txt
.venv\Scripts\pip install mazegenerator-2.1.0-py3-none-any.whl
.venv\Scripts\python pac-man.py config.json

The program takes exactly one argument: a .json configuration file. Any error (missing file, bad value, missing key, generator failure) is reported with a clear message — never a Python traceback.

Controls

Action Keys
Move Arrow keys or W A S D
Pause / resume P or ESC
Menu navigation Arrows + ENTER / SPACE
Confirm name type (letters/digits/space, ≤10), ENTER

Cheat mode (during play)

Cheats exist to let a reviewer test every feature quickly.

Key Cheat
I Toggle invincibility (ghosts can't kill you)
G Freeze / unfreeze ghosts
B Toggle speed boost
N Skip to the next level (win the current one)
L Add one life
C Show / hide the cheat help panel

Configuration

The config file is JSON with comments: any line content after # or //, and /* … */ blocks, is ignored. Missing or invalid values fall back to a safe default (a message is logged) and unknown keys are ignored — so the game keeps running no matter what. Only a totally unreadable/unparsable file is fatal (with a clean message). See config.json for a fully-commented example.

Key Type Default Range / notes
highscore_filename string highscores.json where scores are stored
lives int 3 1–99
seed int 42 level 1 seed (0 = random)
level_max_time int 90 5–3600 seconds per level
width int 19 7–41 maze cells (≥14 to show the "42" logo)
height int 15 7–41 maze cells (≥10 to show the "42" logo)
pacgum int 42 max pacgums/level; ≤0 = fill every corridor
points_per_pacgum int 10 0–100000
points_per_super_pacgum int 50 0–100000
points_per_ghost int 200 0–100000
fright_time float 7.0 0–60 s ghosts stay edible
ghost_respawn_delay float 6.0 0–60 s before an eaten ghost returns
player_speed float 5.0 1–20 tiles/s
ghost_speed float 4.5 1–20 tiles/s
frightened_speed_factor float 0.6 0.2–1.0 ghost slow-down while edible
levels list — optional [{ "width":…, "height":… }, …]; padded to ≥10

If levels is omitted, every level uses width×height; the list is always padded to at least 10 levels (growing slightly for difficulty).

Highscore

Highscores are stored as a small JSON file on disk (path from highscore_filename), e.g.:

[ { "name": "Sannaka", "score": 1110 }, { "name": "foliole", "score": 20 } ]
  • Loaded at game start, saved whenever a score is added.
  • Keeps the top 10, best first.
  • Player names are validated: letters, digits and spaces only, ≤10 characters (empty → "Player"); scores are non-negative integers.
  • Robust to every file error: a missing or corrupted file simply yields an empty table instead of crashing; a failed write is swallowed cleanly.

Why this design: a plain JSON file is human-readable, trivial to inspect during review, dependency-free, and easy to reset (just delete it). The manager fully sanitises input on the way in, so the file can never carry the game into a bad state on the way out.

Maze Generation

We do not write our own generator — we adapt the assigned A-Maze-ing package (mazegenerator) as-is, with PERFECT=False (Pac-Man-friendly corridors with loops).

The generator returns a thin-wall grid: a list maze[y][x] where each cell is a 4-bit wall mask (N=1, E=2, S=4, W=8); a set bit means a wall on that side, and the value 15 marks the isolated cells that draw the hidden “42” logo. Two adjacent cells are connected when the shared wall bit is clear.

Pac-Man needs thick walls (walls occupy their own tiles), so our loader (game/maze.py) doubles the resolution into a (2·w+1) × (2·h+1) grid:

cell (x, y)                -> corridor tile (2x+1, 2y+1)
open passage East of (x,y) -> corridor tile (2x+2, 2y+1)
open passage South of (x,y)-> corridor tile (2x+1, 2y+2)
everything else            -> wall

Isolated “42” cells stay solid, so the logo appears embedded in the walls. The loader then decorates the grid: pacgums in the corridors, super-pacgums and one ghost at each of the four corners, and the player at the corridor nearest the centre. The loader adapts to their interface, not the opposite, and every call to the package is wrapped so a generator failure becomes a clean MazeError, never a crash.

Implementation

  • Language / tooling: Python 3.10+, type-hinted throughout, PEP-257 docstrings, flake8 + mypy clean, pytest suite.
  • Movement model: actors hold continuous float positions in tile units; turns and AI decisions happen only at tile centres, and each frame is split so a sub-step never overshoots a centre. This guarantees perfect grid alignment and makes wall-clipping impossible.
  • Ghost AI: at each tile a ghost picks the legal neighbour (no U-turns unless dead-ended) that best fits its target — direct chase (Blinky), four tiles ahead (Pinky), a mirror through Blinky (Inky), or chase/scatter by distance (Clyde). Frightened ghosts maximise distance from the player; eaten ghosts return home and respawn after a delay.
  • Engine/render split: game/engine.py computes everything and emits a small list of events per frame; game/app.py only reads state and draws.
  • Robustness: config clamping, generator guarding, and a last-resort guard around the main loop guarantee the "no crash" requirement. Set PACMAN_DEBUG=1 to re-raise for development.

General Software Architecture

graph TD
    CLI["pac-man.py<br/>(CLI entry, arg parsing)"] --> CFG
    LAUNCH["packaging/launcher.py<br/>(frozen entry)"] --> CFG
    CLI --> APP
    LAUNCH --> APP
    subgraph Core["game/ (logic — no rendering)"]
        CFG["config.py<br/>GameConfig, JSONC loader"]
        HS["highscore.py<br/>HighScoreManager"]
        MAZE["maze.py<br/>Maze, build_maze()"]
        ENT["entities.py<br/>Player, Ghost"]
        ENG["engine.py<br/>GameEngine, Cheats"]
    end
    APP["app.py<br/>PacmanApp (pygame UI)"]
    EXT["mazegenerator<br/>(external A-Maze-ing pkg)"]
    APP --> ENG
    APP --> HS
    ENG --> MAZE
    ENG --> ENT
    ENG --> CFG
    MAZE --> EXT
    MAZE --> CONST["constants.py"]
Loading
Module Responsibility
pac-man.py CLI entry; parse the single argument, load config, launch.
game/constants.py Enums, colours, defaults, tunables.
game/config.py JSONC parsing, validation/clamping → GameConfig.
game/highscore.py Load/validate/persist the top-10 table.
game/maze.py Adapt the external generator into a playable Maze.
game/entities.py Player and Ghost — movement core + ghost AI.
game/engine.py All rules: scoring, lives, fright, levels, timer, cheats.
game/app.py pygame window, screens, HUD, input, render loop.
packaging/ PyInstaller spec + launcher + deployment guide.

Packaging & Deployment

make package (PyInstaller) produces a self-contained desktop build in dist/pacman/, launchable with a double-click (the bundled config.json is loaded automatically). Full instructions for publishing an unlisted/free build on Itch.io or Steam, plus an optional pygbag web build, are in packaging/README.md.

Project Management

The project was driven with a lightweight Kanban flow and an engine-first plan. All planning, analysis, risk tracking, team organisation and the full acceptance-test plan live in project_management/.

Resources

Classic references used while building the game:

How AI was used

AI (an LLM coding assistant) was used as a productivity and review aid, and every suggestion was read, understood, tested and adapted before acceptance:

  • Scaffolding & boilerplate: drafting module skeletons, the Makefile, setup.cfg and docstrings — then hand-tuned.
  • Maze adaptation: discussing the thin→thick wall doubling and verifying connectivity against the package's own logic.
  • Review & debugging: the assistant helped surface the level-timeout life-drain bug, which we then reproduced, fixed and covered with a test.
  • Documentation: first drafts of this README and the project-management documents, edited for accuracy.

AI was not used to submit code we could not explain: the engine, the maze adapter and the ghost AI were reviewed line by line by the team.

About

Pac-Man clone in Python — Ghosts! More ghosts! 42 project.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages