From 5f5f1cd006a3ed2435743db0cc9e5e0952e84cb8 Mon Sep 17 00:00:00 2001 From: Kai Stevenson Date: Sun, 5 Feb 2023 23:35:36 -0800 Subject: v1 done, base game --- Makefile | 11 ++++ a.out | Bin 0 -> 19464 bytes conio.h | 14 +++++ snake | Bin 0 -> 19464 bytes snake.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ snake.h | 12 +++++ vector.c | 48 +++++++++++++++++ vector.h | 21 ++++++++ 8 files changed, 288 insertions(+) create mode 100644 Makefile create mode 100755 a.out create mode 100644 conio.h create mode 100755 snake create mode 100644 snake.c create mode 100644 snake.h create mode 100644 vector.c create mode 100644 vector.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5ce9ec3 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +CC=gcc +CFLAGS=-I. -pthread + +generate: + $(CC) -o snake snake.c vector.c $(CFLAGS) + +install: + cp snake /usr/local/bin/ + +clean: + rm snake diff --git a/a.out b/a.out new file mode 100755 index 0000000..24eb6ac Binary files /dev/null and b/a.out differ diff --git a/conio.h b/conio.h new file mode 100644 index 0000000..fa866f2 --- /dev/null +++ b/conio.h @@ -0,0 +1,14 @@ +#include +#include +#include +int getch(void) { + struct termios oldattr, newattr; + int ch; + tcgetattr(STDIN_FILENO, &oldattr); + newattr = oldattr; + newattr.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &newattr); + ch = getchar(); + tcsetattr(STDIN_FILENO, TCSANOW, &oldattr); + return ch; +} diff --git a/snake b/snake new file mode 100755 index 0000000..24eb6ac Binary files /dev/null and b/snake differ diff --git a/snake.c b/snake.c new file mode 100644 index 0000000..44d4b67 --- /dev/null +++ b/snake.c @@ -0,0 +1,182 @@ +#include +#include +#include +#include +#include "vector.h" +#include "snake.h" +#include "conio.h" + +const int boardSize = 12; +const int updateInterval = 300000000L; +vectorp_t snakePositions; +vector_t foodPos; +vector_t lastMoveDir; + +int main(int argc, char **argv) { + //init values + snakePositions = init_vectorp(malloc(5 * sizeof(vector_t)), 5); + snakePositions.vectors[0] = init_vector(5, 5); + snakePositions.vectors[1] = init_vector(5, 4); + snakePositions.vectors[2] = init_vector(5, 3); + snakePositions.vectors[3] = init_vector(4, 3); + snakePositions.vectors[4] = init_vector(3, 3); + foodPos = init_vector(7, 9); + lastMoveDir = init_vector(0, 1); + srand(time(NULL)); + //clear the screen so as to prevent artifacting + printf("\033[2J"); + //prepare the pointers which will be used to communicate with the input getting thread + volatile int* lastInput = calloc(1, sizeof(int)); + int* cancellationToken = calloc(1, sizeof(int)); + //and the wrapper struct + threadArg_t threadArg; + threadArg.returnV = (void*)lastInput; + threadArg.cancellationToken = cancellationToken; + //create listener thread + pthread_t listenerID; + pthread_create(&listenerID, NULL, get_dir_loop, &threadArg); + //main game logic loop + while(1) { + //first, draw the current state of the board + draw_board(); + //handle player input + //wait for the specified interval + struct timespec time; + time.tv_sec = 0; + time.tv_nsec = updateInterval; + nanosleep(&time, NULL); + //read the most recent value emitted by the listener + vector_t dir = parse_dir(*(char*)lastInput); + //if the player inputted a valid direction, use it. otherwise, use his last valid input + if((dir.mx != 0 || dir.my != 0) && !(dir.mx == 1 && lastMoveDir.mx == -1) && !(dir.mx == -1 && lastMoveDir.mx == 1) && !(dir.my == 1 && lastMoveDir.my == -1) && !(dir.my == -1 && lastMoveDir.my == 1)) { + lastMoveDir = dir; + } + else { + } + //check if snake landed on the food + int ateFood = 0; + if((snakePositions.vectors[0].mx + lastMoveDir.mx == foodPos.mx) && (snakePositions.vectors[0].my + lastMoveDir.my == foodPos.my)) { + ateFood = 1; + //move the food + while(1) { + foodPos = init_vector(1 + (rand() % (boardSize - 2)), 1 + (rand() % (boardSize - 2))); + //if food overlaps snake, try again + int overlap = 0; + for(int i = 0; i < snakePositions.length; i++) { + if((snakePositions.vectors[i].mx == foodPos.mx) && (snakePositions.vectors[i].my == foodPos.my)) { + overlap = 1; + } + } + if(!overlap) { + break; + } + } + } + //move the snake + vectorp_t newSnakePositions = move_snake(lastMoveDir, ateFood); + free(snakePositions.vectors); + snakePositions = newSnakePositions; + //check if snake landed on self + int died = 0; + for(int i = 1; i < snakePositions.length; i++) { + if(snakePositions.vectors[0].mx == snakePositions.vectors[i].mx && snakePositions.vectors[0].my == snakePositions.vectors[i].my) { + //game over + died = 1; + } + } + if(died) { + break; + } + } + //clear the last getch call from the listener + *cancellationToken = 1; + printf("\nYou died. Press any key to exit\n"); + pthread_join(listenerID, NULL); + return 0; +} +void* get_dir_loop(void* threadArg) { + while(!*(int*)((threadArg_t*)threadArg)->cancellationToken) { + *(char*)((threadArg_t*)threadArg)->returnV = getch(); + } + return NULL; +} +//parses WASD or HJKL, returns the necessary deltas to move within a 2d array +vector_t parse_dir(char cDir) { + if(cDir == 'w' || cDir == 'k') { + return init_vector(0, -1); + } + else if(cDir == 'd' || cDir == 'l') { + return init_vector(1, 0); + } + else if(cDir == 's' || cDir == 'j') { + return init_vector(0, 1); + } + else if(cDir == 'a' || cDir == 'h') { + return init_vector(-1, 0); + } + else { + return init_vector(0, 0); + } +} +vectorp_t move_snake(vector_t dir, int lengthen) { + int newLength = snakePositions.length + (lengthen ? 1 : 0); + vector_t* newSnakePositions = malloc((newLength * sizeof(vector_t))); + //wrap around edges + int nhx = snakePositions.vectors[0].mx + dir.mx; + nhx = nhx < 0 ? boardSize - 1 : nhx >= boardSize ? 0 : nhx; + int nhy = snakePositions.vectors[0].my + dir.my; + nhy = nhy < 0 ? boardSize - 1 : nhy >= boardSize ? 0 : nhy; + newSnakePositions[0] = init_vector(nhx, nhy); + for(int i = 1; i < newLength; i++) { + newSnakePositions[i] = snakePositions.vectors[i - 1]; + } + return init_vectorp(newSnakePositions, newLength); +} +void draw_board() { + //move the cursor to the top left corner of the screen + printf("\033[0;0H"); + //iterate through and write the board + for(int r = 0; r < boardSize; r++) { + char* line = calloc(boardSize * 2, sizeof(char)); + for(int c = 0; c < boardSize; c++) { + //determine the contents of the board position + int type = 0; + for(int i = 0; i < snakePositions.length; i++) { + if(snakePositions.vectors[i].mx == c && snakePositions.vectors[i].my == r) { + type = i ? 2 : 1; + break; + } + else { + type = 0; + } + } + if(foodPos.mx == c && foodPos.my == r) { + type = 3; + } + //append the appropriate value + if(type == 0) { + //empty square + line[c * 2] = '`'; + line[(c * 2) + 1] = ' '; + } + else if(type == 1) { + //snake head + line[c * 2] = '@'; + line[(c * 2) + 1] = ' '; + } + else if(type == 2) { + //snake body + line[c * 2] = '*'; + line[(c * 2) + 1] = ' '; + } + else if(type == 3) { + //snake body + line[c * 2] = 'F'; + line[(c * 2) + 1] = ' '; + } + } + printf(line); + printf("\n"); + free(line); + } +} diff --git a/snake.h b/snake.h new file mode 100644 index 0000000..9675a17 --- /dev/null +++ b/snake.h @@ -0,0 +1,12 @@ +#include "vector.h" +#ifndef SNAKE +#define SNAKE +typedef struct ThreadArg { + int* cancellationToken; + void* returnV; +} threadArg_t; +void* get_dir_loop(void* threadArg); +vector_t parse_dir(char cDir); +vectorp_t move_snake(vector_t dir, int lengthen); +void draw_board(); +#endif diff --git a/vector.c b/vector.c new file mode 100644 index 0000000..6ab3bf2 --- /dev/null +++ b/vector.c @@ -0,0 +1,48 @@ +#include +#include "vector.h" +vector_t init_vector(int mx, int my) { + vector_t vector; + vector.mx = mx; + vector.my = my; + return vector; +} +vectorp_t init_vectorp(vector_t* vectors, int length) { + vectorp_t vectorP; + vectorP.vectors = vectors; + vectorP.length = length; + return vectorP; +} +vectorp_t remove_vector(vectorp_t vectorp, int index) { + vector_t* vectors = malloc((vectorp.length - 1) * sizeof(vector_t)); + for(int i = 0; i < vectorp.length - 1; i++) { + int correctedIndex = i > index ? i - 1 : i; + if(i != index) { + vectors[correctedIndex] = vectorp.vectors[i]; + } + } + return init_vectorp(vectors, vectorp.length - 1); +} +vectorp_t remove_vectors(vectorp_t vectorp, int* indexes, int indexCount) { + vector_t* vectors = malloc((vectorp.length - indexCount) * sizeof(vector_t)); + int correctedIndex = 0; + for(int i = 0; i < vectorp.length - indexCount; i++) { + int toRemove = 0; + for(int j = 0; j < indexCount; j++) { + if(i == indexes[j]) { + toRemove = 1; + break; + } + } + if(!toRemove) { + vectors[correctedIndex] = vectorp.vectors[i]; + correctedIndex++; + } + } + return init_vectorp(vectors, vectorp.length - indexCount); +} +vectorpp_t init_vectorpp(vectorp_t* vectorps, int length) { + vectorpp_t vectorPP; + vectorPP.vectorps = vectorps; + vectorPP.length = length; + return vectorPP; +} diff --git a/vector.h b/vector.h new file mode 100644 index 0000000..ea2ddcd --- /dev/null +++ b/vector.h @@ -0,0 +1,21 @@ +#ifndef VECTOR +#define VECTOR +typedef struct Vector { + int mx; + int my; +} vector_t; +vector_t init_vector(int mx, int my); +typedef struct VectorP { + vector_t* vectors; + int length; +} vectorp_t; +vectorp_t init_vectorp(vector_t* vectors, int length); +//removes a vector from vectorp +vectorp_t remove_vector(vectorp_t vectorp, int index); +vectorp_t remove_vectors(vectorp_t vectorp, int* indexes, int indexCount); +typedef struct VectorPP { + vectorp_t* vectorps; + int length; +} vectorpp_t; +vectorpp_t init_vectorpp(vectorp_t* vectorps, int length); +#endif -- cgit v1.3.1