back to scratko.xyz
aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorscratko <m@scratko.xyz>2024-04-02 17:43:35 +0300
committerscratko <m@scratko.xyz>2024-04-02 17:43:35 +0300
commitbdee2852c13f6b02ec5207ded584839a3118233e (patch)
tree39f1c14be91fb848ce0f94a64532e48a7667e5de
downloadpacman-bdee2852c13f6b02ec5207ded584839a3118233e.tar.gz
pacman-bdee2852c13f6b02ec5207ded584839a3118233e.tar.bz2
pacman-bdee2852c13f6b02ec5207ded584839a3118233e.zip
Initial commit
-rw-r--r--Makefile19
-rw-r--r--field.c163
-rw-r--r--field.h46
-rw-r--r--ghosts.c140
-rw-r--r--ghosts.h65
-rw-r--r--pac.c113
-rw-r--r--pac.h29
-rw-r--r--pacman.c78
8 files changed, 653 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..8754c3c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+CC=gcc
+CFLAGS=-Wall -g -c
+
+all: pacman
+
+pacman: ghosts.o field.o pac.o pacman.o
+ $(CC) ghosts.o field.o pac.o pacman.o -lncurses -o pacman
+
+field.o: field.c
+ $(CC) $(CFLAGS) field.c
+
+ghosts.o: ghosts.c
+ $(CC) $(CFLAGS) ghosts.c
+
+pacman.o: pacman.c
+ $(CC) $(CFLAGS) pacman.c
+
+pac.o: pac.c
+ $(CC) $(CFLAGS) pac.c
diff --git a/field.c b/field.c
new file mode 100644
index 0000000..df61097
--- /dev/null
+++ b/field.c
@@ -0,0 +1,163 @@
+#include "field.h"
+#include "ghosts.h"
+#include <ncurses.h>
+#include <stdlib.h>
+
+static const char field_sample[field_height][field_width] = {
+ {"////////////////////////////"},
+ {"/1....2.....1//1.....2....1/"},
+ {"/.////./////.//./////.////./"},
+ {"/./ /./ /.//./ /./ /./"},
+ {"/./ /./ /.//./ /./ /./"},
+ {"/.////./////.//./////.////./"},
+ {"/2....3..2..2..2..2..3....2/"},
+ {"/.////.//.////////.//.////./"},
+ {"/.////.//.////////.//.////./"},
+ {"/1....2//1..1//1..1//2....1/"},
+ {"//////.///// // /////.//////"},
+ {" /.///// // /////./ "},
+ {" /.//1 1//./ "},
+ {" /.// ///##/// //./ "},
+ {"//////.// / /2 2//////"},
+ {" 3 2/ / //2 "},
+ {"//////.// //////// //.//////"},
+ {" /.//2 2//./ "},
+ {"//////.// //////// //.//////"},
+ {"/1....3..2..1//1..2..3....1/"},
+ {"/.////./////.//./////.////./"},
+ {"/.////./////.//./////.////./"},
+ {"/1.1//2..2..2..2..2..2//1.1/"},
+ {"///.//.//.////////.//.//.///"},
+ {"///.//.//.////////.//.//.///"},
+ {"/1.2..1//1..1//1..1//1..2.1/"},
+ {"/.//////////.//.//////////./"},
+ {"/1..........2..2..........1/"},
+ {"////////////////////////////"},
+};
+
+static void copy_field(game_space field)
+{
+ int i;
+ for(i = 0; i < field_height; ++i) {
+ int j;
+ for(j = 0; j < field_width; ++j)
+ field[i][j] = field_sample[i][j];
+ }
+}
+
+game_space get_new_field()
+{
+ game_space field = malloc(field_width * field_height);
+ copy_field(field);
+ return field;
+}
+
+static int is_has_point(int i, int j)
+{
+ return !((i == 12 && j == 9) || (i == 12 && j == 18) ||
+ (i == 14 && j == 18) || (i == 15 && j == 9) ||
+ (i == 17 && j == 9) || (i == 17 && j == 18));
+}
+
+void print_field(game_space field)
+{
+ int i;
+ char symbol;
+ for(i = 0; i < field_height; ++i) {
+ int j;
+ for(j = 0; j < field_width; ++j) {
+ symbol = field[i][j];
+ move(i, j);
+ switch(symbol) {
+ case '1':
+ case '2':
+ case '3':
+ if(is_has_point(i, j))
+ addch('.');
+ else
+ addch(' ');
+ break;
+ case '/':
+ addch('/');
+ break;
+ case '.':
+ addch('.');
+ break;
+ case '#':
+ addch('#');
+ break;
+ }
+ refresh();
+ }
+ }
+}
+
+void display_character(int y, int x, int symbol)
+{
+ move(y, x);
+ addch(symbol);
+ refresh();
+}
+
+void display_ghosts_on_field(struct ghost_type *red_ghost,
+ struct ghost_type *pink_ghost,
+ struct ghost_type *blue_ghost,
+ struct ghost_type *orange_ghost)
+{
+ display_character(red_ghost->position.y, red_ghost->position.x, '&');
+ display_character(pink_ghost->position.y, pink_ghost->position.x, '&');
+ display_character(blue_ghost->position.y, blue_ghost->position.x, '&');
+ display_character(orange_ghost->position.y, orange_ghost->position.x, '&');
+}
+
+void clear_symbol(game_space field, int y, int x,
+ enum select_character character)
+{
+ int symbol = field[y][x];
+ move(y, x);
+ if(character == ghost_char) {
+ switch(symbol) {
+ case '#':
+ addch('#');
+ break;
+ case '.':
+ addch('.');
+ break;
+ case ' ':
+ addch(' ');
+ break;
+ }
+ }
+ else if(character == pac_char)
+ addch(' ');
+ refresh();
+}
+
+enum intersection_type get_intersection(const game_space field,
+ struct ghost_type *ghost)
+{
+ int y, x, symbol;
+ y = ghost->position.y;
+ x = ghost->position.x;
+ symbol = field[y][x];
+ switch(symbol) {
+ case one_path:
+ return one_path;
+ case two_paths:
+ return two_paths;
+ case three_paths:
+ return three_paths;
+ default:
+ return direct_path;
+ }
+}
+
+struct free_directions find_free_directions(game_space field, int y, int x)
+{
+ struct free_directions found_paths;
+ found_paths.left = field[y][x-1] != '/' && field[y][x-1] != '#' ? 1 : 0;
+ found_paths.right = field[y][x+1] != '/' && field[y][x+1] != '#' ? 1 : 0;
+ found_paths.up = field[y-1][x] != '/' && field[y-1][x] != '#' ? 1 : 0;
+ found_paths.down = field[y+1][x] != '/' && field[y+1][x] != '#' ? 1 : 0;
+ return found_paths;
+}
diff --git a/field.h b/field.h
new file mode 100644
index 0000000..42992aa
--- /dev/null
+++ b/field.h
@@ -0,0 +1,46 @@
+#ifndef FIELD_H_SENTRY
+#define FIELD_H_SENTRY
+
+enum intersection_type {
+ one_path = '1',
+ two_paths = '2',
+ three_paths = '3',
+ direct_path
+};
+
+enum {
+ field_width = 29,
+ field_height = 29
+};
+
+enum select_character { ghost_char, pac_char };
+
+typedef char (*game_space)[field_width];
+game_space get_new_field();
+
+struct free_directions {
+ int left;
+ int right;
+ int up;
+ int down;
+};
+
+void print_field(game_space field);
+
+void display_character(int y, int x, int symbol);
+
+struct ghost_type;
+void display_ghosts_on_field(struct ghost_type *red_ghost,
+ struct ghost_type *pink_ghost,
+ struct ghost_type *blue_ghost,
+ struct ghost_type *orange_ghost);
+
+void clear_symbol(game_space field, int y, int x,
+ enum select_character character);
+
+enum intersection_type get_intersection(const game_space field,
+ struct ghost_type *ghost);
+
+struct free_directions find_free_directions(game_space field, int y, int x);
+
+#endif
diff --git a/ghosts.c b/ghosts.c
new file mode 100644
index 0000000..2559740
--- /dev/null
+++ b/ghosts.c
@@ -0,0 +1,140 @@
+#include "ghosts.h"
+#include "field.h"
+
+void initialize_ghost(struct ghost_type *ghost, enum ghost_color color)
+{
+ switch(color) {
+ case red:
+ ghost->position.y = red_y;
+ ghost->position.x = red_x;
+ ghost->home_position.y = red_home_y;
+ ghost->home_position.x = red_home_x;
+ ghost->color = red;
+ ghost->mode = scatter;
+ ghost->direction = none;
+ break;
+ case pink:
+ ghost->position.y = pink_y;
+ ghost->position.x = pink_x;
+ ghost->home_position.y = pink_home_y;
+ ghost->home_position.x = pink_home_x;
+ ghost->color = pink;
+ ghost->mode = scatter;
+ ghost->direction = none;
+ break;
+ case blue:
+ ghost->position.y = blue_y;
+ ghost->position.x = blue_x;
+ ghost->home_position.y = blue_home_y;
+ ghost->home_position.x = blue_home_x;
+ ghost->color = blue;
+ ghost->mode = scatter;
+ ghost->direction = none;
+ break;
+ case orange:
+ ghost->position.y = orange_y;
+ ghost->position.x = orange_x;
+ ghost->home_position.y = orange_home_y;
+ ghost->home_position.x = orange_home_x;
+ ghost->color = orange;
+ ghost->mode = scatter;
+ ghost->direction = none;
+ break;
+ }
+}
+
+void pull_out_ghosts(int *get_out_stage,
+ struct ghost_type *red_ghost,
+ struct ghost_type *pink_ghost,
+ struct ghost_type *blue_ghost,
+ struct ghost_type *orange_ghost)
+{
+ enum { final_stage = 1 };
+ enum movement_direction red_moves[] = { none, right, up, up, left };
+ enum movement_direction blue_moves[] = { none, left, up, up, left };
+ enum movement_direction pink_moves[] = { right, up, up, up, right };
+ enum movement_direction orange_moves[] = { left, up, up, up, right };
+ int index = *get_out_stage - 1;
+ if(*get_out_stage > final_stage) {
+ red_ghost->direction = red_moves[index];
+ blue_ghost->direction = blue_moves[index];
+ }
+ pink_ghost->direction = pink_moves[index];
+ orange_ghost->direction = orange_moves[index];
+ --*get_out_stage;
+}
+
+static void change_position(struct ghost_type *ghost,
+ enum movement_direction direction)
+{
+ switch(direction) {
+ case left:
+ --ghost->position.x;
+ return;
+ case right:
+ ++ghost->position.x;
+ return;
+ case up:
+ --ghost->position.y;
+ return;
+ case down:
+ ++ghost->position.y;
+ return;
+ default:
+ return;
+ }
+}
+
+void make_ghost_moves(game_space field,
+ struct ghost_type *red_ghost,
+ struct ghost_type *pink_ghost,
+ struct ghost_type *blue_ghost,
+ struct ghost_type *orange_ghost)
+{
+ clear_symbol(field, red_ghost->position.y, red_ghost->position.x,
+ ghost_char);
+ clear_symbol(field, pink_ghost->position.y, pink_ghost->position.x,
+ ghost_char);
+ clear_symbol(field, blue_ghost->position.y, blue_ghost->position.x,
+ ghost_char);
+ clear_symbol(field, orange_ghost->position.y, orange_ghost->position.x,
+ ghost_char);
+ change_position(red_ghost, red_ghost->direction);
+ change_position(pink_ghost, pink_ghost->direction);
+ change_position(blue_ghost, blue_ghost->direction);
+ change_position(orange_ghost, orange_ghost->direction);
+}
+
+static void pave_only_way(game_space field, struct ghost_type *ghost)
+{
+ struct free_directions path = find_free_directions(field, ghost->position.y,
+ ghost->position.x);
+ enum movement_direction reverse_direction = ghost->direction;
+ reverse_direction ^= 1;
+ if(path.left && left != reverse_direction)
+ ghost->direction = left;
+ else if(path.right && right != reverse_direction)
+ ghost->direction = right;
+ else if(path.up && up != reverse_direction)
+ ghost->direction = up;
+ else if(path.down && down != reverse_direction)
+ ghost->direction = down;
+}
+
+void redirect(game_space field, enum intersection_type paths,
+ struct ghost_type *ghost,
+ void (*pathfinder)(game_space field, enum intersection_type paths,
+ struct ghost_type *ghost))
+{
+ if(paths == one_path)
+ pave_only_way(field, ghost);
+ else if(paths == two_paths || paths == three_paths)
+ pathfinder(field, paths, ghost);
+}
+
+
+void breadth_first_search(game_space field, enum intersection_type paths,
+ struct ghost_type *ghost)
+{
+
+}
diff --git a/ghosts.h b/ghosts.h
new file mode 100644
index 0000000..405a5e2
--- /dev/null
+++ b/ghosts.h
@@ -0,0 +1,65 @@
+#ifndef GHOSTS_H_SENTRY
+#define GHOSTS_H_SENTRY
+
+#include "field.h"
+
+enum {
+ red_y = 14,
+ red_x = 15,
+ red_home_y = -1,
+ red_home_x = 25,
+ pink_y = red_y + 1,
+ pink_x = red_x - 2,
+ pink_home_y = red_home_y,
+ pink_home_x = 1,
+ blue_y = red_y,
+ blue_x = red_x-1,
+ blue_home_y = 29,
+ blue_home_x = 27,
+ orange_y = red_y + 1,
+ orange_x = red_x - 3,
+ orange_home_y = blue_home_y,
+ orange_home_x = 0,
+ field_size = 29
+};
+
+enum ghost_color { red, pink, blue, orange };
+enum behavior_mode { chase, scatter, frightened };
+enum movement_direction { left, right, up, down, none };
+
+struct coordinates {
+ int y;
+ int x;
+};
+
+struct ghost_type {
+ struct coordinates position;
+ struct coordinates home_position;
+ enum ghost_color color;
+ enum behavior_mode mode;
+ enum movement_direction direction;
+};
+
+void initialize_ghost(struct ghost_type *ghost, enum ghost_color color);
+
+void pull_out_ghosts(int *get_out_stage,
+ struct ghost_type *red_ghost,
+ struct ghost_type *pink_ghost,
+ struct ghost_type *blue_ghost,
+ struct ghost_type *orange_ghost);
+
+void make_ghost_moves(game_space field,
+ struct ghost_type *red_ghost,
+ struct ghost_type *pink_ghost,
+ struct ghost_type *blue_ghost,
+ struct ghost_type *orange_ghost);
+
+void redirect(game_space field, enum intersection_type paths,
+ struct ghost_type *ghost,
+ void (*pathfinder)(game_space field, enum intersection_type paths,
+ struct ghost_type *ghost));
+
+void breadth_first_search(game_space field, enum intersection_type paths,
+ struct ghost_type *ghost);
+
+#endif
diff --git a/pac.c b/pac.c
new file mode 100644
index 0000000..50256d8
--- /dev/null
+++ b/pac.c
@@ -0,0 +1,113 @@
+#include "pac.h"
+#include "ncurses.h"
+
+void initialize_pac(struct pacman *pac)
+{
+ pac->lives = max_live;
+ pac->points_eaten = 0;
+ pac->position.y = pac_y;
+ pac->position.x = pac_x;
+ pac->direction = none;
+}
+
+static enum movement_direction get_correct_path(struct pacman *pac,
+ struct free_directions
+ free_path,
+ enum movement_direction
+ direction)
+{
+ switch(direction) {
+ case left:
+ return free_path.left ? left : none;
+ case right:
+ return free_path.right ? right : none;
+ case up:
+ return free_path.up ? up : none;
+ case down:
+ return free_path.down ? down : none;
+ default:
+ return none;
+ }
+
+}
+
+void check_remaining_direction(game_space field, struct pacman *pac,
+ enum movement_direction *stored_direction)
+{
+ enum movement_direction temp_direction;
+ struct free_directions free_path =
+ find_free_directions(field, pac->position.y, pac->position.x);
+ enum movement_direction current_direction =
+ stored_direction ? *stored_direction : pac->direction;
+ temp_direction = get_correct_path(pac, free_path, current_direction);
+ if(temp_direction != none)
+ pac->direction = temp_direction;
+ else {
+ current_direction = pac->direction;
+ pac->direction = get_correct_path(pac, free_path, current_direction);
+ }
+ if(stored_direction)
+ *stored_direction = none;
+}
+
+void change_pac_direction(game_space field, struct pacman *pac, int key,
+ enum movement_direction *stored_direction)
+{
+ enum state { no, yes } is_changed_direction;
+ is_changed_direction = no;
+ struct free_directions path = find_free_directions(field, pac->position.y,
+ pac->position.x);
+ switch(key) {
+ case KEY_LEFT:
+ if(path.left) {
+ pac->direction = left;
+ is_changed_direction = yes;
+ } else
+ *stored_direction = left;
+ break;
+ case KEY_RIGHT:
+ if(key == KEY_RIGHT && path.right) {
+ pac->direction = right;
+ is_changed_direction = yes;
+ } else
+ *stored_direction = right;
+ break;
+ case KEY_UP:
+ if(key == KEY_UP && path.up) {
+ pac->direction = up;
+ is_changed_direction = yes;
+ } else
+ *stored_direction = up;
+ break;
+ case KEY_DOWN:
+ if(key == KEY_DOWN && path.down) {
+ pac->direction = down;
+ is_changed_direction = yes;
+ } else
+ *stored_direction = down;
+ break;
+ }
+ if(is_changed_direction == no)
+ check_remaining_direction(field, pac, NULL);
+}
+
+void make_pac_move(game_space field, struct pacman *pac)
+{
+ clear_symbol(field, pac->position.y, pac->position.x, pac_char);
+ switch(pac->direction) {
+ case left:
+ --pac->position.x;
+ return;
+ case right:
+ ++pac->position.x;
+ return;
+ case up:
+ --pac->position.y;
+ return;
+ case down:
+ ++pac->position.y;
+ return;
+ default:
+ return;
+ }
+}
diff --git a/pac.h b/pac.h
new file mode 100644
index 0000000..1125694
--- /dev/null
+++ b/pac.h
@@ -0,0 +1,29 @@
+#ifndef PAC_H_SENTRY
+#define PAC_H_SENTRY
+
+#include "ghosts.h"
+
+enum {
+ max_live = 3,
+ pac_y = 22,
+ pac_x = 14
+};
+
+struct pacman {
+ char lives;
+ unsigned char points_eaten;
+ struct coordinates position;
+ enum movement_direction direction;
+};
+
+void initialize_pac(struct pacman *pac);
+
+void change_pac_direction(game_space field, struct pacman *pac, int key,
+ enum movement_direction *stored_direction);
+
+void check_remaining_direction(game_space field, struct pacman *pac,
+ enum movement_direction *stored_direction);
+
+void make_pac_move(game_space field, struct pacman *pac);
+
+#endif
diff --git a/pacman.c b/pacman.c
new file mode 100644
index 0000000..c3225b2
--- /dev/null
+++ b/pacman.c
@@ -0,0 +1,78 @@
+#include "field.h"
+#include "ghosts.h"
+#include "pac.h"
+#include <ncurses.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+enum {
+ timeout_duration = 0,
+ sleep_duration = 190000,
+ key_escape = 27,
+ count_get_out_moves = 5
+};
+
+static void pathfinder_stage(game_space field, struct ghost_type *red_ghost,
+ struct ghost_type *pink_ghost,
+ struct ghost_type *blue_ghost,
+ struct ghost_type *orange_ghost)
+{
+ enum intersection_type intersection;
+ intersection = get_intersection(field, red_ghost);
+ if(intersection != direct_path)
+ redirect(field, intersection, red_ghost, breadth_first_search);
+}
+
+int main()
+{
+ game_space field = NULL;
+ struct ghost_type red_ghost, pink_ghost, blue_ghost, orange_ghost;
+ struct pacman pac;
+ int key, get_out_stage;
+ enum movement_direction stored_direction;
+ stored_direction = none;
+ get_out_stage = count_get_out_moves;
+ initscr();
+ cbreak();
+ noecho();
+ curs_set(0);
+ keypad(stdscr, 1);
+ timeout(timeout_duration);
+ start_color();
+ field = get_new_field();
+ print_field(field);
+ initialize_ghost(&red_ghost, red);
+ initialize_ghost(&pink_ghost, pink);
+ initialize_ghost(&blue_ghost, blue);
+ initialize_ghost(&orange_ghost, orange);
+ initialize_pac(&pac);
+ display_ghosts_on_field(&red_ghost, &pink_ghost, &blue_ghost,
+ &orange_ghost);
+ display_character(pac.position.y, pac.position.x, 'C');
+ usleep(sleep_duration);
+ while((key = getch()) != key_escape) {
+ if(get_out_stage)
+ pull_out_ghosts(&get_out_stage, &red_ghost, &pink_ghost,
+ &blue_ghost, &orange_ghost);
+ else
+ pathfinder_stage(field, &red_ghost, &pink_ghost, &blue_ghost,
+ &orange_ghost);
+ if(key != ERR)
+ change_pac_direction(field, &pac, key, &stored_direction);
+ else {
+ if(stored_direction != none)
+ check_remaining_direction(field, &pac, &stored_direction);
+ else
+ check_remaining_direction(field, &pac, NULL);
+ }
+ make_ghost_moves(field, &red_ghost, &pink_ghost, &blue_ghost,
+ &orange_ghost);
+ make_pac_move(field, &pac);
+ display_ghosts_on_field(&red_ghost, &pink_ghost, &blue_ghost,
+ &orange_ghost);
+ display_character(pac.position.y, pac.position.x, 'C');
+ usleep(sleep_duration);
+ }
+ endwin();
+ return 0;
+}