Implementation of Snake Game in C:
To implement the snake game in C, we'll break down the problem into smaller components. First, we'll represent the game grid using a 2D array, where each cell can be empty, occupied by the snake, or contain a fruit. We'll use a struct to store the snake's position, length, & direction of movement. The fruit will be randomly placed on the grid, and the snake will grow longer each time it eats the fruit. The game will end if the snake collides with the boundaries or its own tail.
Let’s look at the steps it might have :
1. Initialize the game grid & snake's starting position
2. Render the game grid, snake, & fruit on the screen
3. Handle user input to change the snake's direction
4. Update the snake's position based on its current direction
5. Check for collisions with the boundaries, fruit, or snake's own tail
6. If the snake eats a fruit, increase its length & randomly place a new fruit
7. Repeat steps 3-6 until the game ends
Snake Implementation Logic
The snake is the main character in the game, and we'll represent it using an array of coordinates. Each coordinate represents a segment of the snake's body. The snake's head will be the first element in the array, and its tail will be the last element.
To move the snake, we'll update the coordinates of each segment based on the direction of movement. For example, if the snake is moving right, we'll subtract 1 from the x-coordinate of each segment. If the snake is moving up, we'll add 1 to the y-coordinate of each segment.
For example:
#define MAX_LENGTH 100
struct Snake {
int x[MAX_LENGTH];
int y[MAX_LENGTH];
int length;
int direction;
};
void moveSnake(struct Snake* snake) {
int i;
for (i = snake->length - 1; i > 0; i--) {
snake->x[i] = snake->x[i - 1];
snake->y[i] = snake->y[i - 1];
}
switch (snake->direction) {
case UP:
snake->y[0]--;
break;
case DOWN:
snake->y[0]++;
break;
case LEFT:
snake->x[0]--;
break;
case RIGHT:
snake->x[0]++;
break;
}
}
Fruit Implementation Logic
The fruit is the object that the snake must eat to grow longer. We'll represent the fruit using a simple struct with x and y coordinates.
This is an example of how to define the Fruit struct:
struct Fruit {
int x;
int y;
};
To place the fruit randomly on the game grid, we can use the rand() function from the standard library. We'll generate random x and y coordinates within the bounds of the grid and ensure that the fruit doesn't spawn on top of the snake.
Let’s look at a sample function to place the fruit randomly:
void placeFruit(struct Fruit* fruit, struct Snake* snake, int gridWidth, int gridHeight) {
int x, y;
do {
x = rand() % gridWidth;
y = rand() % gridHeight;
} while (isFruitOnSnake(x, y, snake));
fruit->x = x;
fruit->y = y;
}
int isFruitOnSnake(int x, int y, struct Snake* snake) {
int i;
for (i = 0; i < snake->length; i++) {
if (snake->x[i] == x && snake->y[i] == y) {
return 1;
}
}
return 0;
}
Boundary Implementation Logic
The game boundaries are the edges of the grid that the snake must not cross. If the snake collides with a boundary, the game ends.
To implement the boundary logic, we'll check if the snake's head (x[0], y[0]) has gone beyond the grid dimensions. Here's an example of how to check for boundary collisions:
int isCollision(struct Snake* snake, int gridWidth, int gridHeight) {
if (snake->x[0] < 0 || snake->x[0] >= gridWidth || snake->y[0] < 0 || snake->y[0] >= gridHeight) {
return 1;
}
return 0;
}
In this function, we check if the x-coordinate of the snake's head is less than 0 (left boundary) or greater than or equal to the grid width (right boundary). Similarly, we check if the y-coordinate is less than 0 (top boundary) or greater than or equal to the grid height (bottom boundary).
If any of these conditions are true, the function returns 1, indicating a collision. Otherwise, it returns 0.
You can call this function in your main game loop to check for collisions and end the game if necessary:
if (isCollision(snake, gridWidth, gridHeight)) {
gameOver = 1;
}
Game Motion Implementation Logic
To create smooth game motion, we'll update the snake's position and redraw the game grid at regular intervals. To control the game's speed, we can use a timer or a delay function.
Let’s look at an example of how to implement game motion using a delay function:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void gameLoop(struct Snake* snake, struct Fruit* fruit, int gridWidth, int gridHeight) {
while (!gameOver) {
// Clear the screen
system("clear");
// Move the snake
moveSnake(snake);
// Check for collision with boundaries
if (isCollision(snake, gridWidth, gridHeight)) {
gameOver = 1;
break;
}
// Check for collision with fruit
if (snake->x[0] == fruit->x && snake->y[0] == fruit->y) {
// Increase snake length
snake->length++;
// Place new fruit
placeFruit(fruit, snake, gridWidth, gridHeight);
}
// Draw the game grid, snake, and fruit
drawGrid(gridWidth, gridHeight);
drawSnake(snake);
drawFruit(fruit);
// Delay to control game speed
usleep(100000); // Delay for 100 milliseconds
}
}
In this example, we use a while loop to continuously update the game state until the game is over. Inside the loop, we clear the screen, move the snake, check for collisions with boundaries and fruit, and redraw the game elements.
The `usleep()` function is used to introduce a delay between each game update, controlling the game's speed. You can adjust the delay value to make the game faster or slower.
Real Time Response Logic
To make the game interactive, we need to handle user input in real-time. We'll use the `kbhit()` function to check if a key has been pressed and the `getch()` function to retrieve the pressed key.
For example:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
void handleInput(struct Snake* snake) {
if (kbhit()) {
switch (getch()) {
case 'w':
case 'W':
if (snake->direction != DOWN)
snake->direction = UP;
break;
case 's':
case 'S':
if (snake->direction != UP)
snake->direction = DOWN;
break;
case 'a':
case 'A':
if (snake->direction != RIGHT)
snake->direction = LEFT;
break;
case 'd':
case 'D':
if (snake->direction != LEFT)
snake->direction = RIGHT;
break;
}
}
}
In this example, we define a `handleInput()` function that checks if a key has been pressed using `kbhit()`. If a key is pressed, we use `getch()` to retrieve the pressed key.
We then use a switch statement to check which key was pressed and update the snake's direction accordingly. We also add a check to prevent the snake from reversing its direction (e.g., moving directly from UP to DOWN).
You can call the `handleInput()` function in your game loop to handle user input in real-time:
void gameLoop(struct Snake* snake, struct Fruit* fruit, int gridWidth, int gridHeight) {
while (!gameOver) {
// ...
// Handle user input
handleInput(snake);
// ...
}
}
Snake Game Code in C:
Now that we've discussed all the key components of the snake game let's put everything together into a complete C program.
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <unistd.h>
#define MAX_LENGTH 100
#define GRID_WIDTH 20
#define GRID_HEIGHT 20
enum Direction {
UP,
DOWN,
LEFT,
RIGHT
};
struct Snake {
int x[MAX_LENGTH];
int y[MAX_LENGTH];
int length;
enum Direction direction;
};
struct Fruit {
int x;
int y;
};
void initSnake(struct Snake* snake) {
snake->length = 1;
snake->x[0] = GRID_WIDTH / 2;
snake->y[0] = GRID_HEIGHT / 2;
snake->direction = RIGHT;
}
void placeFruit(struct Fruit* fruit, struct Snake* snake) {
// ... (Code for placing fruit randomly)
}
void moveSnake(struct Snake* snake) {
// ... (Code for moving the snake)
}
int isCollision(struct Snake* snake) {
// ... (Code for checking collisions)
}
void handleInput(struct Snake* snake) {
// ... (Code for handling user input)
}
void drawGrid() {
// ... (Code for drawing the game grid)
}
void drawSnake(struct Snake* snake) {
// ... (Code for drawing the snake)
}
void drawFruit(struct Fruit* fruit) {
// ... (Code for drawing the fruit)
}
int main() {
struct Snake snake;
struct Fruit fruit;
int gameOver = 0;
initSnake(&snake);
placeFruit(&fruit, &snake);
while (!gameOver) {
system("clear");
handleInput(&snake);
moveSnake(&snake);
if (isCollision(&snake)) {
gameOver = 1;
}
if (snake.x[0] == fruit.x && snake.y[0] == fruit.y) {
snake.length++;
placeFruit(&fruit, &snake);
}
drawGrid();
drawSnake(&snake);
drawFruit(&fruit);
usleep(100000);
}
printf("Game Over! Your score: %d\n", snake.length - 1);
return 0;
}

You can also try this code with Online C Compiler
Run Code
This is a complete snake game implementation in C. The code includes all the necessary functions and structures we discussed earlier.
In the `main()` function, we initialize the snake and fruit, and then enter the game loop. Inside the loop, we handle user input, move the snake, check for collisions and fruit eating, and redraw the game elements.
Once the game is over, we print the final score (the snake's length minus 1) and exit the program.
Just remember that some functions like `placeFruit()`, `drawGrid()`, `drawSnake()`, and `drawFruit()` are not fully implemented here and would require additional code based on your specific requirements and chosen graphics library.
Frequently Asked Questions
How can I modify the game speed?
To change the game speed, adjust the delay value in the `usleep()` function call in the game loop. A smaller value will make the game faster, while a larger value will slow it down.
Can I add obstacles to the game?
Yes, you can add obstacles by creating a new struct for obstacles and modifying the collision detection logic to check for collisions with obstacles in addition to boundaries and the snake's own tail.
How can I enhance the game's visual appearance?
To improve the game's visuals, you can use a graphics library like SDL or ncurses to render the game elements with colors, textures, or images instead of simple ASCII characters.
Conclusion
In this article, we discussed the implementation of a classic snake game in C. We covered the essential components, including the snake's movement, fruit placement, collision detection, boundary handling, game motion, & real-time user input. By breaking down the problem into smaller parts & using basic C programming concepts, we created a functional snake game that you can now run & enjoy. With the knowledge gained from this article, you can further enhance the game by adding new features, improving the visuals, or optimizing the code.
You can also check out our other blogs on Code360.