diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..f49a4e1 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/PacXon.cbp b/PacXon.cbp new file mode 100755 index 0000000..87d0603 --- /dev/null +++ b/PacXon.cbp @@ -0,0 +1,53 @@ + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..1c85dc8 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# PacXon + +A partial PacXon game clone in C & Windows cmd.exe (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**. + +![A Screenshot](./Screenshot.png) + +## Simple build & run + +Open the project in Code::Blocks 17.12 (with MinGW) on a Windows OS, and hit Build and run. + +> Running the game might be tricky; because it is a legacy software now, and it was not packaged professionally. \ No newline at end of file diff --git a/Screenshot.png b/Screenshot.png new file mode 100644 index 0000000..6f64774 Binary files /dev/null and b/Screenshot.png differ diff --git a/ghosts.c b/ghosts.c new file mode 100755 index 0000000..075fd70 --- /dev/null +++ b/ghosts.c @@ -0,0 +1 @@ +#include "ghosts.h" diff --git a/ghosts.h b/ghosts.h new file mode 100755 index 0000000..5e8116b --- /dev/null +++ b/ghosts.h @@ -0,0 +1,32 @@ +#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/main.c b/main.c new file mode 100755 index 0000000..df956e7 --- /dev/null +++ b/main.c @@ -0,0 +1,1017 @@ +/** +to dos : + - brick zone ghost creation : appears after 5% + - brick zone ghost move algorithm + - power ups + - progress != 100% bug + - score + - collisions... + - restart + - save and load + - levels + - menu + - reflectDirection method + - colored char struct +**/ + +#include +#include +#include +#include "utils.h" +#include "ghosts.h" +#include "pacman.h" + +#define MAX_GHOSTS 100 //maximum capacity of the ghosts array + +#define REFRESH_RATE 120 //Hz; affects velocity of things in game (pac man, ghosts) + +//shapes +#define BRICK_CHAR 178 + +//the whole game boar dimensions +#define ROWS 30 +#define COLUMNS 70 + +#define WIN_PROGRESS 80 //win condition + +//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 + +//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() { + + int append_index = 0; + + int i; + for(i = 0; i < float_ghosts; i++) { //float ghosts + + addGhost(FLOAT_GHOST, append_index); + append_index++; + + } + + 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++; + + } + +} + +//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++; + +} + +//how pac man starts +void initPacMan() { + + pm.location.x = 0; + pm.location.y = 0; + + pm.movement = -1; + pm.lives = 3; + +} + +//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 + + } + + } + +} + +//draws whole things +void drawGame() { + + if(view_validation) return; + + updatePicture(); + + cls(); + + //print the header + drawHeader(); + + //print the picture + print2DCharArray(ROWS, COLUMNS, picture); + +} + +void init() { + + ghosts_quantity = float_ghosts + rail_ghosts + brick_zone_ghosts; + + initBricks(); + initGhosts(); + initPacMan(); + +} + +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 drawHeader() { + + printf("Lives: %d\tScore: %d\tProgress: %d/%d%c\n", pm.lives, score, progress, WIN_PROGRESS, PERCENT_CHAR); + +} + +void controlAutoMoves() { + + moveGhosts(); + + if(pm.movement != -1) movePacMan(pm.movement, 1); + +} + +//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 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; + } + + } + +} + +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; + +} + +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 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 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 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; + +} + +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; + +} + +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 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; + +} diff --git a/pacman.h b/pacman.h new file mode 100755 index 0000000..0a139d0 --- /dev/null +++ b/pacman.h @@ -0,0 +1,17 @@ +#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 new file mode 100755 index 0000000..f20c485 --- /dev/null +++ b/utils.c @@ -0,0 +1,436 @@ +#include "utils.h" + +#include +#include +#include + +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; + +} + + diff --git a/utils.h b/utils.h new file mode 100755 index 0000000..eb0e53b --- /dev/null +++ b/utils.h @@ -0,0 +1,98 @@ +#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