diff --git a/serpent b/serpent deleted file mode 100755 index a6a5d53..0000000 Binary files a/serpent and /dev/null differ diff --git a/serpent.c b/serpent.c index fd8427b..d058273 100644 --- a/serpent.c +++ b/serpent.c @@ -30,8 +30,11 @@ #include "serpent.h" /* */ -// Variable to track if snake is alive -bool is_alive = true; +/* Game variables */ +bool is_alive = true; /* variable to track if snake is alive */ +int SPEED = 100; /* speed of the game */ +int MAX_SPEED = 60; /* max speed of the game */ +/* */ /* Initialize structs */ snake_t *snake; @@ -56,6 +59,7 @@ void argVersion(); /* */ int main (int argc, char **argv) { + /* Command line parsing */ int option; static const char* short_options = "chv"; @@ -82,8 +86,9 @@ int main (int argc, char **argv) { return 1; } } + /* */ - // Initialize board + /* Initialize board */ board_t board = { .border = BOARD_CHAR, .boardHeight = SCREEN_HEIGHT, @@ -129,6 +134,7 @@ int main (int argc, char **argv) { usleep(SPEED * 800L); } + /* Game score */ int score = snakeSize(); /* Free memory allocated by snake */ @@ -145,243 +151,327 @@ int main (int argc, char **argv) { return 0; } -void appendSnakeNode(snake_t * new_snake) { +void appendSnakeNode(snake_t *new_snake) { + /* Allocate memory for a new snake node */ snake_node *node_ptr = malloc(sizeof(snake_node)); + /* Set the previous pointer of the new node to the current tail of the snake */ node_ptr->prev = new_snake->tail; + + /* The new node is the last in the linked list, so its next pointer is NULL */ node_ptr->next = NULL; + /* Connect the current tail's next pointer to the new node */ new_snake->tail->next = node_ptr; - + + /* Update the snake's tail to point to the new node, making it the new tail */ new_snake->tail = node_ptr; } snake_t *startSnake(board_t *board) { + /* Allocate memory for a new snake */ snake_t *new_snake = malloc(sizeof(snake_t)); + /* Allocate memory for the head of the snake */ snake_node *head = malloc(sizeof(snake_node)); + /* Initialize the snake's head and tail to the allocated head node */ new_snake->head = head; new_snake->tail = head; + /* Set the head's previous and next pointers to NULL, as it's the only node in the beginning */ head->prev = NULL; head->next = NULL; + + /* Set the initial position of the snake's head at the center of the board */ head->pX = board->boardWidth / 2; head->pY = board->boardHeight / 2; + /* Initialize a node pointer to the head for iterating through the snake's body */ snake_node *node = head; + + /* Create additional nodes to form the initial snake body */ for (int i = 1; i < START_SNAKE_SIZE; i++) { + /* Add a new node to the snake, updating the node pointer */ appendSnakeNode(new_snake); node = node->next; + + /* Copy the position of the previous node to maintain a straight line */ node->pX = node->prev->pX; node->pY = node->prev->pY + 1; } + /* Set the initial direction of the snake to move upward */ new_snake->dX = 0; new_snake->dY = -1; + /* Return the initialized snake */ return new_snake; } void freeSnake() { + /* Initialize pointers to traverse the snake linked list */ snake_node *snake_current = snake->head; snake_node *snake_next = snake->head->next; + /* Traverse the snake linked list and free each node */ while (snake_next != NULL) { + /* Free the current node */ free(snake_current); + + /* Move to the next node */ snake_current = snake_next; snake_next = snake_next->next; } + /* Free the last node */ free(snake_current); + + /* Free the snake structure itself */ free(snake); + + /* Set the global snake pointer to NULL to avoid dangling references */ snake = NULL; } bool snakeOccupies(int x, int y, bool excludeHead) { + /* Initialize a pointer to traverse the snake linked list */ snake_node *snake_ptr; - + + /* Determine the starting point in the linked list based on whether the head should be excluded */ if (excludeHead) { snake_ptr = snake->head; } else { snake_ptr = snake->head->next; } - + + /* Traverse the snake linked list */ while (snake_ptr != NULL) { + /* Check if the current node's position matches the specified coordinates */ if (snake_ptr->pX == x && snake_ptr->pY == y) { + /* The snake occupies the specified position */ return true; } + + /* Move to the next node */ snake_ptr = snake_ptr->next; } - + + /* The snake does not occupy the specified position */ return false; } int snakeSize() { + /* Initialize a pointer to traverse the snake linked list */ snake_node *snake_ptr = snake->head; + + /* Initialize a counter to keep track of the number of nodes in the snake linked list */ int counter = 0; - + + /* Traverse the snake linked list and count each node */ while (snake_ptr != NULL) { counter++; + + /* Move to the next node */ snake_ptr = snake_ptr->next; } - + + /* Return the total size of the snake, which is the number of nodes in the linked list */ return counter; } apple_t *startApple(board_t *board) { + /* Allocate memory for a new apple */ apple_t *new_apple = malloc(sizeof(apple_t)); + + /* Seed the random number generator with the current time */ srand(time(NULL)); + /* Generate random coordinates for the new apple, ensuring it does not overlap with the snake */ do { new_apple->pX = (random() % (board->boardWidth - 2)) + 1; new_apple->pY = (random() % (board->boardHeight - 2)) + 1; } while (snakeOccupies(new_apple->pX, new_apple->pY, false)); + /* Return the initialized apple */ return new_apple; } void moveApple(board_t *board) { + /* Variables to store the new coordinates for the apple */ int new_x, new_y; + /* Generate new random coordinates for the apple, ensuring it does not overlap with the snake */ do { new_x = (random() % (board->boardWidth - 2)) + 1; new_y = (random() % (board->boardHeight - 2)) + 1; } while (snakeOccupies(new_x, new_y, true)); + /* Update the position of the existing apple to the new coordinates */ apple->pX = new_x; apple->pY = new_y; } bool appleOccupies(int x, int y) { + /* Check if the specified coordinates match the position of the apple */ if (apple->pX == x && apple->pY == y) { + /* The apple occupies the specified position */ return true; } + + /* The apple does not occupy the specified position */ return false; } void handleInput(int key) { - switch(key) { + /* Handle different key inputs to change the snake's direction */ + switch (key) { case KEY_UP: + /* If the snake is not currently moving down, change its direction to up */ if (snake->dY == 0) { snake->dY = -1; snake->dX = 0; - } + } break; case KEY_DOWN: + /* If the snake is not currently moving up, change its direction to down */ if (snake->dY == 0) { snake->dY = 1; snake->dX = 0; } break; case KEY_RIGHT: + /* If the snake is not currently moving left, change its direction to right */ if (snake->dX == 0) { snake->dX = 1; snake->dY = 0; } break; case KEY_LEFT: + /* If the snake is not currently moving right, change its direction to left */ if (snake->dX == 0) { snake->dX = -1; snake->dY = 0; } break; default: + /* Do nothing for other keys */ refresh(); break; } } void moveSnake(board_t *board) { + /* Check if the snake is moving (either horizontally or vertically) */ if ((ABS(snake->dX) > 0) || (ABS(snake->dY) > 0)) { + /* Calculate the new coordinates for the head and tail of the snake */ int new_head_x = snake->head->pX + snake->dX; int new_head_y = snake->head->pY + snake->dY; int new_tail_x = snake->tail->pX + snake->dX; int new_tail_y = snake->tail->pY + snake->dY; + /* Check if the new head position does not overlap with an apple */ if (!appleOccupies(new_head_x, new_head_y)) { - // Move the tail to the new position + /* Move the tail to the new head position */ snake->tail->pX = new_head_x; snake->tail->pY = new_head_y; - // Update the links in the snake + /* Adjust the linked list to maintain the snake's continuity */ snake->tail->next = snake->head; snake->head->prev = snake->tail; - snake->tail = snake->tail->prev; snake->tail->next = NULL; snake->head->prev->prev = NULL; - snake->head = snake->head->prev; } else { - // The snake eats the apple + /* If the head overlaps with an apple (the snake ate an apple), move the apple to a new position */ moveApple(board); - // Create a new head node + /* Increase the speed if it hasn't reached the maximum */ + if (SPEED <= MAX_SPEED) { + SPEED -= 1; + } + + /* Create a new head node and update its position */ snake_node *new_head = malloc(sizeof(snake_node)); new_head->pX = new_head_x; new_head->pY = new_head_y; new_head->prev = NULL; new_head->next = snake->head; - // Update the links in the snake + /* Update the linked list to include the new head */ snake->head->prev = new_head; snake->head = new_head; - // Append a new node to the snake's tail + /* Add a new node to the snake's body */ appendSnakeNode(snake); + + /* Set the position for the tail node */ snake->tail->pX = new_tail_x; snake->tail->pY = new_tail_y; } - // Check for collisions and update the snake + /* Check for collision with the game borders or itself */ if (snakeOccupies(new_head_x, new_head_y, false) || (new_head_x == 0) || (new_head_x == board->boardWidth - 1) || (new_head_y == 0) || (new_head_y == board->boardHeight - 1)) { + /* If there is a collision, set the snake as not alive */ is_alive = false; } } } void draw(board_t *board) { + /* Clear the terminal screen */ erase(); - int i, j; - for (i = 0; i < board->boardHeight; i++) { - for (j = 0; j < board->boardWidth; j++) { + /* Draw the borders of the game board */ + for (int i = 0; i < board->boardHeight; i++) { + for (int j = 0; j < board->boardWidth; j++) { if (i == 0 || i == board->boardHeight - 1) { + /* Draw the top and bottom borders */ mvprintw(i, j, "%c", board->border); } else if (j == 0 || j == board->boardWidth - 1) { + /* Draw the left and right borders */ mvprintw(i, j, "%c", board->border); } } } + /* Draw the snake on the game board */ snake_node *snake_ptr = snake->head; - if (snake->dX == -1) { + /* Draw the snake head facing left */ mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_L); } else if (snake->dX == 1) { + /* Draw the snake head facing right */ mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_R); } else if (snake->dY == -1) { + /* Draw the snake head facing up */ mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_U); } else if (snake->dY == 1) { + /* Draw the snake head facing down */ mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_D); } + /* Move to the next node */ snake_ptr = snake_ptr->next; + /* Draw the snake body */ while(snake_ptr != NULL) { mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_BODY); + /* Move to the next node */ snake_ptr = snake_ptr->next; } + /* Draw the apple on the game board */ mvaddch(apple->pY, apple->pX, FOOD); + /* Display the game score */ mvprintw(board->boardHeight + 3, 0, "Score: %d", snakeSize() - START_SNAKE_SIZE); } -// Function for displaying the game controls in the command line +/* Function for displaying the game controls in the command line */ void argControls() { printf("%s controls.\n", NAME); printf("Movement:\n"); @@ -395,7 +485,7 @@ void argControls() { printf("\tr: restart\n"); } -// Function to display the help message in the command line +/* Function to display the help message in the command line */ void argHelp() { printf("Usage: %s [OPTIONS]\n", NAME); printf("Play the all time classic snake game in the console.\n\n"); @@ -405,7 +495,7 @@ void argHelp() { printf("\t-v, --version Display version and exit.\n"); } -// Function to display the version in the command line +/* Function to display the version in the command line */ void argVersion() { printf("%s. version: %.1lf\n", NAME, VERSION); printf("Author: Darius Drake\n"); diff --git a/serpent.h b/serpent.h index f253db5..5f207fe 100644 --- a/serpent.h +++ b/serpent.h @@ -10,8 +10,8 @@ #define ABS(x) (x) < 0 ? -(x) : (x) /* */ -/* global variables/constants */ -#define START_SNAKE_SIZE 2 /* snake's initial size */ +/* Global variables/constants */ +#define START_SNAKE_SIZE 5 /* snake's initial size */ #define SNAKE_BODY '*' /* snake's body */ #define SNAKE_HEAD_U 'v' /* head when going up */ #define SNAKE_HEAD_D '^' /* head when going down */ @@ -21,27 +21,30 @@ #define BOARD_CHAR '#' /* character at corners of border */ #define SCREEN_WIDTH 40 /* the virtual screen width */ #define SCREEN_HEIGHT 30 /* the virtual screen height */ -#define SPEED 100 /* speed of the game */ /* */ +/* Board structure */ typedef struct board_t { - char border; - unsigned int boardHeight; - unsigned int boardWidth; + char border; /* will use BOARD_CHAR */ + unsigned int boardHeight; /* will use SCREEN_HEIGHT */ + unsigned int boardWidth; /* will use SCREEN_WIDTH */ } board_t; +/* Snake node structure (for dobly linked list) */ typedef struct snake_node { - int pX, pY; - struct snake_node *next, *prev; + int pX, pY; /* represents the node's position on the board */ + struct snake_node *next, *prev; /* pointers to the next and previous nodes */ } snake_node; +/* Snake structure (dobly linked list) */ typedef struct snake_t { - int dX, dY; - snake_node *head, *tail; + int dX, dY; /* represents the snake's direction */ + snake_node *head, *tail; /* snake's nodes, dobly linked list */ } snake_t; +/* Apple structure */ typedef struct apple_t { - int pX, pY; + int pX, pY; /* represents the apple's position on the board */ } apple_t; #endif //SERPENT_H