#include <stdio.
h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#define HEIGHT 20
#define WIDTH 60
#define MAX_TAIL_LENGTH (WIDTH * HEIGHT)
typedef enum { UP, DOWN, LEFT, RIGHT, STOP } Direction;
typedef enum { EASY, MEDIUM, HARD, EXTREME } Difficulty;
typedef struct {
Direction dir;
int score;
int fruit_x, fruit_y;
int head_x, head_y;
int tail_length;
int tail_x[MAX_TAIL_LENGTH];
int tail_y[MAX_TAIL_LENGTH];
Difficulty difficulty;
int base_speed;
int min_speed;
int current_speed;
} GameState;
GameState game;
struct termios old_props;
// Function prototypes
void setup();
void clear_screen();
void draw();
void input();
void game_play();
void set_terminal_attributes();
void reset_terminal_attributes();
int input_available();
int is_position_free(int x, int y);
void show_menu();
void init_difficulty(Difficulty diff);
// Terminal setup for non-blocking input
void set_terminal_attributes() {
tcgetattr(STDIN_FILENO, &old_props);
struct termios new_props = old_props;
new_props.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &new_props);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);
void reset_terminal_attributes() {
tcsetattr(STDIN_FILENO, TCSANOW, &old_props);
int input_available() {
fd_set set;
struct timeval timeout;
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
return select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout) > 0;
void clear_screen() {
printf("\033[H\033[J");
int is_position_free(int x, int y) {
if (x == game.head_x && y == game.head_y)
return 0;
for (int i = 0; i < game.tail_length; i++)
if (game.tail_x[i] == x && game.tail_y[i] == y)
return 0;
return 1;
void show_menu() {
clear_screen();
printf("\n\t\tWelcome to The Snake Game!\n\n");
printf("\tChoose difficulty:\n");
printf("\t1. Easy (Slow speed)\n");
printf("\t2. Medium (Balanced speed)\n");
printf("\t3. Hard (Fast speed)\n");
printf("\t4. Extreme (Very fast)\n\n");
printf("\tPress Q to quit\n");
while (1) {
while (!input_available());
char choice = getchar();
switch (choice) {
case '1': init_difficulty(EASY); return;
case '2': init_difficulty(MEDIUM); return;
case '3': init_difficulty(HARD); return;
case '4': init_difficulty(EXTREME); return;
case 'q': case 'Q': exit(0);
void init_difficulty(Difficulty diff) {
game.difficulty = diff;
switch (diff) {
case EASY:
game.base_speed = 250000;
game.min_speed = 100000;
break;
case MEDIUM:
game.base_speed = 180000;
game.min_speed = 60000;
break;
case HARD:
game.base_speed = 120000;
game.min_speed = 40000;
break;
case EXTREME:
game.base_speed = 100000;
game.min_speed = 30000;
break;
}
game.current_speed = game.base_speed;
void setup() {
game.head_x = WIDTH / 2;
game.head_y = HEIGHT / 2;
game.dir = STOP;
game.score = 0;
game.tail_length = 0;
do {
game.fruit_x = rand() % WIDTH;
game.fruit_y = rand() % HEIGHT;
} while (!is_position_free(game.fruit_x, game.fruit_y));
game.current_speed = game.base_speed;
void draw() {
clear_screen();
printf("\t\tSnake Game - ");
switch (game.difficulty) {
case EASY: printf("Easy"); break;
case MEDIUM: printf("Medium"); break;
case HARD: printf("Hard"); break;
case EXTREME: printf("Extreme"); break;
printf("\n");
if (game.dir == STOP)
printf("\t\tPAUSED (Press SPACE to resume)\n");
for (int i = 0; i < WIDTH + 2; i++) printf("#");
printf("\n");
for (int i = 0; i < HEIGHT; i++) {
printf("#");
for (int j = 0; j < WIDTH; j++) {
if (i == game.head_y && j == game.head_x) {
printf("O");
} else if (i == game.fruit_y && j == game.fruit_x) {
printf("F");
} else {
int printed = 0;
for (int k = 0; k < game.tail_length; k++) {
if (game.tail_x[k] == j && game.tail_y[k] == i) {
printf("o");
printed = 1;
break;
if (!printed) printf(" ");
printf("#\n");
for (int i = 0; i < WIDTH + 2; i++) printf("#");
printf("\nScore: %d | Speed: %dms\n", game.score, game.current_speed / 1000);
printf("Controls: WASD = Move | SPACE = Pause | M = Menu | X = Exit\n");
}
void input() {
static Direction last_dir = RIGHT;
if (input_available()) {
char ch = getchar();
switch (ch) {
case 'a': case 'A':
if (game.dir != RIGHT) game.dir = LEFT;
break;
case 's': case 'S':
if (game.dir != UP) game.dir = DOWN;
break;
case 'd': case 'D':
if (game.dir != LEFT) game.dir = RIGHT;
break;
case 'w': case 'W':
if (game.dir != DOWN) game.dir = UP;
break;
case 'x': case 'X':
reset_terminal_attributes();
exit(0);
break;
case ' ':
if (game.dir == STOP) {
game.dir = last_dir;
} else {
last_dir = game.dir;
game.dir = STOP;
break;
case 'm': case 'M':
show_menu();
setup();
break;
void game_play() {
if (game.dir == STOP) return;
for (int i = game.tail_length - 1; i > 0; i--) {
game.tail_x[i] = game.tail_x[i - 1];
game.tail_y[i] = game.tail_y[i - 1];
if (game.tail_length > 0) {
game.tail_x[0] = game.head_x;
game.tail_y[0] = game.head_y;
switch (game.dir) {
case UP: game.head_y--; break;
case DOWN: game.head_y++; break;
case LEFT: game.head_x--; break;
case RIGHT: game.head_x++; break;
default: break;
if (game.head_x < 0) game.head_x = WIDTH - 1;
if (game.head_x >= WIDTH) game.head_x = 0;
if (game.head_y < 0) game.head_y = HEIGHT - 1;
if (game.head_y >= HEIGHT) game.head_y = 0;
for (int i = 0; i < game.tail_length; i++) {
if (game.tail_x[i] == game.head_x && game.tail_y[i] == game.head_y) {
printf("\nGame Over! Final score: %d\n", game.score);
reset_terminal_attributes();
exit(0);
if (game.head_x == game.fruit_x && game.head_y == game.fruit_y) {
game.score += 10;
game.tail_length++;
do {
game.fruit_x = rand() % WIDTH;
game.fruit_y = rand() % HEIGHT;
} while (!is_position_free(game.fruit_x, game.fruit_y));
int main() {
srand(time(NULL));
set_terminal_attributes();
atexit(reset_terminal_attributes);
show_menu();
setup();
while (1) {
draw();
input();
game_play();
int target_speed = game.base_speed - (game.score * 3000);
if (target_speed < game.min_speed) target_speed = game.min_speed;
game.current_speed = target_speed;
usleep(game.current_speed);