/*
* serpent.c
*
* Copyright 2023 Darius Drake
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
/* Libraries */
#include
#include
#include
#include
#include
#include
#include
#include
/* */
/* Program information */
#define NAME "serpent"
#define VERSION 0.1
/* */
/* global variables/constants */
#define REFRESH_RATE 0.08 // refresh rate (in seconds)
#define START_SNAKE_LEN 5 // initial snake length
#define SNAKE_BODY '*' // snake's body
#define SNAKE_HEAD_U 'v' // head when going up
#define SNAKE_HEAD_D '^' // head when going down
#define SNAKE_HEAD_L '>' // head when going left
#define SNAKE_HEAD_R '<' // head when going right
#define FOOD '@' // normal food
#define FOOD_SCORE 1 // score increase when snake eats food
#define BORDER_CORNER '+' // character at corners of border
#define BORDER_VERT '|' // character for vertical border
#define BORDER_HORI '-' // character for horizontal border
#define SCREEN_WIDTH 40 // the virtual screen width
#define SCREEN_HEIGHT 30 // the virtual screen height
/* */
typedef enum { UP, DOWN, LEFT, RIGHT } Direction;
typedef enum { RUNNING, PAUSED, STOPPED } GameState;
typedef struct {
int X;
int Y;
} Point;
typedef struct {
char borderCor;
char borderVer;
char borderHor;
unsigned int width;
unsigned int height;
} Board;
typedef struct {
GameState state;
unsigned int points;
char* player;
} Game;
typedef struct {
Point position;
char sprite;
SnakeNode* next;
} SnakeNode;
typedef struct {
Direction direction;
unsigned int length;
SnakeNode* head;
SnakeNode* tail;
} Snake;
typedef struct {
Point position;
unsigned int points;
char food;
} Food;
typedef struct {
char borderCor;
char borderVer;
char borderHor;
unsigned int width;
unsigned int height;
} Board;
// Function prototypes
void initializeGame(Game *game, Board *board, Snake *snake, Food *food);
void clearScreen();
void waitForKeypress();
void displayLogo();
void displayMainMenu();
void displayControls();
void displayInfo();
void argControls();
void argHelp();
void argVersion();
int main (int argc, char **argv) {
Game game;
Board board;
Food food;
Snake snake;
int option;
static const char* short_options = "chv";
static struct option long_options[] = {
{"show-controls", no_argument, NULL, 'c'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{NULL, 0, NULL, 0}
};
while ((option = getopt_long (argc, argv, short_options, long_options, NULL)) != -1) {
switch (option) {
case 'c':
argControls();
break;
case 'h':
argHelp();
break;
case 'v':
argVersion();
break;
case '?':
fprintf(stderr, "Use '-h, --help' for help.\n");
return 1;
}
}
return 0;
}
void initializeGame(Game *game, Board *board, Snake *snake, Food *food) {
game->state = RUNNING;
game->points = 0;
// Initialize snake direction and head
snake->direction = RIGHT;
snake->head = (SnakeNode*)malloc(sizeof(SnakeNode));
snake->head->sprite = SNAKE_HEAD_R;
snake->head->position.X = SCREEN_HEIGHT / 2;
snake->head->position.Y = SCREEN_HEIGHT / 2;
snake->head->next = NULL;
// Initialize snake body
SnakeNode *currentSegment = snake->head;
for (int i = 1; i < START_SNAKE_LEN; ++i) {
SnakeNode *newSegment = (SnakeNode*)malloc(sizeof(SnakeNode));
newSegment->sprite = SNAKE_BODY;
newSegment->position.X = snake->head->position.X - i;
newSegment->position.Y = snake->head->position.Y;
newSegment->next = NULL;
currentSegment->next = newSegment; // Link the current segment to the new one
currentSegment = newSegment; // Move to the new segment
}
// Set the tail of the snake
snake->tail = currentSegment;
}
// Function for clearing the console
void clearScreen() {
printf("\e[1;1H\e[2J");
}
// Function for waiting for keypress
void waitForKeypress() {
struct termios oldt, newt;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
while (getchar() != '\n'); // Consume any remaining characters in the input buffer
getchar(); // Wait for a key press
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}
// Function for displaying the logo of the game
void displayLogo() {
printf(" ____ \n");
printf(" ________________________/ O \\___/\n");
printf("<_____________________________/ \\\n");
printf(" __ _ \n");
printf("/ _\\ ___ _ __ _ __ ___ _ __ | |_ \n");
printf("\\ \\ / _ \\ '__| '_ \\ / _ \\ '_ \\| __|\n");
printf("_\\ \\ __/ | | |_) | __/ | | | |_ \n");
printf("\\__/\\___|_| | .__/ \\___|_| |_|\\__|\n");
printf(" |_| \n");
printf("\n");
}
// Function for displaying the main menu
void displayMainMenu() {
displayLogo();
printf("Welcome to serpent!\n");
printf("\n");
printf(" Select an option:\n");
printf(" 1. Play\n");
printf(" 2. Show leader board\n");
printf(" 3. Show controls\n");
printf(" 4. Show Info\n");
printf(" 5. Exit\n");
printf("\n");
printf(" Choose [1-5]: ");
}
// Function for tisplaying the game controls
void displayControls() {
displayLogo();
printf("Controls.\n");
printf("\n");
printf(" Movement:\n");
printf(" ↑: move up\n");
printf(" ←: move to the left\n");
printf(" →: move to the right\n");
printf(" ↓: move down\n");
printf(" Game:\n");
printf(" q: quit\n");
printf(" p: pause\n");
printf(" r: restart\n");
printf("\n");
printf("Press any key to go back... ");
}
// Function for displaying the game info
void displayInfo() {
displayLogo();
printf("About this game.\n");
printf("\n");
printf(" Author: Darius Drake\n");
printf(" License: GPL v3\n");
printf(" Contribute:\n");
printf(" The source code is available on GitHub -> https://github.com/d4r1us-drk/serpent\n");
printf(" Feel free to contribute with ideas, issues or pull requests.\n");
printf("\n");
printf("Press any key to go back... ");
}
// 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");
}
// 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");
printf("Options:\n");
printf("\t-c, --show-controls Show the controls for the game.\n");
printf("\t-h, --help Display this help message and exit.\n");
printf("\t-v, --version Display version and exit.\n");
}
// Function to display the version in the command line
void argVersion() {
printf("%s. version: %.1lf\n", NAME, VERSION);
printf("Author: Darius Drake\n");
printf("License: GPL v3\n");
printf("Contribute:\n");
printf(" The source code is available on GitHub -> https://github.com/d4r1us-drk/serpent\n");
printf(" Feel free to contribute with ideas, issues or pull requests.\n");
}