summaryrefslogtreecommitdiff
path: root/snake.c
diff options
context:
space:
mode:
authorKai Stevenson <kai@kaistevenson.com>2023-03-04 17:17:13 -0800
committerKai Stevenson <kai@kaistevenson.com>2023-03-04 17:17:13 -0800
commit79fa249aa729e03b17991c3991f5bb090ffb6b61 (patch)
treecd302a308e51144ddf1e89c13d2e1652a68d0b81 /snake.c
parentaaa2e22835d80cf51fee21025207644ae19d8d66 (diff)
restructured project
Diffstat (limited to 'snake.c')
-rw-r--r--snake.c360
1 files changed, 0 insertions, 360 deletions
diff --git a/snake.c b/snake.c
deleted file mode 100644
index 577c832..0000000
--- a/snake.c
+++ /dev/null
@@ -1,360 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <pthread.h>
-#include <signal.h> /* handling ctrl-c */
-#include <sys/ioctl.h> /* terminal size */
-#include <string.h> /* argument parsing */
-#include <math.h> /* time calculations */
-
-#include "vector.h"
-#include "snake.h"
-#include "conio.h"
-#include "colors.h"
-#include "config.h"
-#define UNUSED(x) (void)(x)
-
-/*** GLOBAL VARIABLES ***/
-volatile sig_atomic_t intCtrlC = 0; /* flag for ctrl-c interrupt */
-int score = 0;
-int updateInterval = 300000000L;
-
-vectorp_t obstaclePositions;
-vectorp_t snakePositions;
-vector_t foodPos;
-vector_t lastMoveDir;
-
-/* unsafe functions like printf cannot be called in async interrupt handler */
-void interrupt_handler(int sig) {
- UNUSED(sig);
- intCtrlC = 1;
-}
-
-int main(int argc, char **argv) {
- /* parsing arguments */
- char *last_flag = NULL;
- int was_flag = 0;
- for(int i = 1; i < argc; i++) {
- if(strcmp("--version", argv[i]) == 0) {
- printf("snake3, version 2.0\n");
- printf("Copyright 2023 Kai Stevenson\n");
- printf("Copyright 2023 Preston Pan\n");
- printf("This software was licensed under the MIT license;\n");
- printf("for more information, see the license file.\n");
- return 0;
- }
- else if(strcmp("--help", argv[i]) == 0) {
- fprintf(stderr, "usage: snake3 [--version] [--help] [--size board_size] [--speed speed] [--obstacles obstacles]\n");
- return 1;
- }
- else if(strcmp("--size", argv[i]) == 0 || strcmp("--speed", argv[i]) == 0 || strcmp("--obstacles", argv[i]) == 0) {
- last_flag = argv[i];
- was_flag = 1;
- }
- else if(was_flag) {
- if(strcmp("--size", last_flag) == 0) {
- boardSize = atoi(argv[i]);
- if(boardSize < 3) {
- fprintf(stderr, "%sboard size too small or argument invalid, please try again.%s\n", RED, COLOR_RESET);
- return 1;
- }
- }
- else if(strcmp("--speed", last_flag) == 0) {
- speed = atoi(argv[i]);
- if(speed <= 0) {
- fprintf(stderr, "%sspeed level must be greater than zero and integer. Please try again.%s\n", RED, COLOR_RESET);
- return 1;
- }
- else if(speed > 10) {
- fprintf(stderr, "%sspeed must not be greater than ten. Please try again.%s\n", RED, COLOR_RESET);
- return 1;
- }
- }
- else if(strcmp("--obstacles", last_flag) == 0) {
- obstacles = atoi(argv[i]);
- if(obstacles < 0) {
- fprintf(stderr, "%sobstacle count cannot be negative. Please try again.%s\n", RED, COLOR_RESET);
- return 1;
- }
- }
- was_flag = 0;
- }
- else {
- printf("usage: snake3 [--version] [--help]\n");
- return 1;
- }
- }
- /* check argument compatibility */
- if((obstacles * obstacleSize) * 3 > pow(boardSize, 2)) {
- fprintf(stderr, "%stoo many obstacles. Please try again.%s\n", RED, COLOR_RESET);
- exit(1);
- }
- /* get terminal size */
- struct winsize win;
- ioctl(fileno(stdout), TIOCGWINSZ, &win);
- int width = (int)(win.ws_col);
- int height = (int)(win.ws_row);
- /* multiplied by 2 to account for the fact that the board is twice as wide as high */
- int smallestAxis = width / 2 < height ? width / 2 : height;
- boardSize = smallestAxis < boardSize ? smallestAxis : boardSize;
- /* hide cursor */
- printf("\033[?25l");
- /* make a new thread handling interrupts */
- signal(SIGINT, interrupt_handler);
- /* 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);
- int opc = (int)pow(obstacleSize, 2) * obstacles;
- obstaclePositions = init_vectorp(malloc(opc * sizeof(vector_t)), opc);
- srand(time(NULL));
- /* place the obstacles */
- /* TODO: make this less recursive */
- int opi = 0;
- for(int i = 0; i < obstacles; i++) {
- while(1) {
- vector_t basePos = init_vector((rand() % (boardSize - (obstacleSize - 1))), (rand() % (boardSize - (obstacleSize - 1))));
- /* if obstacle overlaps snake, try again */
- uint8 overlap = 0;
- for(int r = 0; r < obstacleSize; r++) {
- for(int c = 0; c < obstacleSize; c++) {
- vector_t voxelPos = init_vector(basePos.mx + c, basePos.my + r);
- for(int j = 0; j < snakePositions.length; j++) {
- if((snakePositions.vectors[j].mx == voxelPos.mx) && (snakePositions.vectors[j].my == voxelPos.my)) {
- overlap = 1;
- }
- }
- if(foodPos.mx == voxelPos.mx && foodPos.my == voxelPos.my) {
- overlap = 1;
- }
- }
- }
- if(overlap) {
- continue;
- }
- for(int r = 0; r < obstacleSize; r++) {
- for(int c = 0; c < obstacleSize; c++) {
- vector_t voxelPos = init_vector(basePos.mx + c, basePos.my + r);
- obstaclePositions.vectors[opi] = voxelPos;
- opi++;
- }
- }
- break;
- }
- }
- /* 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) {
- /* handle ctrl-c interrupt */
- if(intCtrlC) {
- goto EXIT;
- }
-
- /* 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 = round((float)updateInterval / ((float)speed / 3));
- 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;
- }
-
- uint8 ateFood = 0;
- /* check if snake is moving onto food */
- if((snakePositions.vectors[0].mx + lastMoveDir.mx == foodPos.mx) && (snakePositions.vectors[0].my + lastMoveDir.my == foodPos.my)) {
- ateFood = 1;
- score += 1;
- if(updateInterval > 100000000) {
- updateInterval -= 10000000;
- }
- /* move the food */
- while(1) {
- uint8 overlap = 0;
- foodPos = init_vector(1 + (rand() % (boardSize - 2)), 1 + (rand() % (boardSize - 2)));
- /* if food overlaps snake or obstacles, try again */
- for(int i = 0; i < snakePositions.length; i++) {
- if((snakePositions.vectors[i].mx == foodPos.mx) && (snakePositions.vectors[i].my == foodPos.my)) {
- overlap = 1;
- }
- }
- for(int i = 0; i < obstaclePositions.length; i++) {
- if((obstaclePositions.vectors[i].mx == foodPos.mx) && (obstaclePositions.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 */
- 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 */
- goto EXIT;
- }
- }
- /* check if snake landed on obstacle */
- for(int i = 0; i < obstaclePositions.length; i++) {
- if((snakePositions.vectors[0].mx == obstaclePositions.vectors[i].mx) && (snakePositions.vectors[0].my == obstaclePositions.vectors[i].my)) {
- goto EXIT;
- }
- }
- }
-EXIT:
- /* clear the last getch call from the listener */
- *cancellationToken = 1;
- printf("\n%sYou died. Score: %d. Press any key to exit%s\n", RED, score, COLOR_RESET);
- printf("\033[?25h");
- pthread_join(listenerID, NULL);
- free(cancellationToken);
- free((int*)lastInput);
- 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++) {
- for(int c = 0; c < boardSize; c++) {
- /* determine the contents of the board position */
- int type = 0;
- /* 0=horizontal, 1=vertical, 2=diagonal slant right, 3=diagonal slant left */
- int tailOrientation = 0;
- for(int i = 0; i < snakePositions.length; i++) {
- if(snakePositions.vectors[i].mx == c && snakePositions.vectors[i].my == r) {
- if(i > 0) {
- type = 2;
- /* get the delta to the connecting tail */
- int dx;
- int dy;
- if(i < snakePositions.length - 1) {
- dx = snakePositions.vectors[i - 1].mx - snakePositions.vectors[i + 1].mx;
- dy = snakePositions.vectors[i - 1].my - snakePositions.vectors[i + 1].my;
- }
- else {
- dx = snakePositions.vectors[i - 1].mx - c;
- dy = snakePositions.vectors[i - 1].my - r;
- }
- if(dy == 0) {
- tailOrientation = 0;
- }
- else if(dx == 0) {
- tailOrientation = 1;
- }
- else if((dx == 1 && dy == 1) || (dx == -1 && dy == -1)) {
- tailOrientation = 3;
- }
- else if((dx == -1 && dy == 1) || (dx == 1 && dy == -1)) {
- tailOrientation = 2;
- }
- }
- else {
- type = 1;
- }
- break;
- }
- }
- if(foodPos.mx == c && foodPos.my == r) {
- type = 3;
- }
- for(int i = 0; i < obstaclePositions.length; i++) {
- if(obstaclePositions.vectors[i].mx == c && obstaclePositions.vectors[i].my == r) {
- type = 4;
- break;
- }
- }
- /* append the appropriate value */
- if(type == 0) {
- /* empty square */
- printf("` ");
- }
- else if(type == 1) {
- /* snake head */
- printf("%s@ %s", GRN, COLOR_RESET);
- }
- else if(type == 2) {
- /* snake body */
- char bodyChar = tailOrientation == 0 ? '-'
- : tailOrientation == 1 ? '|'
- : tailOrientation == 2 ? '/'
- : tailOrientation == 3 ? '\\'
- : '*';
- printf("%s%c %s", GRN, bodyChar, COLOR_RESET);
- }
- else if(type == 3) {
- /* food */
- printf("%sF %s", MAG, COLOR_RESET);
- }
- else if(type == 4) {
- /* obstacle */
- printf("%s# %s", RED, COLOR_RESET);
- }
- }
- printf("\n");
- }
-}