The Snake game is a simple arcade game where the player controls a snake that moves around to eat food. The goal is to keep eating food without crashing into the walls. The game ends when the snake crashes, with the player's score based on how much food was collected.
In this article, we will learn how to create snake game using C programming language.
Prerequisite
We can create a console-based snake game using basic knowledge of C along with the following prerequisites:
- Working C compiler and IDE.
- For windows, we need <windows.h>. For linux, we need <unistd.h>. These libraries are generally installed by default.
- We might need to install <console.h> library as it does not comes bundled with the standard C compiler.
Gameplay
The gameplay of the snake game is simple. There are three game objects:
- Snake: The snake automatically moves into given direction till we change it. It has a mouth and tail.
- Fruit: The object that snake has to eat to grow.
- Walls: They are the boundary of the game area.
These objects work according to the game rules which are as follows:
- The snake moves at a constant speed in one direction, and we can change its direction using the movement keys. However, the snake can only turn 90° at a time.
- The objective is to eat as many fruits as possible, which will make the snake to grow in length.
- The game ends if the snake collides with the boundary or its own body.
Implementation of Snake Game in C
Our objective is to create an interactive console-based snake game using C. The whole console screen can be visualized as the grid where each point has two coordinates, x-axis and y-axis coordinates. The position of each object the game will be described by these coordinates. All the game will be based on the continuous updating of these coordinates. The below is the intuition for the implementation of different game mechanics:
Snake Implementation Logic
The snake can be represented by the coordinates of its body stored in two arrays:
- One Array for x-axis coordinates.
- One Array for y-axis coordinates.
The head of the snake moves according to the user input:
- W for up
- S for down
- A for left
- D for right
while the rest of the body follows. When the snake eats some fruit, its length increases, and the coordinate array is updated. Collision detection is implemented to checking if the snake coordinates overlap (self-collision) or goes over the boundary limit (boundary collision).
Fruit Implementation Logic
The fruit is represented as a single coordinate within the game area. It is randomly placed inside the boundary, ensuring it doesn't spawn outside the playable area. The fruit have the following functions:
- Random Positioning: The fruit's position is generated using random coordinates within the height and width of the game area, excluding the boundary.
- Snake Eating: If the snake's head reaches the fruit's position, the fruit is "eaten", the snake grows in length, and the score increases.
Boundary Implementation Logic
The boundary defines the playing area of the game. It is created by drawing a box or rectangle that surrounds the playing field using some characters (usually # or | for the sides and - for the top and bottom), and it acts as a collision zone. We can define macros for setting the boundary limits.
Game Motion Implementation Logic
The motion in game is generated by an infinite loop that continuously updates the position of the snake and checks for game events such as collisions, fruit consumption or user input. In each iteration of the loop, the snake moves one step in the current direction, and the screen is redrawn in place or previous one to reflect the new position. It is similar to creating static images as frames and move them fast enough to create a motion.
Real Time Response Logic
Generally, we need to press the Enter key for the input buffer to supply data to the program. But this does not provide real time response. For real time interactive response, we use the kbhit() function provided by <conio.h>. This function returns the pressed character immediately as soon as the key is pressed on the keyboard.
Snake Game Code in C
#include <conio.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <time.h>
#define HEIGHT 20
#define WIDTH 40
int snakeTailX[100], snakeTailY[100];
int snakeTailLen;
int gameover, key, score;
int x, y, fruitx, fruity;
// Function to move cursor to (0,0) to prevent flickering
void goToXY(int x, int y) {
COORD coord;
coord.X = x;
coord.Y = y;
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
void setup() {
srand(time(NULL));
gameover = 0;
score = 0;
snakeTailLen = 0;
// Initial snake position
x = WIDTH / 2;
y = HEIGHT / 2;
// Initial direction (0 = stopped)
key = 0;
// Initial fruit position
fruitx = rand() % (WIDTH - 2) + 1;
fruity = rand() % (HEIGHT - 2) + 1;
}
void draw() {
goToXY(0, 0); // Reset cursor instead of system("cls")
for (int i = 0; i < WIDTH + 2; i++) printf("-");
printf("\n");
for (int i = 0; i < HEIGHT; i++) {
for (int j = 0; j <= WIDTH; j++) {
if (j == 0 || j == WIDTH) {
printf("#");
}
if (i == y && j == x) {
printf("O"); // Head
} else if (i == fruity && j == fruitx) {
printf("*"); // Fruit
} else {
int prTail = 0;
for (int k = 0; k < snakeTailLen; k++) {
if (snakeTailX[k] == j && snakeTailY[k] == i) {
printf("o"); // Body
prTail = 1;
break;
}
}
if (!prTail) printf(" ");
}
}
printf("\n");
}
for (int i = 0; i < WIDTH + 2; i++) printf("-");
printf("\nScore: %d\n", score);
printf("Controls: W, A, S, D | X to Quit\n");
}
void input() {
if (_kbhit()) {
switch (tolower(_getch())) {
case 'a': if(key != 2) key = 1; break;
case 'd': if(key != 1) key = 2; break;
case 'w': if(key != 4) key = 3; break;
case 's': if(key != 3) key = 4; break;
case 'x': gameover = 1; break;
}
}
}
void logic() {
// 1. Logic for tail following
int prevX = snakeTailX[0];
int prevY = snakeTailY[0];
int prev2X, prev2Y;
snakeTailX[0] = x;
snakeTailY[0] = y;
for (int i = 1; i < snakeTailLen; i++) {
prev2X = snakeTailX[i];
prev2Y = snakeTailY[i];
snakeTailX[i] = prevX;
snakeTailY[i] = prevY;
prevX = prev2X;
prevY = prev2Y;
}
// 2. Direction movement
switch (key) {
case 1: x--; break;
case 2: x++; break;
case 3: y--; break;
case 4: y++; break;
}
// 3. Wall Collision
if (x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT)
gameover = 1;
// 4. Tail Collision
for (int i = 0; i < snakeTailLen; i++) {
if (snakeTailX[i] == x && snakeTailY[i] == y)
gameover = 1;
}
// 5. Eating Fruit
if (x == fruitx && y == fruity) {
score += 10;
snakeTailLen++;
fruitx = rand() % (WIDTH - 2) + 1;
fruity = rand() % (HEIGHT - 2) + 1;
}
}
int main() {
// Clear screen once at the start
system("cls");
setup();
while (!gameover) {
draw();
input();
logic();
Sleep(40); // Controls game speed
}
_getch(); // Wait for user input before closing window
return 0;
}
Output: