back to scratko.xyz
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md98
-rw-r--r--field.c4
-rw-r--r--field.h6
-rw-r--r--ghosts.c21
-rw-r--r--pacman.c10
-rw-r--r--pacman.pngbin0 -> 12471 bytes
6 files changed, 135 insertions, 4 deletions
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.
+
+<img src = "pacman.png" />
+
+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: <a href="https://scratko.xyz/games/pacman.exe" target="_blank">Download</a>
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(&current_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
--- /dev/null
+++ b/pacman.png
Binary files differ