A minimalist, terminal-first Pacman clone written in Go.
termpac brings the classic 1980 arcade maze chase to your terminal with a focus on smooth real-time movement, historic accuracy, and clean design. Built using the ttasc/ttbox library, it leverages a fixed-timestep game loop to provide a consistent gameplay experience whether you prefer arrow keys or strict Vim-style navigation.
It exists as both a fully playable retro game and a practical reference implementation for building structured, real-time TUI applications in Go without over-engineering.
- Input Buffering: Seamless cornering. Keystrokes are buffered so Pacman turns exactly when he hits an intersection, preventing "missed" inputs.
- Historic Arcade AI: Ghosts behave just like they did in 1980. Blinky chases directly, Pinky ambushes, and Clyde wanders off when getting too close.
- Zero-allocation Loop: Designed without runtime heap allocations. The map and entities are reused during resets, leaving the Garbage Collector completely idle during gameplay.
- Fixed-Timestep Engine: Logic updates at a strict 10 FPS while input is polled at 50 FPS, ensuring consistent game speed across all hardware while keeping CPU usage minimal.
- Vim-key Support: Full support for Keyboard (
hjklor Arrows).
You can download the pre-built binary file here or build from source:
git clone https://github.com/ttasc/termpac.git
cd termpac
go build -o termpacSimply run the executable.
./termpac| Action | Keyboard | Mouse |
|---|---|---|
| Move Pacman | h, j, k, l / Arrows |
- |
| Reset Game | r / R |
Left-Click [ RESET ] |
| Quit | ESC / q / Ctrl+C |
- |
The project adheres to a strict separation of concerns, ensuring that rendering, logic, and state management never bleed into each other.
state.go: The Source of Truth. Defines the data models (GameState,Pacman,Ghost,Map). Holds all mutable data including the static grid and entity positions. Implements memory recycling for fast game resets.main.go: The Orchestrator. Bootstraps the terminal, manages the application lifecycle, and runs the continuous, real-time fixed-timestep game loop.input.go: The Translator. Parses raw terminal events and maps them to directional intents, feeding theNextDirbuffer for smooth movement.logic.go: The Ruleset. Implements the core gameplay mechanics, including wall sliding, historic Arcade Ghost AI (Euclid distance target finding), and collision detection.renderer.go: The View. A pure function of the state. Clears and redraws the terminal screen every frame, handling spatial math, aspect ratio correction (2:1 character grids to simulate perfect squares), and ANSI colors.
- Minimalism: Code should do exactly what it needs to do and nothing more. No complex design patterns like A* pathfinding when a simple historic vector calculation suffices.
- Readability Over Cleverness: Algorithms like the Ghost Target AI are written explicitly. It favors standard control structures over dense, chained abstractions.
- Terminal-First UX: Terminals have unique constraints. The UI is mathematically constructed to look balanced and readable natively, using a 2-character-wide block approach (e.g.,
██for walls) to achieve a true retro grid feel.
Contributions are welcome! Whether it's a bug fix, an optimization, or a minor UI tweak, feel free to open a PR.
Please ensure your code aligns with the minimalist philosophy: keep abstractions low, rely on standard Go idioms, and ensure the terminal UI remains fast and responsive without heavy CPU usage.
MIT License. See LICENSE for details.
