diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60d4bfa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea/ +cmake-build-*/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ae687a8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.22.1) +project(PacXon C) + +set(CMAKE_C_STANDARD 11) + +find_package(Curses REQUIRED) +include_directories(${CURSES_INCLUDE_DIR}) + +add_executable(PacXon main.c) +target_link_libraries(PacXon curses) \ No newline at end of file diff --git a/PacXon.cbp b/PacXon.cbp deleted file mode 100755 index 87d0603..0000000 --- a/PacXon.cbp +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - diff --git a/README.md b/README.md index 10987f4..0bff3b1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # PacXon -A partial PacXon game clone in C & Windows cmd.exe (you can play the original version [here](https://www.pacxon.net/)). +A partial PacXon game clone in C, on CLI (you can play the original version [here](https://www.pacxon.net/)). This game was my final project for the **computer fundamentals and programming** university course, made in **early 2019**. @@ -8,6 +8,22 @@ This game was my final project for the **computer fundamentals and programming** ## Simple build & run -Open the project in Code::Blocks 17.12 (with MinGW) on a Windows OS, and hit Build and run. +> Pro-tip: use an **IDE** to build and run the project. -> Running the game might be tricky; because it is left behind as a **legacy**, and it is not packaged properly. \ No newline at end of file +### Ubuntu + +> Tested on Ubuntu 22.04 LTS. + +- `sudo apt install cmake gcc make libncurses-dev`. +- `cmake . && make && ./PacXon` + +Or + +- `sudo apt install gcc libncurses-dev` +- `gcc main.c -lncurses && ./a.out` + +### Other Operating Systems, including Windows + +Figure it out yourself :slightly_smiling_face:. + +> Fow Windows, you can check out earlier commit [765f67781980fb90d9b2bdfef960878687298926](https://github.com/agcom/pacxon/tree/765f67781980fb90d9b2bdfef960878687298926) and follow the instructions there. \ No newline at end of file diff --git a/dir.c b/dir.c new file mode 100644 index 0000000..d7f78ce --- /dev/null +++ b/dir.c @@ -0,0 +1,69 @@ +#ifndef DIR_C_INCLUDED +#define DIR_C_INCLUDED + +#include +#include "utils.c" + +// Directions' masks +#define VERTICAL 0b0001 +#define HORIZONTAL 0b0010 +#define LEFT 0b0100 +#define UP 0b1000 + +typedef int dir_t; + +// Clock-wise order +const dir_t all_directions[8] = { + VERTICAL | UP, // UP + HORIZONTAL | VERTICAL | UP | !LEFT, // Up Right + HORIZONTAL | !LEFT, // Right + HORIZONTAL | VERTICAL | !UP | !LEFT, // Down Right + VERTICAL | !UP, // Down + HORIZONTAL | VERTICAL | !UP | LEFT, // Down Left + HORIZONTAL | LEFT, // Left + HORIZONTAL | VERTICAL | UP | LEFT // Up Left +}; + +void print_dir(const dir_t dir) { + bool is_vertical_printed = false; + + if (dir & VERTICAL) { + if (dir & UP) printf("Up"); + else printf("Down"); + + is_vertical_printed = true; + } + + if (dir & HORIZONTAL) { + if (is_vertical_printed) printf(" "); + + if (dir & LEFT) printf("Left"); + else printf("Right"); + } +} + +void print_dir_arr(const int size, const dir_t dirs[size]) { + int i; + for (i = 0; i < size; i++) { + print_dir(dirs[i]); + if (i != size - 1) printf(", "); + } +} + +int turn_dir(dir_t dir, const bool clockwise, const int steps) { + + // Find its index at directions + int index; + if ((index = linear_int_arr_search(8, all_directions, dir)) == -1) + return SHOULD_NOT_REACH_HERE; // Not a direction + + int i; + for (i = 0; i < steps; i++) { + if (clockwise) dir = all_directions[index == 7 ? 0 : index + 1]; + else dir = all_directions[index == 0 ? 7 : index - 1]; + } + + return dir; +} + +#endif // DIR_C_INCLUDED \ No newline at end of file diff --git a/ghosts.c b/ghosts.c index 075fd70..02c4261 100755 --- a/ghosts.c +++ b/ghosts.c @@ -1 +1,30 @@ -#include "ghosts.h" +#ifndef GHOSTS_C_INCLUDED +#define GHOSTS_C_INCLUDED + +#include "loc.c" + +// Ghosts type coding +typedef enum { + FLOAT_GHOST, RAIL_GHOST, BRICK_ZONE_GHOST +} ghost_type_t; + +// Shapes +#define FLOAT_GHOST_CHAR "α" +#define RAIL_GHOST_CHAR "ß" +#define BRICK_ZONE_GHOST_CHAR "δ" + +// Locations needed sizes +#define FLOAT_GHOST_LOCS_SIZE 1 +#define BRICK_ZONE_GHOST_LOCS_SIZE 1 +#define RAIL_GHOST_LOCS_SIZE 2 + +#define MAX_GHOST_LOCS_SIZE 2 + +typedef struct { + ghost_type_t type; + int locs_size; + loc_t locs[MAX_GHOST_LOCS_SIZE]; + int movement; +} ghost_t; + +#endif // GHOSTS_C_INCLUDED \ No newline at end of file diff --git a/ghosts.h b/ghosts.h deleted file mode 100755 index 5e8116b..0000000 --- a/ghosts.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef GHOSTS_H_INCLUDED -#define GHOSTS_H_INCLUDED - -#include "utils.h" - -//ghosts type coding -#define FLOAT_GHOST 0 -#define RAIL_GHOST 1 -#define BRICK_ZONE_GHOST 2 - -//shapes -#define FLOAT_GHOST_CHAR 224 -#define RAIL_GHOST_CHAR 225 -#define BRICK_ZONE_GHOST_CHAR 235 - -#define A_GHOST_MAX_LOCATIONS 10 - -#define FLOAT_GHOST_LOCATIONSS 1 -#define BRICK_ZONE_GHOST_LOCATIONSS 1 -#define RAIL_GHOST_LOCATIONSS 2 - -//a ghost -typedef struct { - - int type; - int locationss; - Location locations[A_GHOST_MAX_LOCATIONS]; - int movement; - -} Ghost; - -#endif // GHOSTS_H_INCLUDED diff --git a/kbhit.c b/kbhit.c new file mode 100644 index 0000000..0bffa06 --- /dev/null +++ b/kbhit.c @@ -0,0 +1,14 @@ +#ifndef KBHIT_C_INCLUDED +#define KBHIT_C_INCLUDED + +#include + +int kbhit() { + const int c = getch(); + if (c != ERR) { + ungetch(c); + return 1; + } else return 0; +} + +#endif // KBHIT_C_INCLUDED \ No newline at end of file diff --git a/loc.c b/loc.c new file mode 100644 index 0000000..b02aa2a --- /dev/null +++ b/loc.c @@ -0,0 +1,106 @@ +#ifndef LOC_C_INCLUDED +#define LOC_C_INCLUDED + +#include +#include "dir.c" +#include "utils.c" + +// x = row & y = column +typedef struct { + int x, y; +} loc_t; + +void rand_loc(loc_t *p, const int min_x, const int max_x, const int min_y, const int max_y) { + p->x = min_x + rand() % (max_x - min_x + 1); + p->y = min_y + rand() % (max_y - min_y + 1); +} + +void next_loc(loc_t *next, const dir_t dir) { + if (dir & VERTICAL) { + if (dir & UP) next->x--; + else next->x++; + } + + if (dir & HORIZONTAL) { + if (dir & LEFT) next->y--; + else next->y++; + } +} + +int reverse_dir(dir_t dir) { + if (dir & HORIZONTAL) dir ^= LEFT; + if (dir & VERTICAL) dir ^= UP; + + return dir; +} + +int rand_dir(const bool is_diagonal) { + if (is_diagonal) return VERTICAL | HORIZONTAL | (rand_bool() ? LEFT : !LEFT) | (rand_bool() ? UP : !UP); + else { + const int rnd = rand() % 4; + return rnd == 0 ? (HORIZONTAL | LEFT) : + rnd == 1 ? (VERTICAL | UP) : + rnd == 2 ? (HORIZONTAL | !LEFT) : + rnd == 3 ? (VERTICAL | !UP) : SHOULD_NOT_REACH_HERE; + } + +} + +bool are_equal_locs(const loc_t p1, const loc_t p2) { + return p1.x == p2.x && p1.y == p2.y; +} + +bool are_neighbour_locs(const loc_t p1, const loc_t p2) { + if (are_equal_locs(p1, p2)) return false; + + int i; + for (i = 0; i < 8; i++) { + loc_t t = p2; + next_loc(&t, all_directions[i]); + if (are_equal_locs(p1, t)) return true; + } + + return false; +} + +void locs_diff_vector(const loc_t p1, const loc_t p2, loc_t *res) { + res->x = p2.x - p1.x; + res->y = p2.y - p1.y; +} + +int loc_vector_to_dir(const loc_t vector) { + if (vector.x < 0 && vector.y < 0) return VERTICAL | HORIZONTAL | LEFT | UP; // Up Left + else if (vector.x < 0 && vector.y == 0) return VERTICAL | UP; // Up + else if (vector.x < 0 && vector.y > 0) return VERTICAL | HORIZONTAL | !LEFT | UP; // Up Right + else if (vector.x == 0 && vector.y < 0) return HORIZONTAL | LEFT; // Left + else if (vector.x == 0 && vector.y > 0) return HORIZONTAL | !LEFT; // Right + else if (vector.x > 0 && vector.y < 0) return HORIZONTAL | VERTICAL | !UP | LEFT; // Down Left + else if (vector.x > 0 && vector.y == 0) return VERTICAL | !UP; // Down + else if (vector.x > 0 && vector.y > 0) return VERTICAL | HORIZONTAL | !UP | !LEFT; // Down Right + else return SHOULD_NOT_REACH_HERE; // Zero vector +} + +bool are_size_by_size_locs(const loc_t l1, const loc_t l2, const bool check_being_neighbors) { + if (check_being_neighbors && !are_neighbour_locs(l1, l2)) return false; + + loc_t vector; + locs_diff_vector(l1, l2, &vector); + + const int diff_dir = loc_vector_to_dir(vector); + if (diff_dir & VERTICAL && diff_dir & HORIZONTAL) return false; + else return true; +} + +void print_loc(const loc_t p) { + printf("(%d, %d)", p.x, p.y); +} + +void print_loc_arr(const int size, const loc_t locs[size]) { + int i; + for (i = 0; i < size; i++) { + print_loc(locs[i]); + printf(" "); + } +} + +#endif // LOC_C_INCLUDED \ No newline at end of file diff --git a/main.c b/main.c index df956e7..23c55cc 100755 --- a/main.c +++ b/main.c @@ -10,1008 +10,704 @@ to dos : - save and load - levels - menu - - reflectDirection method + - reflect_dir method (int reflect_dir(const dir_t dir, const int mirror_pos) - colored char struct **/ #include #include -#include -#include "utils.h" -#include "ghosts.h" -#include "pacman.h" +#include +#include +#include "utils.c" +#include "ghosts.c" +#include "pacman.c" +#include "kbhit.c" +#include "dir.c" +#include "loc.c" -#define MAX_GHOSTS 100 //maximum capacity of the ghosts array +#define MAX_GHOSTS 100 // Maximum capacity of the ghosts array -#define REFRESH_RATE 120 //Hz; affects velocity of things in game (pac man, ghosts) +#define REFRESH_RATE 30 // In Hzs; affects velocity of things in game (pac-man and ghosts). -//shapes -#define BRICK_CHAR 178 +// Shapes +#define BRICK_CHAR "▓" -//the whole game boar dimensions +// The whole game board dimensions #define ROWS 30 #define COLUMNS 70 -#define WIN_PROGRESS 80 //win condition +#define WIN_PROGRESS 80 // Win percentage -//global variables -int float_ghosts = 2; -int rail_ghosts = 1; -int brick_zone_ghosts = 0; -int ghosts_quantity; //count of active ghosts -Ghost ghosts[MAX_GHOSTS]; //ghosts pool -int bricks[ROWS][COLUMNS]; //-1 for bricks, >= 0 for zones -int view_validation = 0; //the drawn picture is the current state or not, if not redraw it -PacMan pm; //the hero -char picture[ROWS][COLUMNS]; //the whole things view except the header -int score = 0; //score -int progress = 0; //current progress; from 100 -int ghosts_speed_ratio = 3; //ghosts speed = pac man speed / this ratio +// Ghosts quantity +#define FLOAT_GHOSTS_COUNT 2 +#define RAIL_GHOSTS_COUNT 1 +#define BRICK_ZONE_GHOSTS_COUNT 0 +#define GHOSTS_SIZE FLOAT_GHOSTS_COUNT + RAIL_GHOSTS_COUNT + BRICK_ZONE_GHOSTS_COUNT // Count of active ghosts +#define GHOSTS_SPEED_RATIO 3 // Ghosts' speed = pac-man's speed / this ratio -//how ghosts start -//f = count of float ghosts -//r = count of rail ghosts -//b = count of brick zone ghosts -//f + r + b == ghosts_quantity -void initGhosts() { +ghost_t ghosts[MAX_GHOSTS]; // Ghosts pool - int append_index = 0; +int bricks[ROWS][COLUMNS]; // -1 for bricks and >=0 for zones - int i; - for(i = 0; i < float_ghosts; i++) { //float ghosts +pac_man pm; // The hero - addGhost(FLOAT_GHOST, append_index); - append_index++; +char *picture[ROWS][COLUMNS]; // The whole things view except the header +bool is_view_update = false; // The drawn picture is the current state or not - } - - for(i = 0; i < rail_ghosts; i++) { //rail ghosts - - addGhost(RAIL_GHOST, append_index); - append_index++; - - } - - for(i = 0; i < brick_zone_ghosts; i++) { //brick zone ghosts - - addGhost(BRICK_ZONE_GHOST, append_index); - append_index++; - - } +int score = 0; +int progress = 0; // Current progress; from 100. +bool is_brick_there(const loc_t loc) { + // Check location to be in bounds + if (loc.x < 0 || loc.x > ROWS - 1 || loc.y < 0 || loc.y > COLUMNS - 1) return false; // Out of bounds location + return bricks[loc.x][loc.y] < 0; } -//for initGhosts method -void addGhost(int type, int index) { - - ghosts[index].type = type; - - switch(type) { - - case FLOAT_GHOST : { - - ghosts[index].locationss = 1; - randPoint(&(ghosts[index].locations[0]), 1, ROWS - 2, 1, COLUMNS - 2); - ghosts[index].movement = randDirection(1); - - break; - } - - case RAIL_GHOST : { - - ghosts[index].locationss = 2; - - int bricks_count = (ROWS * 2 + COLUMNS * 2) - 4; //count of bricks at the start of the game - - int rand_room = rand() % bricks_count; - - //rand_room to location - int i, counter = 0; - for(i = 0; i < ROWS; i++) { - - int j; - for(j = 0; j < COLUMNS; j++) { - - if(bricks[i][j] == -1) { - - if(counter == rand_room) { - - ghosts[index].locations[0].x = i; - ghosts[index].locations[0].y = j; - - //break both loops - i = ROWS; - break; - - } - - counter++; - - } - - } - - } - - //choose a random possible direction - int possiblitites[2]; - int possiblitites_append = 0; - for(i = 0; i < 8; i++) { - - Location t = ghosts[index].locations[0]; - nextLocation(&t, directions[i]); - - if(isBrickThere(t)) { - - possiblitites[possiblitites_append++] = directions[i]; - - printf("%d", possiblitites_append); - - } - - } - - ghosts[index].movement = possiblitites[rand()%2]; - - ghosts[index].locations[1] = ghosts[index].locations[0]; - - if(ghosts[index].movement & VERTICAL) { //vertical - - //left or right - if(ghosts[index].locations[0].y == 0) { //right - - nextLocation(&ghosts[index].locations[1], HORIZONTAL | !LEFT); - - } else { //left - - nextLocation(&ghosts[index].locations[1], HORIZONTAL | LEFT); - - } - - } else { //horizontal - - //up or down - if(ghosts[index].locations[0].x == 0) { //down - - nextLocation(&ghosts[index].locations[1], VERTICAL | !UP); - - } else { //up - - nextLocation(&ghosts[index].locations[1], VERTICAL | UP); - - } - - } - - break; - - } - - case BRICK_ZONE_GHOST : { - - //should initialize the location after the first zone captured - ghosts[index].locationss = 1; - ghosts[index].locations[0].x = -1; - ghosts[index].locations[0].y = -1; - ghosts[index].movement = randDirection(1); - - break; - } - - } - - index++; - +// For initGhosts method +void add_ghost(const int type, const int index) { + ghosts[index].type = type; + + switch (type) { + case FLOAT_GHOST : { + ghosts[index].locs_size = 1; + rand_loc(&(ghosts[index].locs[0]), 1, ROWS - 2, 1, COLUMNS - 2); + ghosts[index].movement = rand_dir(1); + + break; + } + case RAIL_GHOST : { + ghosts[index].locs_size = 2; + + const int bricks_count = (ROWS * 2 + COLUMNS * 2) - 4; // Count of bricks at the start of the game + const int rand_brick = rand() % bricks_count; + + // rand_room to location + int i, counter = 0; + for (i = 0; i < ROWS; i++) { + int j; + for (j = 0; j < COLUMNS; j++) { + if (bricks[i][j] == -1) { + if (counter == rand_brick) { + ghosts[index].locs[0].x = i; + ghosts[index].locs[0].y = j; + + // Break both loops + i = ROWS; + break; + } + counter++; + } + } + } + + // Choose a random possible direction + int possibilities[2]; + int possibilities_append_index = 0; + for (i = 0; i < 8; i++) { + loc_t t = ghosts[index].locs[0]; + next_loc(&t, all_directions[i]); + + if (is_brick_there(t)) { + possibilities[possibilities_append_index++] = all_directions[i]; + } + } + + ghosts[index].movement = possibilities[rand() % 2]; + ghosts[index].locs[1] = ghosts[index].locs[0]; + + if (ghosts[index].movement & VERTICAL) { // Vertical: Left or Right + if (ghosts[index].locs[0].y == 0) { // Right + next_loc(&ghosts[index].locs[1], HORIZONTAL | !LEFT); + } else { // Left + next_loc(&ghosts[index].locs[1], HORIZONTAL | LEFT); + } + } else { // Horizontal: Up or Down + if (ghosts[index].locs[0].x == 0) { // Down + next_loc(&ghosts[index].locs[1], VERTICAL | !UP); + } else { // Up + next_loc(&ghosts[index].locs[1], VERTICAL | UP); + } + } + + break; + } + + case BRICK_ZONE_GHOST : { + // Should initialize the location after the first zone captured + ghosts[index].locs_size = 1; + ghosts[index].locs[0].x = -1; + ghosts[index].locs[0].y = -1; + ghosts[index].movement = rand_dir(1); + + break; + } + + } } -//how pac man starts -void initPacMan() { - - pm.location.x = 0; - pm.location.y = 0; - - pm.movement = -1; - pm.lives = 3; - +void init_ghosts() { + int append_index = 0; + + int i; + for (i = 0; i < FLOAT_GHOSTS_COUNT; i++) { + add_ghost(FLOAT_GHOST, append_index); + append_index++; + } + + for (i = 0; i < RAIL_GHOSTS_COUNT; i++) { + add_ghost(RAIL_GHOST, append_index); + append_index++; + } + + for (i = 0; i < BRICK_ZONE_GHOSTS_COUNT; i++) { + add_ghost(BRICK_ZONE_GHOST, append_index); + append_index++; + } } -//bricks layout at the start -void initBricks() { - - int i; - for(i = 0; i < ROWS; i++) { - - int j; - for(j = 0; j < COLUMNS; j++) { - - if(i == 0 || i == ROWS - 1 || j == 0 || j == COLUMNS - 1) bricks[i][j] = -1; - else bricks[i][j] = 0; //first zone - - } - - } - +void init_pac_man() { + pm.location.x = 0; + pm.location.y = 0; + + pm.movement = -1; + pm.lives = 3; } -//draws whole things -void drawGame() { - - if(view_validation) return; - - updatePicture(); - - cls(); - - //print the header - drawHeader(); - - //print the picture - print2DCharArray(ROWS, COLUMNS, picture); - +// Initial bricks layout +void init_bricks() { + int i; + for (i = 0; i < ROWS; i++) { + int j; + for (j = 0; j < COLUMNS; j++) { + if (i == 0 || i == ROWS - 1 || j == 0 || j == COLUMNS - 1) bricks[i][j] = -1; + else bricks[i][j] = 0; // First zone + } + } } -void init() { - - ghosts_quantity = float_ghosts + rail_ghosts + brick_zone_ghosts; - - initBricks(); - initGhosts(); - initPacMan(); - +void update_picture() { + int i; + for (i = 0; i < ROWS; i++) { + int j; + for (j = 0; j < COLUMNS; j++) { + if (bricks[i][j] == -1) picture[i][j] = BRICK_CHAR; + else if (bricks[i][j] == -2) picture[i][j] = PAC_MAN_TRACE_CHAR; + else picture[i][j] = " "; // Empty + } + } + + // Add pac-man + picture[pm.location.x][pm.location.y] = PAC_MAN_CHAR; + + // Add ghosts + for (i = 0; i < GHOSTS_SIZE; i++) { + int j; + for (j = 0; j < ghosts[i].locs_size; j++) { + picture[ghosts[i].locs[j].x][ghosts[i].locs[j].y] = ( + ghosts[i].type == FLOAT_GHOST ? FLOAT_GHOST_CHAR : + ghosts[i].type == RAIL_GHOST ? RAIL_GHOST_CHAR : + ghosts[i].type == BRICK_ZONE_GHOST ? BRICK_ZONE_GHOST_CHAR : + "" + ); + } + } + + is_view_update = true; } -void updatePicture() { - - int i; - for(i = 0; i < ROWS; i++) { - - int j; - for(j = 0; j < COLUMNS; j++) { - - if(bricks[i][j] == -1) picture[i][j] = BRICK_CHAR; //brick here - else if(bricks[i][j] == -2) picture[i][j] = PAC_MAN_TRACE_CHAR;//pacman trace - else picture[i][j] = ' '; //empty - - } - - } - - //add pac man - picture[pm.location.x][pm.location.y] = PAC_MAN_CHAR; - - //add ghosts - for(i = 0; i < ghosts_quantity; i++) { - - int j; - for(j = 0; j < ghosts[i].locationss; j++) { - - picture[ghosts[i].locations[j].x][ghosts[i].locations[j].y] = (ghosts[i].type == FLOAT_GHOST ? FLOAT_GHOST_CHAR : ghosts[i].type == RAIL_GHOST ? RAIL_GHOST_CHAR : ghosts[i].type == BRICK_ZONE_GHOST ? BRICK_ZONE_GHOST_CHAR : SHOULD_NOT_REACH_HERE); - - } - - - } - - view_validation = 1; - +void draw_header() { + printf("Lives: %d\tScore: %d\tProgress: %d/%d%%\r\n", pm.lives, score, progress, WIN_PROGRESS); } -void drawHeader() { - - printf("Lives: %d\tScore: %d\tProgress: %d/%d%c\n", pm.lives, score, progress, WIN_PROGRESS, PERCENT_CHAR); - +// Draws everything +void draw_game() { + if (is_view_update) return; + + update_picture(); + clear_screen(); + draw_header(); + print_2d_str_arr(ROWS, COLUMNS, picture); } -void controlAutoMoves() { - - moveGhosts(); - - if(pm.movement != -1) movePacMan(pm.movement, 1); - +void init() { + init_bricks(); + init_ghosts(); + init_pac_man(); } -//controls ghosts movement -void moveGhosts() { - - static int called_times = 0; - called_times++; - - if(called_times == ghosts_speed_ratio) { - - int i; - for(i = 0; i < ghosts_quantity; i++) { - - moveGhost(i); - - } - - called_times = 0; - - } - +void move_ghost(const int index) { + switch (ghosts[index].type) { + case FLOAT_GHOST : { + if (ghosts[index].movement == -1) break; + + loc_t next = ghosts[index].locs[0]; + next_loc(&next, ghosts[index].movement); + + if (!is_brick_there(next)) { // Execute the move + ghosts[index].locs[0] = next; + is_view_update = 0; + } else { // Opposite the vertical movement + ghosts[index].movement = ghosts[index].movement ^ UP; // Opposite it + + // Reset the point + next = ghosts[index].locs[0]; + next_loc(&next, ghosts[index].movement); + + if (!is_brick_there(next)) { // Execute the move + ghosts[index].locs[0] = next; + is_view_update = 0; + } else { // Opposite the horizontal movement + ghosts[index].movement = ghosts[index].movement ^ UP; // Undo vertical changes + ghosts[index].movement = ghosts[index].movement ^ LEFT; // Opposite it + + // Reset the point + next = ghosts[index].locs[0]; + next_loc(&next, ghosts[index].movement); + + if (!is_brick_there(next)) { // Execute the move + ghosts[index].locs[0] = next; + is_view_update = 0; + } else { // Reverse both movements + ghosts[index].movement = ghosts[index].movement ^ UP; + + // Reset the point + next = ghosts[index].locs[0]; + next_loc(&next, ghosts[index].movement); + + if (!is_brick_there(next)) { // Execute the move + ghosts[index].locs[0] = next; + is_view_update = 0; + } else { // Stuck in a rail; find possible moving directions. + + int pdai = 0; // Possible Directions Append Index + int possible_dirs[4]; + + int j; + for (j = 0; j < 8; j++) { + if (all_directions[j] & VERTICAL && all_directions[j] & HORIZONTAL) + continue; // Skip 2D directions + + loc_t t = ghosts[index].locs[0]; + next_loc(&t, all_directions[j]); + + if (!is_brick_there(t)) { // Add + possible_dirs[pdai++] = all_directions[j]; + } + } + + if (pdai == 0); // Actually stuck + else { + // Undo changes + ghosts[index].movement = reverse_dir(ghosts[index].movement); + + // Find nearest direction to current moving direction from possible_dirs + int nearest_dir_index = -1; + int nearest_dir_changes = -1; + + // Clock-wise and non + for (j = 0; j <= 1; j++) { + int k; + for (k = 0; k < pdai; k++) { + int changes = 0; + int direction = ghosts[index].movement; + + while (direction != possible_dirs[k]) { + direction = turn_dir(direction, j, 1); + changes++; + } + + if (nearest_dir_index == -1 || changes < nearest_dir_changes) { + nearest_dir_index = k; + nearest_dir_changes = changes; + } + } + } + + // Update direction on this algorithm + + if (pdai == 1) { + ghosts[index].movement = reverse_dir(ghosts[index].movement); + } else { + if (possible_dirs[nearest_dir_index] & HORIZONTAL) { + ghosts[index].movement ^= UP; + } else { // Vertical + ghosts[index].movement ^= LEFT; + } + } + + // Move on nearest direction + next_loc(&ghosts[index].locs[0], possible_dirs[nearest_dir_index]); + is_view_update = 0; + } + } + } + } + } + + break; + } + case RAIL_GHOST : { + // First and second locations should be sync + int fpai = 0; // First Possibilities Append Index + int first_possibilities[4]; + + int spai = 0; // Second Possibilities Append Index + int second_possibilities[4]; + + // Check first location moving possibilities + int j; + for (j = 0; j < 8; j++) { + if (all_directions[j] & VERTICAL && all_directions[j] & HORIZONTAL) continue; //skip 2D directions + + loc_t next = ghosts[index].locs[0]; + next_loc(&next, all_directions[j]); + + if (is_brick_there(next)) { //possible + first_possibilities[fpai++] = all_directions[j]; + } + } + + // Check second location moving possibilities + for (j = 0; j < 8; j++) { + if (all_directions[j] & VERTICAL && all_directions[j] & HORIZONTAL) continue; // Skip 2D directions + + loc_t next = ghosts[index].locs[1]; + next_loc(&next, all_directions[j]); + + if (!is_brick_there(next)) { // Possible + second_possibilities[spai++] = all_directions[j]; + } + } + + // Current moving direction possibility + int first_possibility = linear_int_arr_search(fpai, first_possibilities, ghosts[index].movement) != -1; + int second_possibility = linear_int_arr_search(spai, second_possibilities, ghosts[index].movement) != -1; + + int should_change_first_direction = 0; + int should_change_second_direction = 0; + + if (first_possibility) { + loc_t next = ghosts[index].locs[0]; + next_loc(&next, ghosts[index].movement); + + // Check to stay neighbors + if (are_neighbour_locs(next, ghosts[index].locs[1])) { // Execute it + ghosts[index].locs[0] = next; + is_view_update = 0; + } else should_change_first_direction = 1; + } else should_change_first_direction = 1; + if (second_possibility) { + loc_t next = ghosts[index].locs[1]; + next_loc(&next, ghosts[index].movement); + + // Check to stay neighbors + if (are_neighbour_locs(next, ghosts[index].locs[0])) { // Execute it + ghosts[index].locs[1] = next; + is_view_update = 0; + } else should_change_second_direction = 1; + } else should_change_second_direction = 1; + + if (should_change_first_direction && should_change_second_direction) { + // Find common possible directions then remove the reverse of current direction + int commons[2]; + int cai = find_common_elements_of_int_arrs( + 2, fpai, spai, commons, + first_possibilities, second_possibilities + ); // Commons' append index + + if (cai != 0) { // Found some commons + // Remove the reverse of current direction + if (shift_left_int_arr( + cai, commons, + linear_int_arr_search(cai, commons, reverse_dir(ghosts[index].movement)) + )) { + cai--; + } else bug_alert("Reverse of current direction not removed!\n"); + + if (cai != 0) { + ghosts[index].movement = commons[0]; + // If ghosts are not side by side, reach that, else normal move. + if (!are_size_by_size_locs( + ghosts[index].locs[1], ghosts[index].locs[0], + 0 + )) { // Are not side by size + loc_t next = ghosts[index].locs[0]; + next_loc(&next, ghosts[index].movement); + + // Check if now side by side + if (!are_size_by_size_locs(ghosts[index].locs[1], next, 0)) { //are not side by side + next = ghosts[index].locs[1]; + next_loc(&next, ghosts[index].movement); + + // Check if now side by side + if (!are_size_by_size_locs(next, ghosts[index].locs[0], 0)) { // Stuck + // WTF + } else { // Execute + ghosts[index].locs[1] = next; + is_view_update = 0; + } + } else { // Are side by side; execute the move. + ghosts[index].locs[0] = next; + is_view_update = 0; + } + } else { // Are side by side; do a normal move. + move_ghost(index); + } + } else { // No commons after removing reverse direction of current moving direction + + // Find a direction based on being side by side but not reverse of current direction + for (j = 0; j < 8; j++) { + if ((all_directions[j] & VERTICAL && all_directions[j] & HORIZONTAL) || + all_directions[j] == reverse_dir(ghosts[index].movement)) + continue; // Skip 2D directions and reverse direction of current direction + + loc_t next = ghosts[index].locs[0]; + next_loc(&next, all_directions[j]); + + if (is_brick_there(next)) { // Check to be side by side + if (!are_size_by_size_locs(ghosts[index].locs[1], next, 0)) { + continue; // Not side by side + } else { // Found it; execute the move. + ghosts[index].movement = all_directions[j]; + move_ghost(index); + break; + } + } + } + } + } else { // Stuck + + // Find a direction based on being side by side but not reverse of current direction + for (j = 0; j < 8; j++) { + if ((all_directions[j] & VERTICAL && all_directions[j] & HORIZONTAL) || + all_directions[j] == reverse_dir(ghosts[index].movement)) + continue; // Skip 2D directions and reverse direction of current direction + + loc_t next = ghosts[index].locs[0]; + next_loc(&next, all_directions[j]); + + if (is_brick_there(next)) { + if (!are_size_by_size_locs(ghosts[index].locs[1], next, 0)) continue; // Not side by side + else { // Found it; execute the move + ghosts[index].movement = all_directions[j]; + move_ghost(index); + break; + } + } + } + } + } + + break; + } + case BRICK_ZONE_GHOST : { + // TODO + break; + } + } } -void moveGhost(int i) { - - switch(ghosts[i].type) { - - case FLOAT_GHOST : { - - if(ghosts[i].movement == -1) break; - - Location next = ghosts[i].locations[0]; - - nextLocation(&next, ghosts[i].movement); - - if(!isBrickThere(next)) { //execute the move - - ghosts[i].locations[0] = next; - view_validation = 0; - - } else { //opposite the vertical movement - - ghosts[i].movement = ghosts[i].movement ^ UP; //opposite it - //reset the point - next = ghosts[i].locations[0]; - nextLocation(&next, ghosts[i].movement); - - if(!isBrickThere(next)) { //execute the move - - ghosts[i].locations[0] = next; - view_validation = 0; - - } else { //opposite the horizontal movement - - ghosts[i].movement = ghosts[i].movement ^ UP; //undo vertical changes - ghosts[i].movement = ghosts[i].movement ^ LEFT; //opposite it - //reset the point - next = ghosts[i].locations[0]; - nextLocation(&next, ghosts[i].movement); - - if(!isBrickThere(next)) { //execute the move - - ghosts[i].locations[0] = next; - view_validation = 0; - - } else { //reverse both movements - - ghosts[i].movement = ghosts[i].movement ^ UP; - //reset the point - next = ghosts[i].locations[0]; - nextLocation(&next, ghosts[i].movement); - - if(!isBrickThere(next)) { //execute the move - - ghosts[i].locations[0] = next; - view_validation = 0; - - } else { //stuck - - //stuck in a rail - - //find possible moving directions - - int pdai = 0; //possible directions append index - int possible_directions[4]; - - int j; - for(j = 0; j < 8; j++) { - - if(directions[j] & VERTICAL && directions[j] & HORIZONTAL) continue; //skip 2D directions - - Location t = ghosts[i].locations[0]; - nextLocation(&t, directions[j]); - - if(!isBrickThere(t)) { //add - - possible_directions[pdai++] = directions[j]; - - } - - } - - //debug -// printf("possible directions : "); -// printDirectionsArray(pdai, possible_directions); -// printf("\n"); - - if(pdai == 0); //actually stuck - else { - - //undo changes - ghosts[i].movement = reverseDirection(ghosts[i].movement); - - //debug -// printf("current direction : "); -// printDirection(ghosts[i].movement); -// printf("\n"); - - //find nearest direction to current moving direction from possible_directions - int nearest_direction_index = -1; - int nearest_direction_changes = -1; - - //clockwise and non - for(j = 0; j <= 1; j++) { - - int k; - for(k = 0; k < pdai; k++) { - - int changes = 0; - int direction = ghosts[i].movement; - - while(direction != possible_directions[k]) { - - direction = turnDirection(direction, j, 1); - changes++; - - } - - if(nearest_direction_index == -1 || changes < nearest_direction_changes) { - - nearest_direction_index = k; - nearest_direction_changes = changes; - - } - -// printf("%s To ", j == 1 ? "clockwise" : "non_clockwise"); -// printDirection(possible_directions[k]); -// printf(" = %d\t", changes); - - } - - } - - //update direction on this algorithm - - if(pdai == 1) { - - ghosts[i].movement = reverseDirection(ghosts[i].movement); - - } else { - - if(possible_directions[nearest_direction_index] & HORIZONTAL) { - - ghosts[i].movement ^= UP; - - } else { //vertical - - ghosts[i].movement ^= LEFT; - - } - - } - - //move on nearest direction - nextLocation(&ghosts[i].locations[0], possible_directions[nearest_direction_index]); - view_validation = 0; - - } - - } - - } - - } - - } - - break; - } - - case RAIL_GHOST : { - - //first and second locations should be sync - - int fpai = 0; //first possibilities append index - int first_possibilities[4]; - - int spai = 0; //second possibilities append index - int second_possibilities[4]; - - //check first location moving possibilities - int j; - for(j = 0; j < 8; j++) { - - if(directions[j] & VERTICAL && directions[j] & HORIZONTAL) continue; //skip 2D directions - - Location next = ghosts[i].locations[0]; - nextLocation(&next, directions[j]); - - if(isBrickThere(next)) { //possible - - first_possibilities[fpai++] = directions[j]; - - } - - } - - //check second location moving possibilities - for(j = 0; j < 8; j++) { - - if(directions[j] & VERTICAL && directions[j] & HORIZONTAL) continue; //skip 2D directions - - Location next = ghosts[i].locations[1]; - nextLocation(&next, directions[j]); - - if(!isBrickThere(next)) { //possible - - second_possibilities[spai++] = directions[j]; - - } - - } - - //current moving direction possibility - int first_possibility = linearIntArraySearch(fpai, first_possibilities, ghosts[i].movement) != -1; - int second_possibility = linearIntArraySearch(spai, second_possibilities, ghosts[i].movement) != -1; - - int should_change_first_direction = 0; - int should_change_second_direction = 0; - - if(first_possibility) { - - Location next = ghosts[i].locations[0]; - nextLocation(&next, ghosts[i].movement); - - //check to stay neighbors - if(areNeighborPoints(next, ghosts[i].locations[1])) { //execute it - - ghosts[i].locations[0] = next; - view_validation = 0; - - } else should_change_first_direction = 1; - - } else should_change_first_direction = 1; - - if(second_possibility) { - - Location next = ghosts[i].locations[1]; - nextLocation(&next, ghosts[i].movement); - - //check to stay neighbors - if(areNeighborPoints(next, ghosts[i].locations[0])) { //execute it - - ghosts[i].locations[1] = next; - view_validation = 0; - - } else should_change_second_direction = 1; - - } else should_change_second_direction = 1; - - if(should_change_first_direction && should_change_second_direction) { - - //find common possible directions then remove the reverse of current direction - int commons[2]; - int cai = findCommonElementsOfIntArrays(2, fpai, spai, commons, first_possibilities, second_possibilities); //commons append index - - if(cai != 0) { //found some commons - -// printf("remove index : %d\n", linearIntArraySearch(cai, commons, reverseDirection(ghosts[i].movement))); -// printf("commons before remove %d : ", cai); -// printDirectionsArray(cai, commons); -// printf("\n"); -// -// printf("first p %d : ", fpai); -// printDirectionsArray(fpai, first_possibilities); -// printf("\n"); -// -// printf("second p %d : ", spai); -// printDirectionsArray(spai, second_possibilities); -// printf("\n"); - - //remove the reverse of current direction - if(shiftLeftIntArray(cai, commons, linearIntArraySearch(cai, commons, reverseDirection(ghosts[i].movement)))) cai--; -// else bugAlert("reverse of current direction not removed!\n"); - -// printf("commons %d : ", cai); -// printDirectionsArray(cai, commons); -// printf("\n"); -// printf("current movement : "); -// printDirection(ghosts[i].movement); -// printf("\n"); -// printf("reverse of current movement : "); -// printDirection(reverseDirection(ghosts[i].movement)); -// printf("\n"); -// Sleep(1000); - - if(cai != 0) { - - ghosts[i].movement = commons[0]; - - //if ghosts are not side by side reach that else normal move - if(!areSideBySideLocations(ghosts[i].locations[1], ghosts[i].locations[0], 0)) { //are not side by side - -// bugAlert("are not sbs\n"); - - Location next = ghosts[i].locations[0]; - nextLocation(&next, ghosts[i].movement); - - //check if now side by side - if(!areSideBySideLocations(ghosts[i].locations[1], next, 0)) { //are not side by side - - next = ghosts[i].locations[1]; - nextLocation(&next, ghosts[i].movement); - - //check if now side by side - if(!areSideBySideLocations(next, ghosts[i].locations[0], 0)) { //stuck - - ///wtf - - } else { //execute - - ghosts[i].locations[1] = next; - view_validation = 0; - - } - - } else { //are side by side; execute the move - - ghosts[i].locations[0] = next; - view_validation = 0; - - } - - } else { //are side by side; do a normal move - - moveGhost(i); - - } - - } else { //no commons after removing reverse direction of current moving direction - - //find a direction based on being side by side but not reverse of current direction - for(j = 0; j < 8; j++) { - - if((directions[j] & VERTICAL && directions[j] & HORIZONTAL) || directions[j] == reverseDirection(ghosts[i].movement)) continue; //skip 2D directions and reverse direction of current direction - - Location next = ghosts[i].locations[0]; - nextLocation(&next, directions[j]); - - if(isBrickThere(next)) { //check to be side by side - -// printf("sbs = "); -// printDirection(sbs); -// printf("\n"); - - if(!areSideBySideLocations(ghosts[i].locations[1], next, 0)) continue; //not side by side - else { //found it; execute the move - -// printf("found it : "); -// printDirection(directions[j]); -// printf("\n"); -// Sleep(1000); - - ghosts[i].movement = directions[j]; - - moveGhost(i); - - break; - - } - - } - - } - - } - - } else { //stuck - -// printf("stuckio!\n"); - - //find a direction based on being side by side but not reverse of current direction - for(j = 0; j < 8; j++) { - - if((directions[j] & VERTICAL && directions[j] & HORIZONTAL) || directions[j] == reverseDirection(ghosts[i].movement)) continue; //skip 2D directions and reverse direction of current direction - - Location next = ghosts[i].locations[0]; - nextLocation(&next, directions[j]); - - if(isBrickThere(next)) { - -// printf("sbs = "); -// printDirection(sbs); -// printf("\n"); - - if(!areSideBySideLocations(ghosts[i].locations[1], next, 0)) continue; //not side by side - else { //found it; execute the move - -// printf("found it : "); -// printDirection(directions[j]); -// printf("\n"); -// Sleep(1000); - - ghosts[i].movement = directions[j]; - - moveGhost(i); - - break; - - } - - } - - } - - } - - } - - break; - } - - case BRICK_ZONE_GHOST : { - - /// - - break; - } - - } - +// Controls ghosts movement +void move_ghosts() { + static int called_times = 0; + called_times++; + + if (called_times == GHOSTS_SPEED_RATIO) { + int i; + for (i = 0; i < GHOSTS_SIZE; i++) { + move_ghost(i); + } + + called_times = 0; + } } -int isBrickThere(Location p) { - - //check location to be in bounds - if(p.x < 0 || p.x > ROWS - 1 || p.y < 0 || p.y > COLUMNS - 1) return 0; //out of bound location - - return bricks[p.x][p.y] < 0; - +bool can_pac_man_move(const dir_t dir) { + if (dir & HORIZONTAL) { + if (dir & LEFT) return pm.location.y - 1 >= 0; // Left + else return pm.location.y + 1 < COLUMNS; // Right + } else if (dir & VERTICAL) { + if (dir & UP) return pm.location.x - 1 >= 0; // Up + else return pm.location.x + 1 < ROWS; // Down + } else return SHOULD_NOT_REACH_HERE; } -int canPacManMove(int direction) { - - if(direction & HORIZONTAL) { - - if(direction & LEFT) return pm.location.y - 1 >= 0; //left - else return pm.location.y + 1 < COLUMNS; //right - - } - - if(direction & VERTICAL) { - - if(direction & UP) return pm.location.x - 1 >= 0; //up - else return pm.location.x + 1 < ROWS; //down - - } - - return SHOULD_NOT_REACH_HERE; - +void update_zones() { + int current_color = 0; + int current_color_use_time = 0; + + // Replace pac-man trace with bricks + replace_2d_int_arr(ROWS, COLUMNS, bricks, -2, -1); + + int i; + for (i = 0; i < ROWS; i++) { + int j; + for (j = 0; j < COLUMNS; j++) { + if (bricks[i][j] == -1) { + if (current_color_use_time > 0) current_color++; + } else { + bricks[i][j] = current_color; + current_color_use_time++; + } + } + } + + for (i = 0; i < ROWS; i++) { + int j; + for (j = 0; j < COLUMNS; j++) { + if (bricks[i][j] != -1) { + // Check neighbors + loc_t l; + + int k; + for (k = 0; k < 8; k++) { + // Reset + l.x = i; + l.y = j; + + next_loc(&l, all_directions[k]); + + if (bricks[l.x][l.y] == -1) continue; + else { // A zone number + int o; + for (o = 0; o < ROWS; o++) { + int p; + for (p = 0; p < COLUMNS; p++) { + if (bricks[o][p] == -1) continue; + else if (bricks[o][p] == bricks[i][j]) bricks[o][p] = bricks[l.x][l.y]; + } + } + } + } + } + } + } } -void updateZones() { - - int current_color = 0; - int current_color_use_time = 0; - - //replace pac man trace with bricks - Replace2DintArray(ROWS, COLUMNS, bricks, -2, -1); - - int i; - for(i = 0; i < ROWS; i++) { - - int j; - for(j = 0; j < COLUMNS; j++) { - - if(bricks[i][j] == -1) { - - if(current_color_use_time > 0) current_color++; - - } else { - - bricks[i][j] = current_color; - current_color_use_time++; - - } - - } - - } - - for(i = 0; i < ROWS; i++) { - - int j; - for(j = 0; j < COLUMNS; j++) { - - if(bricks[i][j] != -1) { - - //check neighbors - Location l; - - int k; - for(k = 0; k < 8; k++) { - - //reset - l.x = i; - l.y = j; - - nextLocation(&l, directions[k]); - - if(bricks[l.x][l.y] == -1) continue; - else { //a zone number - - int o; - for(o = 0; o < ROWS; o++) { - - int p; - for(p = 0; p < COLUMNS; p++) { - - if(bricks[o][p] == -1) continue; - else if(bricks[o][p] == bricks[i][j]) bricks[o][p] = bricks[l.x][l.y]; - - } - - } - - } - - } - - } - - } - - } - +void try_capture_zones() { + int ghosts_zones[FLOAT_GHOSTS_COUNT * FLOAT_GHOST_LOCS_SIZE + RAIL_GHOSTS_COUNT * RAIL_GHOST_LOCS_SIZE + + BRICK_ZONE_GHOSTS_COUNT * BRICK_ZONE_GHOST_LOCS_SIZE]; + + int i; + for (i = 0; i < GHOSTS_SIZE; i++) { + int j; + for (j = 0; j < ghosts[i].locs_size; j++) { + ghosts_zones[i] = bricks[ghosts[i].locs[j].x][ghosts[i].locs[j].y]; + } + } + + for (i = 0; i < ROWS; i++) { + int j; + for (j = 0; j < COLUMNS; j++) { + if (bricks[i][j] == -1) continue; + else if (linear_int_arr_search(GHOSTS_SIZE, ghosts_zones, bricks[i][j]) == -1) { + replace_2d_int_arr(ROWS, COLUMNS, bricks, bricks[i][j], -1); + } + } + } } -void tryCaptureZones() { - - int ghosts_zones[float_ghosts * FLOAT_GHOST_LOCATIONSS + rail_ghosts * RAIL_GHOST_LOCATIONSS + brick_zone_ghosts * BRICK_ZONE_GHOST_LOCATIONSS]; - - int i; - for(i = 0; i < ghosts_quantity; i++) { - - int j; - for(j = 0; j < ghosts[i].locationss; j++) { - - ghosts_zones[i] = bricks[ghosts[i].locations[j].x][ghosts[i].locations[j].y]; - - } - - } - - for(i = 0; i < ROWS; i++) { - - int j; - for(j = 0; j < COLUMNS; j++) { - - if(bricks[i][j] == -1) continue; - else if(linearIntArraySearch(ghosts_quantity, ghosts_zones, bricks[i][j]) == -1) { - - Replace2DintArray(ROWS, COLUMNS, bricks, bricks[i][j], -1); - - } - - } - - } - +void update_progress() { + int bricks_count = 0; + + int i; + for (i = 0; i < ROWS; i++) { + int j; + for (j = 0; j < COLUMNS; j++) { + if (bricks[i][j] == -1) bricks_count++; + } + } + + static int all_bricks_count = (ROWS - 2) * (COLUMNS - 2); + + progress = (int) ((float) (bricks_count - (2 * (ROWS + COLUMNS))) / (float) all_bricks_count * 100); + is_view_update = 0; } -void updateProgress() { - - int bricks_count = 0; - - int i; - for(i = 0; i < ROWS; i++) { - - int j; - for(j = 0; j < COLUMNS; j++) { - - if(bricks[i][j] == -1) bricks_count++; - - } - - } - - static int all_bricks_count = (ROWS - 2) * (COLUMNS - 2); - - progress = ((float) bricks_count - (2 * (ROWS + COLUMNS))) / (float) all_bricks_count * 100; - view_validation = 0; - +bool move_pac_man(const dir_t dir, const int auto_call) { + if (!can_pac_man_move(dir)) return false; + + if (!auto_call && pm.movement != -1) { + if (reverse_dir(pm.movement) != dir) pm.movement = dir; + return true; + } + + next_loc(&pm.location, dir); + + if (bricks[pm.location.x][pm.location.y] != -1) { // Stepped out of the safe zone, run! + bricks[pm.location.x][pm.location.y] = -2; + + pm.movement = dir; + } else { // Reached the end or moving on the safe zone + if (pm.movement != -1) { // Reached the end + update_zones(); + try_capture_zones(); + update_progress(); + pm.movement = -1; + } + } + + is_view_update = 0; + + return true; } -int movePacMan(int direction, int auto_call) { - - if(!canPacManMove(direction)) return 0; - - if(!auto_call && pm.movement != -1) { - - if(reverseDirection(pm.movement) != direction) pm.movement = direction; - - return 0; - - } - - nextLocation(&pm.location, direction); - - if(bricks[pm.location.x][pm.location.y] != -1) { //stepped out of the safe zone, run! - - bricks[pm.location.x][pm.location.y] = -2; - - pm.movement = direction; - - } else { //reached the end or moving on the safe zone - - if(pm.movement != -1) { //reached the end - - updateZones(); - tryCaptureZones(); - updateProgress(); - pm.movement = -1; - - } - - } - - view_validation = 0; - - return 1; - +void control_auto_moves() { + move_ghosts(); + + if (pm.movement != -1) move_pac_man(pm.movement, 1); } -int arrowToDirection(char a) { - - switch(a) { - - case UAKCC : return VERTICAL | UP; - case DAKCC : return VERTICAL | !UP; - case LAKCC : return HORIZONTAL | LEFT; - case RAKCC : return HORIZONTAL | !LEFT; - - } - - return SHOULD_NOT_REACH_HERE; - +int arrow_to_dir(const int arrow_key_code) { + switch (arrow_key_code) { + case UP_AKC : + return VERTICAL | UP; + case DOWN_AKC : + return VERTICAL | !UP; + case LEFT_AKC : + return HORIZONTAL | LEFT; + case RIGHT_AKC : + return HORIZONTAL | !LEFT; + default: + return SHOULD_NOT_REACH_HERE; + } } -int main(){ - - srand(time(NULL)); //for absolute random - system("chcp 437"); //support ascii characters - cls(); //clear Active Code Page : 437 - - init(); - - drawGame(); - - while(1) { - - if(_kbhit()) { - - char c = _getch(); - - switch(c) { //key press handler - - case AKCCM : { //arrow key - - movePacMan(arrowToDirection(_getch()), 0); - - break; - } - - case SKCC : { - - bricks[pm.location.x][pm.location.y] = 0; - updateZones(); - tryCaptureZones(); - - } - - //default : printf("%d\t", c); - - } - - } - - purgePressedKeys(); - controlAutoMoves(); - drawGame(); - Sleep(1000/REFRESH_RATE); - } - - return 0; - -} +int main() { + initscr(); + noecho(); + nl(); + keypad(stdscr, TRUE); + cbreak(); + nodelay(stdscr, TRUE); + + srand(time(NULL)); //for absolute random + + clear_screen(); + init(); + draw_game(); + + while (true) { + + if (kbhit()) { + int c = getch(); + switch (c) { // Key press handler + case LEFT_AKC: + case RIGHT_AKC: + case UP_AKC: + case DOWN_AKC : { // Arrow key + move_pac_man(arrow_to_dir(c), 0); + break; + } + } + } + + purge_keys(); + control_auto_moves(); + draw_game(); + sleep_ms(1000 / REFRESH_RATE); + } + + endwin(); + return 0; +} \ No newline at end of file diff --git a/pacman.c b/pacman.c new file mode 100755 index 0000000..980521f --- /dev/null +++ b/pacman.c @@ -0,0 +1,13 @@ +#ifndef PACMAN_C_INCLUDED +#define PACMAN_C_INCLUDED + +#define PAC_MAN_CHAR "O" +#define PAC_MAN_TRACE_CHAR "░" + +typedef struct { + loc_t location; + int movement; + int lives; +} pac_man; + +#endif // PACMAN_C_INCLUDED diff --git a/pacman.h b/pacman.h deleted file mode 100755 index 0a139d0..0000000 --- a/pacman.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef PACMAN_H_INCLUDED -#define PACMAN_H_INCLUDED - -#include "utils.h" - -#define PAC_MAN_CHAR 'O' -#define PAC_MAN_TRACE_CHAR 176 - -typedef struct { - - Location location; - int movement; - int lives; - -} PacMan; - -#endif // PACMAN_H_INCLUDED diff --git a/utils.c b/utils.c index f20c485..ce44089 100755 --- a/utils.c +++ b/utils.c @@ -1,436 +1,160 @@ -#include "utils.h" +#ifndef UTILS_C_INCLUDED +#define UTILS_C_INCLUDED -#include -#include +#ifdef WINDOWS #include +#define SLEEP_MS(x) Sleep(x) +#else +#include +#define SLEEP_MS(x) usleep((x) * 1000) +#endif -int randFlag() { - - return rand()%2; - -} - -void randPoint(Location * p, int minx, int maxx, int miny, int maxy) { - - p->x = minx + rand() % (maxx - minx + 1); - p->y = miny + rand() % (maxy - miny + 1); - -} - -void print2DCharArray(int rows, int columns, char c[rows][columns]) { - - int i; - for(i = 0; i < rows; i++) { - - int j; - for(j = 0; j < columns; j++) { - - printf("%c", c[i][j]); - - } - - printf("\n"); - - } - -} - -void printIntArray(int length, int array[length]) { - - int i; - for(i = 0; i < length; i++) { - - printf("%d", array[i]); - - if(i != length - 1) printf(", "); - - } - -} - -void printDirectionsArray(int length, int directions[length]) { - - int i; - for(i = 0; i < length; i++) { - - printDirection(directions[i]); - - if(i != length - 1) printf(", "); - - } - -} - -void printDirection(int direction) { - - int vertical_printed = 0; - - if(direction & VERTICAL) { - - if(direction & UP) printf("Up"); - else printf("Down"); - - vertical_printed = 1; - - } - - if(direction & HORIZONTAL) { - - if(direction & LEFT) { - - if(vertical_printed) printf(" Left"); - else printf("Left"); - - } else { - - if(vertical_printed) printf(" Right"); - else printf("Right"); - - } - - } - - -} - -int Replace2DintArray(int rows, int columns, int array[rows][columns], int key, int replace) { - - int replaceds = 0; - - int i; - for(i = 0; i < rows; i++) { - - int j; - for(j = 0; j < columns; j++) { - - if(array[i][j] == key) { - - array[i][j] = replace; - replaceds++; - - } - - } - - } - - return replaceds; - -} - -int linearIntArraySearch(const int N, int array[N], int key) { - - int i; - for(i = 0; i < N; i++) { - - if(array[i] == key) return i; - - } - - return -1; - -} - -void cls() { - - system("cls"); - -} - -void nextLocation(Location * next, int direction) { - - if(direction & VERTICAL) { - - if(direction & UP) { //up - - next->x--; - - } else { //down - - next->x++; - - } - - } - - if(direction & HORIZONTAL) { - - if(direction & LEFT) { //left - - next->y--; - - } else { //right - - next->y++; - - } - - } - -} - -int reverseDirection(int direction) { - - if(direction & HORIZONTAL) direction ^= LEFT; - - if(direction & VERTICAL) direction ^= UP; - - return direction; - -} - -int randDirection(int diagonal) { - - if(diagonal) { - - return VERTICAL | HORIZONTAL | (randFlag() ? LEFT : !LEFT) | (randFlag() ? UP : !UP); - - } else { - - int rnd = rand()%4; - - return rnd == 0 ? (HORIZONTAL | LEFT) : rnd == 1 ? (VERTICAL | UP) : rnd == 2 ? (HORIZONTAL | !LEFT) : rnd == 3 ? (VERTICAL | !UP) : SHOULD_NOT_REACH_HERE; - - - } - -} - -void purgePressedKeys() { - - while(_kbhit()) _getch(); - -} - -int areNeighborPoints(Location p1, Location p2) { - - if(areEqualPoints(p1, p2)) return 0; - - int i; - for(i = 0; i < 8; i++) { - - Location temp = p2; - - nextLocation(&temp, directions[i]); - - if(areEqualPoints(p1, temp)) return 1; - - } - - return 0; - -} - -int areEqualPoints(Location p1, Location p2) { - - return p1.x == p2.x && p1.y == p2.y; - -} - -void printPointArray(int length, Location p[length]) { - - int i; - for(i = 0; i < length; i++) { - - printf("(%d, %d) ", p[i].x, p[i].y); - - } - -} - -void pVector(Location p1, Location p2, Location * result) { - - result->x = p2.x - p1.x; - result->y = p2.y - p1.y; - -} - -int vectorToDirection(Location vector) { - - if(vector.x < 0 && vector.y < 0) { //up left - - return VERTICAL | HORIZONTAL | LEFT | UP; - - } else if(vector.x < 0 && vector.y == 0) { //up - - return VERTICAL | UP; - - } else if(vector.x < 0 && vector.y > 0) { //up right - - return VERTICAL | HORIZONTAL | !LEFT | UP; - - } else if(vector.x == 0 && vector.y < 0) { //left - - return HORIZONTAL | LEFT; - - } else if(vector.x == 0 && vector.y == 0) { //zero vector - - return SHOULD_NOT_REACH_HERE; - - } else if(vector.x == 0 && vector.y > 0) { //right - - return HORIZONTAL | !LEFT; - - } else if(vector.x > 0 && vector.y < 0) { //down left - - return HORIZONTAL | VERTICAL | !UP | LEFT; - - } else if(vector.x > 0 && vector.y == 0) { //down - - return VERTICAL | !UP; - - } else if(vector.x > 0 && vector.y > 0) { //down right - - return VERTICAL | HORIZONTAL | !UP | !LEFT; - - } - -} - -int areSideBySideLocations(Location l1, Location l2, int check_being_neighbors) { - - if(check_being_neighbors && !areNeighborPoints(l1, l2)) return 0; - - Location vector; - - pVector(l1 ,l2, &vector); - - int sbs = vectorToDirection(vector); - - if(sbs & VERTICAL && sbs & HORIZONTAL) return 0; - else return 1; - -} - -void printPoint(Location p) { - - printf("(%d, %d)", p.x, p.y); - -} - -int reflectDirection(int direction, int mirror_position) { - - /// - -} - -int findCommonElementsOfIntArrays(int result_length, int array1_length, int array2_length, int result[result_length], int array1[array1_length], int array2[array2_length]) { - - int rai = 0; //result append index - int has_space = result_length != 0; - - //set the smaller length array as base - - if(array1_length < array2_length) { - - int i; - for(i = 0; i < array1_length; i++) { - - if(linearIntArraySearch(array2_length, array2, array1[i]) != -1) { //found a common int - - if(has_space) result[rai] = array1[i]; - - rai++; - - if(rai == result_length) has_space = 0; - - } - - } - - } else { - - int i; - for(i = 0; i < array2_length; i++) { - - if(linearIntArraySearch(array1_length, array1, array2[i]) != -1) { //found a common int - - if(has_space) result[rai] = array2[i]; - - rai++; - - if(rai == result_length) has_space = 0; - - } - - } - - } - - return rai; - -} - -void insertionSortIntArray(int length, int array[length]) { - - int i; - for(i = 1; i < length; i++) { - - int j; - for(j = i; array[j] < array[j-1] && j > 0; j--) { - - int t = array[j]; - array[j] = array[j-1]; - array[j-1] = t; - - } - - } - -} - -int shiftLeftIntArray(int N, int array[N], int remove_index) { - - if(remove_index < 0) return 0; - if(remove_index == N - 1) return 1; - - int executed = 0; - - int i; - for(i = remove_index + 1; i < N; i++) { - - int t = array[i]; - array[i] = array[i-1]; - array[i-1] = t; - - if(!executed) executed = 1; - - } - - return executed; - -} - -void bugAlert(char * msg) { - - printf("Bug : %s", msg); - Sleep(1000); - -} - -int turnDirection(int direction, int clockwise, int steps) { - - //find its index at directions - int index; - if((index = linearIntArraySearch(8, directions, direction)) == -1) return SHOULD_NOT_REACH_HERE; //not a direction - - int i; - for(i = 0; i < steps; i++) { - - if(clockwise) { - - direction = directions[index == 7 ? 0 : index + 1]; - - } else { - - direction = directions[index == 0 ? 7 : index - 1]; - - } - - } - - return direction; - -} - - +#include +#include +#include +#include "kbhit.c" + +// AKC = Arrow Key Code +#define LEFT_AKC KEY_LEFT +#define RIGHT_AKC KEY_RIGHT +#define UP_AKC KEY_UP +#define DOWN_AKC KEY_DOWN + +#define SHOULD_NOT_REACH_HERE (-1) + +bool rand_bool() { + return rand() % 2; +} + +void print_2d_str_arr(const int rows, const int columns, const char *arr[rows][columns]) { + int i; + for (i = 0; i < rows; i++) { + int j; + for (j = 0; j < columns; j++) { + printf("%s", arr[i][j]); + } + printf("\r\n"); + } +} + +void print_int_arr(const int size, const int arr[size]) { + int i; + for (i = 0; i < size; i++) { + printf("%d", arr[i]); + if (i != size - 1) printf(", "); + } +} + +int replace_2d_int_arr(const int rows, const int columns, int arr[rows][columns], const int key, const int replace) { + int replaceds = 0; + + int i; + for (i = 0; i < rows; i++) { + int j; + for (j = 0; j < columns; j++) { + if (arr[i][j] == key) { + arr[i][j] = replace; + replaceds++; + } + } + } + + return replaceds; +} + +int linear_int_arr_search(const int size, const int arr[size], const int key) { + int i; + for (i = 0; i < size; i++) { + if (arr[i] == key) return i; + } + + return -1; +} + +void clear_screen() { +#ifdef WINDOWS + system("cls"); +#else + system("clear"); +#endif +} + +void purge_keys() { + while (kbhit()) getch(); +} + +int find_common_elements_of_int_arrs( + const int res_size, const int arr1_size, const int arr2_size, int res[res_size], + const int arr1[arr1_size], const int arr2[arr2_size] +) { + + int rai = 0; // Result Append Index + bool has_space = res_size != 0; + + // Set the smaller length array as base + if (arr1_size < arr2_size) { + int i; + for (i = 0; i < arr1_size; i++) { + if (linear_int_arr_search(arr2_size, arr2, arr1[i]) != -1) { // Found a common int + if (has_space) res[rai] = arr1[i]; + rai++; + + if (rai == res_size) has_space = false; + } + } + } else { + int i; + for (i = 0; i < arr2_size; i++) { + if (linear_int_arr_search(arr1_size, arr1, arr2[i]) != -1) { // Found a common int + if (has_space) res[rai] = arr2[i]; + rai++; + + if (rai == res_size) has_space = false; + } + } + } + + return rai; +} + +void insertion_sort_int_arr(int size, int arr[size]) { + int i; + for (i = 1; i < size; i++) { + int j; + for (j = i; arr[j] < arr[j - 1] && j > 0; j--) { + const int t = arr[j]; + arr[j] = arr[j - 1]; + arr[j - 1] = t; + } + } +} + +bool shift_left_int_arr(const int size, int arr[size], const int remove_index) { + + if (remove_index < 0 || remove_index >= size) return false; + else if (remove_index == size - 1) return true; + + bool executed = false; + + int i; + for (i = remove_index + 1; i < size; i++) { + const int t = arr[i]; + arr[i] = arr[i - 1]; + arr[i - 1] = t; + + if (!executed) executed = true; + } + + return executed; +} + +void sleep_ms(const long ms) { + SLEEP_MS(ms); +} + +void bug_alert(const char *msg) { + printf("Bug: %s", msg); + sleep_ms(1000); +} + +#endif // UTILS_C_INCLUDED \ No newline at end of file diff --git a/utils.h b/utils.h deleted file mode 100755 index eb0e53b..0000000 --- a/utils.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef UTILS_H_INCLUDED -#define UTILS_H_INCLUDED - -#include -#include -#include - -//directions -#define VERTICAL 0b0001 -#define HORIZONTAL 0b0010 -#define LEFT 0b0100 -#define UP 0b1000 - -//Key Char Codes = KCC -#define AKCCM -32 //Arrow Key Char Code Modifier -#define LAKCC 75 //Left Arrow Key Char Code -#define RAKCC 77 //Right Arrow Key Char Code -#define UAKCC 72 //Up Arrow Key Char Code -#define DAKCC 80 //Down Arrow Key Char Code -#define SKCC 32 //Enter Key Char Code - -//something -#define SHOULD_NOT_REACH_HERE -1 - -#define PERCENT_CHAR 37 //percent character - -//point = location; x = row, y = column -typedef struct { - - int x, y; - -} Location; - -//clockwise order -static int directions[8] = {VERTICAL | UP, //up - HORIZONTAL | VERTICAL | UP | !LEFT, //up right - HORIZONTAL | !LEFT, //right - HORIZONTAL | VERTICAL | !UP | !LEFT, //down right - VERTICAL | !UP, //down - HORIZONTAL | VERTICAL | !UP | LEFT, //down left - HORIZONTAL | LEFT, //left - HORIZONTAL | VERTICAL | UP | LEFT}; //up left - -//random boolean -int randFlag(); - -//fills the passed point with random x, y within inclusive specified bounds -void randPoint(Location * p, int minx, int maxx, int miny, int maxy); - -//prints the 2D char array -void print2DCharArray(int rows, int columns, char c[rows][columns]); - -//clear dos -void cls(); - -//next step of the move by the specified direction -//next : current location; will be replaced by the new location -void nextLocation(Location * next, int movement); - -int randDirection(int diagonal); - -void printIntArray(int length, int a[length]); - -void printPointArray(int length, Location p[length]); - -void printPoint(Location p); - -void pVector(Location p1, Location p2, Location * result); - -int areEqualPoints(Location p1, Location p2); - -int areNeighborPoints(Location p1, Location p2); - -int reverseDirection(int direction); - -void printIntArray(int length, int array[length]); - -void printDirection(int direction); - -int linearIntArraySearch(const int N, int array[N], int key); - -int Replace2DintArray(int rows, int columns, int array[rows][columns], int key, int replace); - -void insertionSortIntArray(int length, int array[length]); - -//returns founded elements count; result array filled length -int findCommonElementsOfIntArrays(int result_length, int array1_length, int array2_length, int result[result_length], int array1[array1_length], int array2[array2_length]); - -//returns the shift execution -int shiftLeftIntArray(int N, int array[N], int remove_index); - -void bugAlert(char * msg); - -int areSideBySideLocations(Location l1, Location l2, int check_being_neighbors); - -int turnDirection(int direction, int clockwise, int steps); - -#endif // UTILS_H_INCLUDED