From 569dd8832eae031efad6f6c38bcdd20d650b0a30 Mon Sep 17 00:00:00 2001 From: Darius Drake Date: Fri, 8 Dec 2023 05:02:48 -0400 Subject: [PATCH] Updated: better ncurses, new main menu, etc --- serpent.c | 684 +++++++++++++++++++++++++++++++----------------------- serpent.h | 38 +-- 2 files changed, 416 insertions(+), 306 deletions(-) diff --git a/serpent.c b/serpent.c index d058273..d2fe7af 100644 --- a/serpent.c +++ b/serpent.c @@ -30,29 +30,45 @@ #include "serpent.h" /* */ -/* 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 */ +/* global variables */ +bool isAlive = true; /* variable to track if snake is alive */ +bool isRunning = true; +unsigned int SPEED = 100; /* speed of the game */ +unsigned int MAX_SPEED = 60; /* max speed of the game */ +unsigned int terminalRows; +unsigned int terminalCols; +int startY; +int startX; +unsigned int score; /* */ /* Initialize structs */ -snake_t *snake; -apple_t *apple; +Snake *snake; +Apple *apple; /* */ /* Function prototypes */ -void appendSnakeNode(snake_t *new_snake); -snake_t *startSnake(board_t *board); +// structure functions +Snake *startSnake(); +Apple *startApple(); +void appendSnakeNode(Snake *new_snake); void freeSnake(); -bool snakeOccupies(int x, int y, bool excludeHead); int snakeSize(); -apple_t *startApple(board_t *board); -void moveApple(board_t *board); -bool appleOccupies(int x, int y); +// game logic +void updateSnake(); +void updateApple(); +bool snakeCollision(int x, int y, bool excludeHead); +bool appleCollision(int x, int y); +// game i-o void handleInput(int key); -void moveSnake(board_t *board); -void draw(board_t *board); +void drawGame(); +// game functions +void initializeGame(); +void gameLoop(); +void run(); +void mainMenu(WINDOW *menuScreen, int menuType); +void cleanup(); +// command line functions void argControls(); void argHelp(); void argVersion(); @@ -86,74 +102,77 @@ int main (int argc, char **argv) { return 1; } } - /* */ - /* Initialize board */ - board_t board = { - .border = BOARD_CHAR, - .boardHeight = SCREEN_HEIGHT, - .boardWidth = SCREEN_WIDTH - }; - - /* Initialize the snake doubly-linked list */ - snake = startSnake(&board); - - /* Initialize the apple */ - apple = startApple(&board); - - /* Initialize window settings with ncurses */ - initscr(); - cbreak(); - noecho(); - keypad(stdscr, TRUE); - curs_set(0); - nodelay(stdscr, TRUE); + initializeGame(); + + cleanup(); - /* Check if the board size is greater than the screen size */ - if (board.boardWidth > COLS || board.boardHeight > LINES) { - endwin(); - printf("ERROR: Board size is larger than the terminal window.\n"); - return 1; - } - - while (is_alive) { - /* Take arrow key inputs */ - int c = getch(); - if (c != ERR) { - handleInput(c); - } - - /* Update the snake position */ - moveSnake(&board); - - /* Redraw the frame */ - draw(&board); - - /* Refresh the window */ - refresh(); - usleep(SPEED * 800L); - } - - /* Game score */ - int score = snakeSize(); - - /* Free memory allocated by snake */ - freeSnake(); - - /* Free memory allocated by apple */ - free(apple); - - /* End ncurses window */ - endwin(); - - /* Print final score out to terminal */ - printf("Game over. Score: %d", score - START_SNAKE_SIZE); return 0; } -void appendSnakeNode(snake_t *new_snake) { +/* Function responsible of initializing the snake strucutre */ +Snake *startSnake() { + /* Allocate memory for a new snake */ + Snake *new_snake = malloc(sizeof(Snake)); + + /* Allocate memory for the head of the snake */ + SnakeNode *head = malloc(sizeof(SnakeNode)); + + /* 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 = SCREEN_WIDTH / 2; + head->pY = SCREEN_HEIGHT / 2; + + /* Initialize a node pointer to the head for iterating through the snake's body */ + SnakeNode *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->direction = UP; + + /* Return the initialized snake */ + return new_snake; +} + +/* Function responsible of initializing the apple structure */ +Apple *startApple() { + /* Allocate memory for a new apple */ + Apple *new_apple = malloc(sizeof(Apple)); + + /* 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() % (SCREEN_WIDTH - 2)) + 1; + new_apple->pY = (random() % (SCREEN_HEIGHT - 2)) + 1; + } while (snakeCollision(new_apple->pX, new_apple->pY, false)); + + /* Return the initialized apple */ + return new_apple; +} + +/* Function responsible of adding a new node to the tail of the snake */ +void appendSnakeNode(Snake *new_snake) { /* Allocate memory for a new snake node */ - snake_node *node_ptr = malloc(sizeof(snake_node)); + SnakeNode *node_ptr = malloc(sizeof(SnakeNode)); /* Set the previous pointer of the new node to the current tail of the snake */ node_ptr->prev = new_snake->tail; @@ -168,51 +187,11 @@ void appendSnakeNode(snake_t *new_snake) { 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; -} - +/* Function responsible of freeing the memory for the snake structure */ void freeSnake() { /* Initialize pointers to traverse the snake linked list */ - snake_node *snake_current = snake->head; - snake_node *snake_next = snake->head->next; + SnakeNode *snake_current = snake->head; + SnakeNode *snake_next = snake->head->next; /* Traverse the snake linked list and free each node */ while (snake_next != NULL) { @@ -234,9 +213,109 @@ void freeSnake() { snake = NULL; } -bool snakeOccupies(int x, int y, bool excludeHead) { +/* Function responsible of saving the size of the snake */ +int snakeSize() { /* Initialize a pointer to traverse the snake linked list */ - snake_node *snake_ptr; + SnakeNode *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; +} + +/* Function responsible of handling the snake's movement */ +void updateSnake() { + /* Check if the snake is moving (either horizontally or vertically) */ + if ((ABS(snake->direction == LEFT ? -1 : snake->direction == RIGHT ? 1 : 0) > 0) || + (ABS(snake->direction == UP ? -1 : snake->direction == DOWN ? 1 : 0) > 0)) { + /* Calculate the new coordinates for the head and tail of the snake */ + int new_head_x = snake->head->pX + (snake->direction == LEFT ? -1 : snake->direction == RIGHT ? 1 : 0); + int new_head_y = snake->head->pY + (snake->direction == UP ? -1 : snake->direction == DOWN ? 1 : 0); + int new_tail_x = snake->tail->pX + (snake->direction == LEFT ? -1 : snake->direction == RIGHT ? 1 : 0); + int new_tail_y = snake->tail->pY + (snake->direction == UP ? -1 : snake->direction == DOWN ? 1 : 0); + + /* Check if the new head position does not overlap with an apple */ + if (!appleCollision(new_head_x, new_head_y)) { + /* Move the tail to the new head position */ + snake->tail->pX = new_head_x; + snake->tail->pY = new_head_y; + + /* 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 { + /* If the head overlaps with an apple (the snake ate an apple), move the apple to a new position */ + updateApple(); + + /* 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 */ + SnakeNode *new_head = malloc(sizeof(SnakeNode)); + new_head->pX = new_head_x; + new_head->pY = new_head_y; + new_head->prev = NULL; + new_head->next = snake->head; + + /* Update the linked list to include the new head */ + snake->head->prev = new_head; + snake->head = new_head; + + /* 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 collision with the game borders or itself */ + if (snakeCollision(new_head_x, new_head_y, false) || + (new_head_x == 0) || (new_head_x == SCREEN_WIDTH - 1) || + (new_head_y == 0) || (new_head_y == SCREEN_HEIGHT - 1)) { + /* If there is a collision, set the snake as not alive */ + isAlive = false; + } + } +} + +/* Function responsible of moving the apple to a new position */ +void updateApple() { + /* 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() % (SCREEN_WIDTH - 2)) + 1; + new_y = (random() % (SCREEN_HEIGHT - 2)) + 1; + } while (snakeCollision(new_x, new_y, true)); + + /* Update the position of the existing apple to the new coordinates */ + apple->pX = new_x; + apple->pY = new_y; +} + +/* Function responsible of checking if the snake collided with itself */ +bool snakeCollision(int x, int y, bool excludeHead) { + /* Initialize a pointer to traverse the snake linked list */ + SnakeNode *snake_ptr; /* Determine the starting point in the linked list based on whether the head should be excluded */ if (excludeHead) { @@ -261,58 +340,8 @@ bool snakeOccupies(int x, int y, bool excludeHead) { 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) { +/* Function to chech if the snake ate an apple */ +bool appleCollision(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 */ @@ -323,35 +352,32 @@ bool appleOccupies(int x, int y) { return false; } +/* Function responsible of handling user input */ void handleInput(int 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; + if (snake->direction != DOWN) { + snake->direction = UP; } 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; + if (snake->direction != UP) { + snake->direction = DOWN; } 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; + if (snake->direction != LEFT) { + snake->direction = RIGHT; } 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; + if (snake->direction != RIGHT) { + snake->direction = LEFT; } break; default: @@ -361,128 +387,212 @@ void handleInput(int key) { } } -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 head position */ - snake->tail->pX = new_head_x; - snake->tail->pY = new_head_y; - - /* 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 { - /* If the head overlaps with an apple (the snake ate an apple), move the apple to a new position */ - moveApple(board); - - /* 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 linked list to include the new head */ - snake->head->prev = new_head; - snake->head = new_head; - - /* 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 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) { +/* Function responsible of drawing each object in the screen */ +void drawGame() { /* Clear the terminal screen */ erase(); - /* 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); - } - } + // Calculate the center coordinates + int startY = (terminalRows - SCREEN_HEIGHT) / 2; + int startX = (terminalCols - SCREEN_WIDTH) / 2; + + WINDOW *gameBoard = newwin(SCREEN_HEIGHT, SCREEN_WIDTH, startY, startX); + box(gameBoard, 0, 0); + refresh(); + wrefresh(gameBoard); + + /* Draw the snake's head on the game board */ + SnakeNode *snake_ptr = snake->head; + switch (snake->direction) { + case LEFT: + mvaddch(snake_ptr->pY + startY, snake_ptr->pX + startX, SNAKE_HEAD_L); + break; + case RIGHT: + mvaddch(snake_ptr->pY + startY, snake_ptr->pX + startX, SNAKE_HEAD_R); + break; + case UP: + mvaddch(snake_ptr->pY + startY, snake_ptr->pX + startX, SNAKE_HEAD_U); + break; + case DOWN: + mvaddch(snake_ptr->pY + startY, snake_ptr->pX + startX, SNAKE_HEAD_D); + break; } - /* 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 */ + /* Draw the snake's body */ while(snake_ptr != NULL) { - mvaddch(snake_ptr->pY, snake_ptr->pX, SNAKE_BODY); + mvaddch(snake_ptr->pY + startY, snake_ptr->pX + startX, 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); + mvaddch(apple->pY + startY, apple->pX + startX, FOOD); /* Display the game score */ - mvprintw(board->boardHeight + 3, 0, "Score: %d", snakeSize() - START_SNAKE_SIZE); + mvprintw(startY, startX + 1, "Score: %d", snakeSize() - START_SNAKE_SIZE); +} + +/* Game loop function */ +void gameLoop() { + /* Take arrow key inputs */ + int c = getch(); + if (c != ERR) { + handleInput(c); + } + + /* Update the snake position */ + updateSnake(); + + /* Redraw the frame */ + drawGame(); + + /* Refresh the window */ + refresh(); + + /* Introduce a delay for the game loop */ + usleep(SPEED * 800L); +} + +/* Function responsible of initializing the game */ +void initializeGame() { + /* Initialize window settings with ncurses */ + initscr(); + cbreak(); + noecho(); + keypad(stdscr, TRUE); + curs_set(0); + nodelay(stdscr, TRUE); + getmaxyx(stdscr, terminalRows, terminalCols); + + startY = (terminalRows - SCREEN_HEIGHT) / 2; + startX = (terminalCols - SCREEN_WIDTH) / 2; + + /* Initialize the snake doubly-linked list */ + snake = startSnake(); + + /* Initialize the apple */ + apple = startApple(); + + run(); +} + +/* Starting point of the game */ +void run() { + int choice; + + WINDOW * menuScreen = newwin(SCREEN_HEIGHT, SCREEN_WIDTH, startY, startX); + refresh(); + wrefresh(menuScreen); + + while (isRunning) { + /* Display main menu */ + mainMenu(menuScreen, 1); + + /* Input validation loop */ + do { + /* Use getch to get a single character */ + choice = wgetch(menuScreen); + + } while (choice < '1' || choice > '3'); + + switch (choice) { + case '1': + if (!isAlive) { + snake = startSnake(); + isAlive = true; + } + /* Start the game loop */ + while (isAlive) { + gameLoop(); + } + score = snakeSize() - START_SNAKE_SIZE; + mainMenu(menuScreen, 3); + break; + case '2': + /* Show controls */ + mainMenu(menuScreen, 2); + continue; + break; + case '3': + /* Exit the game */ + isRunning = false; + break; + default: + break; + } + } +} + +/* Function to display the main menu */ +void mainMenu(WINDOW *menuScreen, int menuType) { + int menuY = (SCREEN_HEIGHT - startY) / 5; + int menuX = (SCREEN_WIDTH - startX) / 2; + + wclear(menuScreen); + box(menuScreen, 0, 0); + mvwprintw(menuScreen, menuY - 1, menuX - 2, " ____ "); + mvwprintw(menuScreen, menuY, menuX - 2, " ________________________/ O \\___/"); + mvwprintw(menuScreen, menuY + 1, menuX - 2, "<_____________________________/ \\"); + mvwprintw(menuScreen, menuY + 2, menuX - 2, " __ _ "); + mvwprintw(menuScreen, menuY + 3, menuX - 2, "/ _\\ ___ _ __ _ __ ___ _ __ | |_ "); + mvwprintw(menuScreen, menuY + 4, menuX - 2, "\\ \\ / _ \\ '__| '_ \\ / _ \\ '_ \\| __|"); + mvwprintw(menuScreen, menuY + 5, menuX - 2, "_\\ \\ __/ | | |_) | __/ | | | |_ "); + mvwprintw(menuScreen, menuY + 6, menuX - 2, "\\__/\\___|_| | .__/ \\___|_| |_|\\__|"); + mvwprintw(menuScreen, menuY + 7, menuX - 2, " |_| "); + + switch (menuType) { + case 1: + /* Print the main menu inside the menuScreen window */ + mvwprintw(menuScreen, menuY + 9, menuX, "\tMain Menu"); + mvwprintw(menuScreen, menuY + 10, menuX, "\t 1. Start Game"); + mvwprintw(menuScreen, menuY + 11, menuX, "\t 2. Show Controls"); + mvwprintw(menuScreen, menuY + 12, menuX, "\t 3. Exit Game"); + mvwprintw(menuScreen, menuY + 13, menuX, "\tPress a key [1-3]..."); + wrefresh(menuScreen); + break; + case 2: + /* Display controls */ + mvwprintw(menuScreen, menuY + 9, menuX, "\tControls"); + mvwprintw(menuScreen, menuY + 10, menuX, "\t Arrow Up: Move Up"); + mvwprintw(menuScreen, menuY + 11, menuX, "\t Arrow Down: Move Down"); + mvwprintw(menuScreen, menuY + 12, menuX, "\t Arrow Left: Move Left"); + mvwprintw(menuScreen, menuY + 13, menuX, "\t Arrow Right: Move Right"); + mvwprintw(menuScreen, menuY + 14, menuX, "\tPress a key to go back..."); + wrefresh(menuScreen); + wgetch(menuScreen); + break; + case 3: + /* Display the final score on the main menu */ + mvwprintw(menuScreen, menuY + 13, menuX, "\tFinal Score: %d", score); + mvwprintw(menuScreen, menuY + 14, menuX, "\tPress a key to go back..."); + wgetch(menuScreen); + break; + } +} + +/* Function responsible of cleaning the memory */ +void cleanup() { + /* Free memory allocated by snake */ + freeSnake(); + + /* Free memory allocated by apple */ + free(apple); + + /* End ncurses window */ + endwin(); } /* Function for displaying the game controls in the command line */ void argControls() { - printf("%s controls.\n", NAME); - printf("Movement:\n"); - printf("\t↑: move up\n"); - printf("\t←: move to the left\n"); - printf("\t→: move to the right\n"); - printf("\t↓: move down\n"); - printf("Game:\n"); - printf("\tq: quit\n"); - printf("\tp: pause\n"); - printf("\tr: restart\n"); + printf("%s version: %.1lf\n", NAME, VERSION); + printf("Controls:\n"); + printf("\tArrow Up: move up\n"); + printf("\tArrow Down: move down\n"); + printf("\tArrow Left: move to the left\n"); + printf("\tArrow Right: move to the right\n"); } /* Function to display the help message in the command line */ diff --git a/serpent.h b/serpent.h index 5f207fe..9685e64 100644 --- a/serpent.h +++ b/serpent.h @@ -1,7 +1,7 @@ #ifndef SERPENT_H #define SERPENT_H - /* Program information */ +/* Program information */ #define NAME "serpent" #define VERSION 0.1 /* */ @@ -18,33 +18,33 @@ #define SNAKE_HEAD_L '>' /* head when going left */ #define SNAKE_HEAD_R '<' /* head when going right */ #define FOOD '@' /* normal food */ -#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 SCREEN_WIDTH 50 /* the virtual screen width */ +#define SCREEN_HEIGHT 20 /* the virtual screen height */ /* */ -/* Board structure */ -typedef struct board_t { - char border; /* will use BOARD_CHAR */ - unsigned int boardHeight; /* will use SCREEN_HEIGHT */ - unsigned int boardWidth; /* will use SCREEN_WIDTH */ -} board_t; +/* Possible directions for the snake */ +typedef enum { + UP, + DOWN, + LEFT, + RIGHT +} Direction; /* Snake node structure (for dobly linked list) */ -typedef struct snake_node { +typedef struct SnakeNode { 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; + struct SnakeNode *next, *prev; /* pointers to the next and previous nodes */ +} SnakeNode; /* Snake structure (dobly linked list) */ -typedef struct snake_t { - int dX, dY; /* represents the snake's direction */ - snake_node *head, *tail; /* snake's nodes, dobly linked list */ -} snake_t; +typedef struct Snake { + Direction direction; + SnakeNode *head, *tail; /* snake's nodes, dobly linked list */ +} Snake; /* Apple structure */ -typedef struct apple_t { +typedef struct Food { int pX, pY; /* represents the apple's position on the board */ -} apple_t; +} Apple; #endif //SERPENT_H