From 8fe2319e189b4d830a892b8c686d08cff5556962 Mon Sep 17 00:00:00 2001 From: scratko Date: Thu, 31 Jul 2025 03:55:59 +0300 Subject: Add README and revise distance metric Added many inline comments for clarity. Introduced initial version of README with gameplay details, controls, and AI logic. Replaced Euclidean distance with Manhattan distance in standard_distance() to better match grid-based movement without diagonal traversal. --- README.md | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ field.c | 4 +-- field.h | 6 ++++ ghosts.c | 21 +++++++++++-- pacman.c | 10 +++++++ pacman.png | Bin 0 -> 12471 bytes 6 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 pacman.png diff --git a/README.md b/README.md index 6e2db1a..116b66f 100644 --- a/README.md +++ b/README.md @@ -1 +1,99 @@ +# Pac-Man (Console Version with `ncurses`) + +A terminal-based Pac-Man game written in C with `ncurses`. It faithfully +replicates the original ghost AI from the classic 1980 arcade game, including +their behavioral modes, targeting rules, and unique pathfinding algorithms. + + + +This version contains one fixed level and implements core gameplay mechanics: +Pac-Man movement, ghost AI, lives, energizers (power pellets), frightened mode, +collision handling, and score tracking. + +## Features + +- **Classic Pac-Man logic** — core mechanics faithfully implemented +- **Ghost behavior inspired by the original game** — based on *The Pac-Man Dossier* +- **Mode switching** — scatter, chase, frightened +- **Unique AI logic per ghost** — each ghost uses different rules and pathfinding +- **Life system** — Pac-Man has limited lives +- **Power pellets** — enable Pac-Man to eat ghosts in frightened mode +- **Colored rendering** — `ncurses`-based graphics with different colors for each ghost and Pac-Man +- **Directional movement logic** — buffered input for responsive turns +- **Single static level** — pre-defined grid layout with maze walls and teleport tunnel +- **Maze collision and coin collection** — walls, tunnels, and dot eating logic +- **Pause support** — press spacebar to pause/resume the game + +## Technologies Used + +- **Language:** C (C99) +- **Graphics:** `ncurses` +- **Terminal:** ANSI-compatible +- **Manual memory management**, no external libraries beyond `ncurses` + +## Controls + +- Arrow keys — Move Pac-Man +- ESC — Quit the game +- Spacebar - Pause + +## Ghost AI Logic + +Each ghost behaves differently depending on the current game mode: + +### Game Modes: + +- **Chase** — Ghosts pursue targets based on individual AI +- **Scatter** — Ghosts retreat to their fixed corners +- **Frightened** — Ghosts move randomly after Pac-Man eats a power pellet +- **Returning** — Ghosts return to their house after being eaten + +### Algorithms Used + +- **BFS (Breadth-First Search)** — used for actual pathfinding on the map +- **Manhattan Distance** — used for heuristic target evaluation +- **Random Choice** — used when ghosts are frightened + +### Ghost Behaviors: + +#### Blinky (Red) +- **Behavior:** Always targets Pac-Man's current tile in chase mode +- **Algorithm:** BFS + +#### Pinky (Pink) +- **Behavior:** Targets the tile 2 cells ahead of Pac-Man in his current direction +- **Algorithm:** Manhattan distance + +#### Inky (Cyan) +- **Behavior:** + + 1. Computes the tile two cells ahead of Pac-Man + 2. Calculates a vector from Blinky's position to that tile + 3. Doubles the vector to determine the target + +- **Algorithm:** Manhattan distance + +#### Clyde (Orange) +- **Behavior:** + + - If farther than 8 tiles from Pac-Man: behaves like Blinky (chases) + - If closer: retreats to scatter corner + +- **Algorithm:** Manhattan distance and BFS hybrid + +## Build Instructions + +Install `ncurses` (if needed): +```bash +sudo apt install libncurses5-dev libncursesw5-dev +``` +Then: + +```bash +git clone https://git.scratko.xyz/pacman +cd pacman +make +./pacman + +``` Windows version: Download diff --git a/field.c b/field.c index fd31606..1db3b72 100644 --- a/field.c +++ b/field.c @@ -295,8 +295,8 @@ enum intersection_type get_intersection(const game_space field, /* * last conditions x-1 == left_outside_tunnel_x and - * x+1 == right_outside_tunnel_x are used by only pacman (while - * cheking remaining direction) + * x+1 == right_outside_tunnel_x are used by only pacman + * because it's only checked for ghosts at intersections. */ struct free_directions find_free_directions(const game_space field, int x, int y) diff --git a/field.h b/field.h index 728d75a..7bce8c5 100644 --- a/field.h +++ b/field.h @@ -16,6 +16,12 @@ enum intersection_type { one_path = '1', two_paths = '2', three_paths = '3', + /* + * Yellow blocks': ghosts can't go up from these tiles during normal + * movement. When entering from left or right, they can only continue + * straight. This restriction is ignored in frightened mode or during + * mode transitions. + */ yellow_block = 'y', direct_path }; diff --git a/ghosts.c b/ghosts.c index 72e4f81..87e2350 100644 --- a/ghosts.c +++ b/ghosts.c @@ -157,6 +157,11 @@ struct coordinates identify_blue_target(struct pacman pac, return result_point; } +/* + * determining the target point for the pink ghost (4 cells in front of + * Pac-Man). + * Also involved in determining the midpoint for the blue ghost + */ struct coordinates identify_target_in_front(struct pacman pac, int shift) { int x, y; @@ -243,6 +248,10 @@ static int is_bfs_search_for_orange(enum search_flag *current_width, return *move_count > orange_search_limit; } +/* + * The search is performed from Pac-Man to the ghost so that at the end + * of the algorithm we immediately know the closest point to the ghost. + */ void breadth_first_search(game_space field, struct ghost_type *ghost, struct coordinates target_point) { @@ -283,6 +292,10 @@ void breadth_first_search(game_space field, struct ghost_type *ghost, if(ghost->color == orange) { if(!is_bfs_search_for_orange(¤t_width, &move_count)) { + /* + * Instead of two_paths, it could be anything except + * direct_path + */ redirect(field, two_paths, ghost, ghost->home_position, compute_distance_between_points); @@ -315,10 +328,14 @@ void breadth_first_search(game_space field, struct ghost_type *ghost, static int standard_distance(int x1, int y1, int x2, int y2) { int dx, dy; + dx = abs(x1 - x2); - dx = dx > field_width ? field_width - dx : dx; + if (dx > field_width / 2) + dx = field_width - dx; + dy = abs(y1 - y2); - return sqrt(dx*dx + dy*dy); + /* Manhattan distance */ + return dx + dy; } static int is_minimal_distance(int min_distance, int tmp_distance, diff --git a/pacman.c b/pacman.c index 15a4b63..eaa453e 100644 --- a/pacman.c +++ b/pacman.c @@ -207,6 +207,7 @@ static void change_mode(struct mode_type *mode_params, struct pacman *pac) mode_params->current_mode = scatter; mode_params->chase_counter = 0; ++mode_params->phase_number; + /* Leave the chase phase until the end of the game */ if(mode_params->phase_number == phase_limit) mode_params->current_mode = chase; if(mode_params->phase_number != phase_limit) @@ -511,6 +512,15 @@ int main() struct mode_type mode_params; struct game_params_type game_options; int key; + /* + * This stores the direction requested by the player (via arrow keys). + * If Pac-Man can't immediately switch to that direction (due to a wall), + * the input is remembered here. On the next iteration, the game checks + * again whether turning in this stored direction is possible, and performs + * the turn if so. This adds a small input buffer window, making controls + * more forgiving: if the player pressed the key slightly early, the turn + * still happens smoothly. + */ enum movement_direction stored_direction; srand(time(NULL)); initscr(); diff --git a/pacman.png b/pacman.png new file mode 100644 index 0000000..c4be68e Binary files /dev/null and b/pacman.png differ -- cgit v1.2.3