#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/select.h>
using namespace std;
#define HEIGHT 20
#define WIDTH 60
enum Direction { UP, DOWN, LEFT, RIGHT, STOP };
enum Difficulty { EASY, MEDIUM, HARD, EXTREME };
class SnakeGame {
private:
struct termios old_props;
Direction dir;
int score;
int fruit_x, fruit_y;
int head_x, head_y;
vector<int> tail_x;
vector<int> tail_y;
int base_speed;
int min_speed;
int current_speed;
Difficulty difficulty;
void setTerminalAttributes() {
tcgetattr(STDIN_FILENO, &old_props);
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 resetTerminalAttributes() {
tcsetattr(STDIN_FILENO, TCSANOW, &old_props);
bool inputAvailable() {
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 clearScreen() {
cout << "\033[H\033[J";
bool isPositionFree(int x, int y) {
if (x == head_x && y == head_y) return false;
for (size_t i = 0; i < tail_x.size(); i++)
if (tail_x[i] == x && tail_y[i] == y)
return false;
return true;
void showMenu() {
clearScreen();
cout << "\n\t\tWelcome to The Snake Game!\n\n";
cout << "\tChoose difficulty:\n";
cout << "\t1. Easy (Slow speed)\n";
cout << "\t2. Medium (Balanced speed)\n";
cout << "\t3. Hard (Fast speed)\n";
cout << "\t4. Extreme (Very fast)\n\n";
cout << "\tPress Q to quit\n";
while (true) {
if (inputAvailable()) {
char choice = getchar();
switch (choice) {
case '1': initDifficulty(EASY); return;
case '2': initDifficulty(MEDIUM); return;
case '3': initDifficulty(HARD); return;
case '4': initDifficulty(EXTREME); return;
case 'q': case 'Q':
resetTerminalAttributes();
exit(0);
void initDifficulty(Difficulty diff) {
difficulty = diff;
switch (diff) {
case EASY: base_speed = 250000; min_speed = 100000; break;
case MEDIUM: base_speed = 180000; min_speed = 60000; break;
case HARD: base_speed = 120000; min_speed = 40000; break;
case EXTREME: base_speed = 100000; min_speed = 30000; break;
current_speed = base_speed;
void draw() {
clearScreen();
cout << "\t\tSnake Game - ";
switch (difficulty) {
case EASY: cout << "Easy"; break;
case MEDIUM: cout << "Medium"; break;
case HARD: cout << "Hard"; break;
case EXTREME: cout << "Extreme"; break;
cout << endl;
if (dir == STOP)
cout << "\t\tPAUSED (Press SPACE to resume)\n";
for (int i = 0; i < WIDTH + 2; i++) cout << "#";
cout << endl;
for (int i = 0; i < HEIGHT; i++) {
cout << "#";
for (int j = 0; j < WIDTH; j++) {
if (i == head_y && j == head_x)
cout << "O";
else if (i == fruit_y && j == fruit_x)
cout << "F";
else {
bool printed = false;
for (size_t k = 0; k < tail_x.size(); k++) {
if (tail_x[k] == j && tail_y[k] == i) {
cout << "o";
printed = true;
break;
if (!printed)
cout << " ";
cout << "#" << endl;
for (int i = 0; i < WIDTH + 2; i++) cout << "#";
cout << "\nScore: " << score << " | Speed: " << current_speed / 1000 << "ms\n";
cout << "Controls: WASD = Move | SPACE = Pause | M = Menu | X = Exit\n";
void input() {
static Direction last_dir = RIGHT;
if (inputAvailable()) {
char ch = getchar();
switch (ch) {
case 'a': case 'A':
if (dir != RIGHT) dir = LEFT;
break;
case 's': case 'S':
if (dir != UP) dir = DOWN;
break;
case 'd': case 'D':
if (dir != LEFT) dir = RIGHT;
break;
case 'w': case 'W':
if (dir != DOWN) dir = UP;
break;
case 'x': case 'X':
resetTerminalAttributes();
exit(0);
case ' ':
if (dir == STOP) dir = last_dir;
else { last_dir = dir; dir = STOP; }
break;
case 'm': case 'M':
showMenu();
setup();
break;
void gamePlay() {
if (dir == STOP) return;
for (int i = tail_x.size() - 1; i > 0; i--) {
tail_x[i] = tail_x[i - 1];
tail_y[i] = tail_y[i - 1];
}
if (!tail_x.empty()) {
tail_x[0] = head_x;
tail_y[0] = head_y;
switch (dir) {
case UP: head_y--; break;
case DOWN: head_y++; break;
case LEFT: head_x--; break;
case RIGHT: head_x++; break;
default: break;
if (head_x < 0) head_x = WIDTH - 1;
if (head_x >= WIDTH) head_x = 0;
if (head_y < 0) head_y = HEIGHT - 1;
if (head_y >= HEIGHT) head_y = 0;
for (size_t i = 0; i < tail_x.size(); i++) {
if (tail_x[i] == head_x && tail_y[i] == head_y) {
cout << "\nGame Over! Final score: " << score << endl;
resetTerminalAttributes();
exit(0);
if (head_x == fruit_x && head_y == fruit_y) {
score += 10;
tail_x.push_back(head_x);
tail_y.push_back(head_y);
do {
fruit_x = rand() % WIDTH;
fruit_y = rand() % HEIGHT;
} while (!isPositionFree(fruit_x, fruit_y));
public:
SnakeGame() {
srand(time(NULL));
setTerminalAttributes();
dir = STOP;
score = 0;
fruit_x = fruit_y = 0;
head_x = WIDTH / 2;
head_y = HEIGHT / 2;
~SnakeGame() {
resetTerminalAttributes();
void setup() {
head_x = WIDTH / 2;
head_y = HEIGHT / 2;
dir = STOP;
score = 0;
tail_x.clear();
tail_y.clear();
do {
fruit_x = rand() % WIDTH;
fruit_y = rand() % HEIGHT;
} while (!isPositionFree(fruit_x, fruit_y));
current_speed = base_speed;
void run() {
showMenu();
setup();
while (true) {
draw();
input();
gamePlay();
int target_speed = base_speed - (score * 3000);
if (target_speed < min_speed) target_speed = min_speed;
current_speed = target_speed;
usleep(current_speed);
};
int main() {
SnakeGame game;
[Link]();
return 0;
}