Updated: commented the whole code

This commit is contained in:
Lian Drake 2023-12-07 19:33:25 -04:00
parent 6c88b3c89b
commit 2554dcac3e
3 changed files with 132 additions and 39 deletions

BIN
serpent

Binary file not shown.

134
serpent.c
View file

@ -30,8 +30,11 @@
#include "serpent.h" #include "serpent.h"
/* */ /* */
// Variable to track if snake is alive /* Game variables */
bool is_alive = true; 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 */ /* Initialize structs */
snake_t *snake; snake_t *snake;
@ -56,6 +59,7 @@ void argVersion();
/* */ /* */
int main (int argc, char **argv) { int main (int argc, char **argv) {
/* Command line parsing */
int option; int option;
static const char* short_options = "chv"; static const char* short_options = "chv";
@ -82,8 +86,9 @@ int main (int argc, char **argv) {
return 1; return 1;
} }
} }
/* */
// Initialize board /* Initialize board */
board_t board = { board_t board = {
.border = BOARD_CHAR, .border = BOARD_CHAR,
.boardHeight = SCREEN_HEIGHT, .boardHeight = SCREEN_HEIGHT,
@ -129,6 +134,7 @@ int main (int argc, char **argv) {
usleep(SPEED * 800L); usleep(SPEED * 800L);
} }
/* Game score */
int score = snakeSize(); int score = snakeSize();
/* Free memory allocated by snake */ /* Free memory allocated by snake */
@ -145,243 +151,327 @@ int main (int argc, char **argv) {
return 0; 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)); 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; 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; node_ptr->next = NULL;
/* Connect the current tail's next pointer to the new node */
new_snake->tail->next = node_ptr; 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; new_snake->tail = node_ptr;
} }
snake_t *startSnake(board_t *board) { snake_t *startSnake(board_t *board) {
/* Allocate memory for a new snake */
snake_t *new_snake = malloc(sizeof(snake_t)); snake_t *new_snake = malloc(sizeof(snake_t));
/* Allocate memory for the head of the snake */
snake_node *head = malloc(sizeof(snake_node)); 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->head = head;
new_snake->tail = 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->prev = NULL;
head->next = 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->pX = board->boardWidth / 2;
head->pY = board->boardHeight / 2; head->pY = board->boardHeight / 2;
/* Initialize a node pointer to the head for iterating through the snake's body */
snake_node *node = head; snake_node *node = head;
/* Create additional nodes to form the initial snake body */
for (int i = 1; i < START_SNAKE_SIZE; i++) { for (int i = 1; i < START_SNAKE_SIZE; i++) {
/* Add a new node to the snake, updating the node pointer */
appendSnakeNode(new_snake); appendSnakeNode(new_snake);
node = node->next; node = node->next;
/* Copy the position of the previous node to maintain a straight line */
node->pX = node->prev->pX; node->pX = node->prev->pX;
node->pY = node->prev->pY + 1; node->pY = node->prev->pY + 1;
} }
/* Set the initial direction of the snake to move upward */
new_snake->dX = 0; new_snake->dX = 0;
new_snake->dY = -1; new_snake->dY = -1;
/* Return the initialized snake */
return new_snake; return new_snake;
} }
void freeSnake() { void freeSnake() {
/* Initialize pointers to traverse the snake linked list */
snake_node *snake_current = snake->head; snake_node *snake_current = snake->head;
snake_node *snake_next = snake->head->next; snake_node *snake_next = snake->head->next;
/* Traverse the snake linked list and free each node */
while (snake_next != NULL) { while (snake_next != NULL) {
/* Free the current node */
free(snake_current); free(snake_current);
/* Move to the next node */
snake_current = snake_next; snake_current = snake_next;
snake_next = snake_next->next; snake_next = snake_next->next;
} }
/* Free the last node */
free(snake_current); free(snake_current);
/* Free the snake structure itself */
free(snake); free(snake);
/* Set the global snake pointer to NULL to avoid dangling references */
snake = NULL; snake = NULL;
} }
bool snakeOccupies(int x, int y, bool excludeHead) { bool snakeOccupies(int x, int y, bool excludeHead) {
/* Initialize a pointer to traverse the snake linked list */
snake_node *snake_ptr; snake_node *snake_ptr;
/* Determine the starting point in the linked list based on whether the head should be excluded */
if (excludeHead) { if (excludeHead) {
snake_ptr = snake->head; snake_ptr = snake->head;
} else { } else {
snake_ptr = snake->head->next; snake_ptr = snake->head->next;
} }
/* Traverse the snake linked list */
while (snake_ptr != NULL) { while (snake_ptr != NULL) {
/* Check if the current node's position matches the specified coordinates */
if (snake_ptr->pX == x && snake_ptr->pY == y) { if (snake_ptr->pX == x && snake_ptr->pY == y) {
/* The snake occupies the specified position */
return true; return true;
} }
/* Move to the next node */
snake_ptr = snake_ptr->next; snake_ptr = snake_ptr->next;
} }
/* The snake does not occupy the specified position */
return false; return false;
} }
int snakeSize() { int snakeSize() {
/* Initialize a pointer to traverse the snake linked list */
snake_node *snake_ptr = snake->head; 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; int counter = 0;
/* Traverse the snake linked list and count each node */
while (snake_ptr != NULL) { while (snake_ptr != NULL) {
counter++; counter++;
/* Move to the next node */
snake_ptr = snake_ptr->next; snake_ptr = snake_ptr->next;
} }
/* Return the total size of the snake, which is the number of nodes in the linked list */
return counter; return counter;
} }
apple_t *startApple(board_t *board) { apple_t *startApple(board_t *board) {
/* Allocate memory for a new apple */
apple_t *new_apple = malloc(sizeof(apple_t)); apple_t *new_apple = malloc(sizeof(apple_t));
/* Seed the random number generator with the current time */
srand(time(NULL)); srand(time(NULL));
/* Generate random coordinates for the new apple, ensuring it does not overlap with the snake */
do { do {
new_apple->pX = (random() % (board->boardWidth - 2)) + 1; new_apple->pX = (random() % (board->boardWidth - 2)) + 1;
new_apple->pY = (random() % (board->boardHeight - 2)) + 1; new_apple->pY = (random() % (board->boardHeight - 2)) + 1;
} while (snakeOccupies(new_apple->pX, new_apple->pY, false)); } while (snakeOccupies(new_apple->pX, new_apple->pY, false));
/* Return the initialized apple */
return new_apple; return new_apple;
} }
void moveApple(board_t *board) { void moveApple(board_t *board) {
/* Variables to store the new coordinates for the apple */
int new_x, new_y; int new_x, new_y;
/* Generate new random coordinates for the apple, ensuring it does not overlap with the snake */
do { do {
new_x = (random() % (board->boardWidth - 2)) + 1; new_x = (random() % (board->boardWidth - 2)) + 1;
new_y = (random() % (board->boardHeight - 2)) + 1; new_y = (random() % (board->boardHeight - 2)) + 1;
} while (snakeOccupies(new_x, new_y, true)); } while (snakeOccupies(new_x, new_y, true));
/* Update the position of the existing apple to the new coordinates */
apple->pX = new_x; apple->pX = new_x;
apple->pY = new_y; apple->pY = new_y;
} }
bool appleOccupies(int x, int 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) { if (apple->pX == x && apple->pY == y) {
/* The apple occupies the specified position */
return true; return true;
} }
/* The apple does not occupy the specified position */
return false; return false;
} }
void handleInput(int key) { void handleInput(int key) {
switch(key) { /* Handle different key inputs to change the snake's direction */
switch (key) {
case KEY_UP: case KEY_UP:
/* If the snake is not currently moving down, change its direction to up */
if (snake->dY == 0) { if (snake->dY == 0) {
snake->dY = -1; snake->dY = -1;
snake->dX = 0; snake->dX = 0;
} }
break; break;
case KEY_DOWN: case KEY_DOWN:
/* If the snake is not currently moving up, change its direction to down */
if (snake->dY == 0) { if (snake->dY == 0) {
snake->dY = 1; snake->dY = 1;
snake->dX = 0; snake->dX = 0;
} }
break; break;
case KEY_RIGHT: case KEY_RIGHT:
/* If the snake is not currently moving left, change its direction to right */
if (snake->dX == 0) { if (snake->dX == 0) {
snake->dX = 1; snake->dX = 1;
snake->dY = 0; snake->dY = 0;
} }
break; break;
case KEY_LEFT: case KEY_LEFT:
/* If the snake is not currently moving right, change its direction to left */
if (snake->dX == 0) { if (snake->dX == 0) {
snake->dX = -1; snake->dX = -1;
snake->dY = 0; snake->dY = 0;
} }
break; break;
default: default:
/* Do nothing for other keys */
refresh(); refresh();
break; break;
} }
} }
void moveSnake(board_t *board) { void moveSnake(board_t *board) {
/* Check if the snake is moving (either horizontally or vertically) */
if ((ABS(snake->dX) > 0) || (ABS(snake->dY) > 0)) { 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_x = snake->head->pX + snake->dX;
int new_head_y = snake->head->pY + snake->dY; int new_head_y = snake->head->pY + snake->dY;
int new_tail_x = snake->tail->pX + snake->dX; int new_tail_x = snake->tail->pX + snake->dX;
int new_tail_y = snake->tail->pY + snake->dY; 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)) { 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->pX = new_head_x;
snake->tail->pY = new_head_y; 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->tail->next = snake->head;
snake->head->prev = snake->tail; snake->head->prev = snake->tail;
snake->tail = snake->tail->prev; snake->tail = snake->tail->prev;
snake->tail->next = NULL; snake->tail->next = NULL;
snake->head->prev->prev = NULL; snake->head->prev->prev = NULL;
snake->head = snake->head->prev; snake->head = snake->head->prev;
} else { } 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); 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)); snake_node *new_head = malloc(sizeof(snake_node));
new_head->pX = new_head_x; new_head->pX = new_head_x;
new_head->pY = new_head_y; new_head->pY = new_head_y;
new_head->prev = NULL; new_head->prev = NULL;
new_head->next = snake->head; 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->prev = new_head;
snake->head = 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); appendSnakeNode(snake);
/* Set the position for the tail node */
snake->tail->pX = new_tail_x; snake->tail->pX = new_tail_x;
snake->tail->pY = new_tail_y; 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) || if (snakeOccupies(new_head_x, new_head_y, false) ||
(new_head_x == 0) || (new_head_x == board->boardWidth - 1) || (new_head_x == 0) || (new_head_x == board->boardWidth - 1) ||
(new_head_y == 0) || (new_head_y == board->boardHeight - 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; is_alive = false;
} }
} }
} }
void draw(board_t *board) { void draw(board_t *board) {
/* Clear the terminal screen */
erase(); erase();
int i, j;
for (i = 0; i < board->boardHeight; i++) { /* Draw the borders of the game board */
for (j = 0; j < board->boardWidth; j++) { for (int i = 0; i < board->boardHeight; i++) {
for (int j = 0; j < board->boardWidth; j++) {
if (i == 0 || i == board->boardHeight - 1) { if (i == 0 || i == board->boardHeight - 1) {
/* Draw the top and bottom borders */
mvprintw(i, j, "%c", board->border); mvprintw(i, j, "%c", board->border);
} else if (j == 0 || j == board->boardWidth - 1) { } else if (j == 0 || j == board->boardWidth - 1) {
/* Draw the left and right borders */
mvprintw(i, j, "%c", board->border); mvprintw(i, j, "%c", board->border);
} }
} }
} }
/* Draw the snake on the game board */
snake_node *snake_ptr = snake->head; snake_node *snake_ptr = snake->head;
if (snake->dX == -1) { if (snake->dX == -1) {
/* Draw the snake head facing left */
mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_L); mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_L);
} else if (snake->dX == 1) { } else if (snake->dX == 1) {
/* Draw the snake head facing right */
mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_R); mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_R);
} else if (snake->dY == -1) { } else if (snake->dY == -1) {
/* Draw the snake head facing up */
mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_U); mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_U);
} else if (snake->dY == 1) { } else if (snake->dY == 1) {
/* Draw the snake head facing down */
mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_D); mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_HEAD_D);
} }
/* Move to the next node */
snake_ptr = snake_ptr->next; snake_ptr = snake_ptr->next;
/* Draw the snake body */
while(snake_ptr != NULL) { while(snake_ptr != NULL) {
mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_BODY); mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_BODY);
/* Move to the next node */
snake_ptr = snake_ptr->next; snake_ptr = snake_ptr->next;
} }
/* Draw the apple on the game board */
mvaddch(apple->pY, apple->pX, FOOD); mvaddch(apple->pY, apple->pX, FOOD);
/* Display the game score */
mvprintw(board->boardHeight + 3, 0, "Score: %d", snakeSize() - START_SNAKE_SIZE); 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() { void argControls() {
printf("%s controls.\n", NAME); printf("%s controls.\n", NAME);
printf("Movement:\n"); printf("Movement:\n");
@ -395,7 +485,7 @@ void argControls() {
printf("\tr: restart\n"); 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() { void argHelp() {
printf("Usage: %s [OPTIONS]\n", NAME); printf("Usage: %s [OPTIONS]\n", NAME);
printf("Play the all time classic snake game in the console.\n\n"); 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"); 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() { void argVersion() {
printf("%s. version: %.1lf\n", NAME, VERSION); printf("%s. version: %.1lf\n", NAME, VERSION);
printf("Author: Darius Drake\n"); printf("Author: Darius Drake\n");

View file

@ -10,8 +10,8 @@
#define ABS(x) (x) < 0 ? -(x) : (x) #define ABS(x) (x) < 0 ? -(x) : (x)
/* */ /* */
/* global variables/constants */ /* Global variables/constants */
#define START_SNAKE_SIZE 2 /* snake's initial size */ #define START_SNAKE_SIZE 5 /* snake's initial size */
#define SNAKE_BODY '*' /* snake's body */ #define SNAKE_BODY '*' /* snake's body */
#define SNAKE_HEAD_U 'v' /* head when going up */ #define SNAKE_HEAD_U 'v' /* head when going up */
#define SNAKE_HEAD_D '^' /* head when going down */ #define SNAKE_HEAD_D '^' /* head when going down */
@ -21,27 +21,30 @@
#define BOARD_CHAR '#' /* character at corners of border */ #define BOARD_CHAR '#' /* character at corners of border */
#define SCREEN_WIDTH 40 /* the virtual screen width */ #define SCREEN_WIDTH 40 /* the virtual screen width */
#define SCREEN_HEIGHT 30 /* the virtual screen height */ #define SCREEN_HEIGHT 30 /* the virtual screen height */
#define SPEED 100 /* speed of the game */
/* */ /* */
/* Board structure */
typedef struct board_t { typedef struct board_t {
char border; char border; /* will use BOARD_CHAR */
unsigned int boardHeight; unsigned int boardHeight; /* will use SCREEN_HEIGHT */
unsigned int boardWidth; unsigned int boardWidth; /* will use SCREEN_WIDTH */
} board_t; } board_t;
/* Snake node structure (for dobly linked list) */
typedef struct snake_node { typedef struct snake_node {
int pX, pY; int pX, pY; /* represents the node's position on the board */
struct snake_node *next, *prev; struct snake_node *next, *prev; /* pointers to the next and previous nodes */
} snake_node; } snake_node;
/* Snake structure (dobly linked list) */
typedef struct snake_t { typedef struct snake_t {
int dX, dY; int dX, dY; /* represents the snake's direction */
snake_node *head, *tail; snake_node *head, *tail; /* snake's nodes, dobly linked list */
} snake_t; } snake_t;
/* Apple structure */
typedef struct apple_t { typedef struct apple_t {
int pX, pY; int pX, pY; /* represents the apple's position on the board */
} apple_t; } apple_t;
#endif //SERPENT_H #endif //SERPENT_H