How I Created a Classic Snake Game Using C

How I Created a Classic Snake Game Using C

A Step-by-Step Guide

Snake game is a classic arcade game that has been popular for decades. The objective of the game is to control a snake that moves around the screen, eating apples and growing in length. However, the game ends when the snake crashes into a wall or its own body.

In this article, we will explore how the game Snake was coded in C programming language using the code provided. The Snake game is a console-based game, which means that it runs in the terminal window of your operating system. The code uses the ANSI escape codes to draw characters on the terminal window and to move the cursor around.

The code starts with a set of header files including stdio.h, stdlib.h, unistd.h, and termios.h. stdio.h is used for input and output operations. stdlib.h provides memory allocation functions, and unistd.h provides access to the POSIX operating system API. termios.h provides terminal input/output control.

Next, the code defines the size of the game board using the preprocessor directive #define. The game board has 60 columns and 30 rows.

The main() function of the code starts by hiding the cursor using the ANSI escape code \e[?25l. It then switches the terminal to canonical mode and disables echo so that the input from the user is read immediately without the need for pressing the Enter key.

The code then defines two arrays of integers, x and y, which represent the position of the snake on the game board. The head and tail of the snake are initially set to 0. The snake's starting position is set to the center of the game board. The gameover variable is set to 0, and the direction of the snake is set to move to the right.

The code then enters a loop that runs until the game is over or the player quits. The loop first renders the game board by drawing characters on the terminal window using ANSI escape codes. It then moves the cursor to the top of the game board so that it can be updated.

Inside the loop, a nested while loop runs until the game is over or the player quits. The inner loop first creates a new apple at a random position on the game board. The apple is only created if it does not overlap with the snake's body.

The snake's tail is then cleared by drawing a dot on the terminal window. If the snake's head is on the apple's position, the apple is eaten, and a bell sound is played. If the snake's head is not on the apple's position, the tail of the snake is moved to the next position.

The snake's head is then moved in the direction of its movement, and the head position is updated. If the snake's head overlaps with its body, the game is over. The head of the snake is then drawn on the terminal window.

The program then waits for a short time, and the keyboard is checked for input. If the user presses a key, the direction of the snake is updated accordingly. If the user presses 'q', the game is quit, and the program exits the loop.

Finally, when the game is over, the program shows a message on the terminal window. The cursor is then shown again using the ANSI escape code \e[?25h, and the terminal is set back to its original state.

In conclusion, the Snake game was coded using the C programming language and ANSI escape codes to draw the game board and move the cursor. The game's logic is based on updating the position of the snake and the apple and checking for collisions. The game also allows the player to control the snake's direction using the keyboard.

/*
 * Snake Game
 * Author: GN-z11Codes
 * Version: 1.0
 * Date: 2023-05-15
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>

#define COLS 60
#define ROWS 30

int main(){
  // Hide cursor
  printf("\e[?25l");

  // Switch to canonical mode, disable echo
  struct termios oldt, newt;
  tcgetattr(STDIN_FILENO, &oldt);
  newt = oldt;
  newt.c_lflag &= ~(ICANON | ECHO);
  tcsetattr(STDIN_FILENO, TCSANOW, &newt);

  int x[1000], y[1000];
  int quit = 0;
  while(!quit){
    // Render table
    printf("┌");
    for(int i = 0; i < COLS; i++)
      printf("─");
    printf("┐\n");

    for(int j = 0; j < ROWS; j++){
      printf("│");
      for(int i = 0; i < COLS; i++)
        printf("·");
      printf("│\n");
    }

    printf("└");
    for(int i = 0; i < COLS; i++)
      printf("─");
    printf("┘\n");

    // Move cursor back to top
    printf("\e[%iA", ROWS + 2);

    int head = 0, tail = 0;
    x[head] = COLS / 2;
    y[head] = ROWS / 2;
    int gameover = 0;
    int xdir = 1, ydir = 0;
    int applex = -1, appley;

    while(!quit && !gameover){
      if(applex < 0){
        // Create new apple
        applex = rand() % COLS;
        appley = rand() % ROWS;

        for(int i = tail; i != head; i = (i + 1) % 1000)
          if(x[i] == applex && y[i] == appley)
            applex = -1;

        if(applex >= 0){
          // Draw apple
          printf("\e[%iB\e[%iC❤", appley + 1, applex + 1);
          printf("\e[%iF", appley + 1);
        }
      }

      // Clear snake tail
      printf("\e[%iB\e[%iC·", y[tail] + 1, x[tail] + 1);
      printf("\e[%iF", y[tail] + 1);

      if(x[head] == applex && y[head] == appley){
        applex = -1;
        printf("\a"); // Bell
      } else
        tail = (tail + 1) % 1000;

      int newhead = (head + 1) % 1000;
      x[newhead] = (x[head] + xdir + COLS) % COLS;
      y[newhead] = (y[head] + ydir + ROWS) % ROWS;
      head = newhead;

      for(int i = tail; i != head; i = (i + 1) % 1000)
        if(x[i] == x[head] && y[i] == y[head])
          gameover = 1;

      // Draw head
      printf("\e[%iB\e[%iC▓", y[head] + 1, x[head] + 1);
      printf("\e[%iF", y[head] + 1);
      fflush(stdout);

      usleep(5 * 1000000 / 60);

      // Read keyboard
      struct timeval tv;
      fd_set fds;
      tv.tv_sec = 0;
      tv.tv_usec = 0;

      FD_ZERO(&fds);
      FD_SET(STDIN_FILENO, &fds);
      select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
      if(FD_ISSET(STDIN_FILENO, &fds)){
        int ch = getchar();
        if(ch == 27 || ch == 'q'){
          quit = 1;
        } 
    else if(ch == 'h' && xdir != 1){
          xdir = -1;
          ydir = 0;
        } 
    else if(ch == 'l' && xdir != -1){
          xdir = 1;
          ydir = 0;
        } 
    else if(ch == 'j' && ydir != -1){
          xdir = 0;
          ydir = 1;
        } 
    else if(ch == 'k' && ydir != 1){
          xdir = 0;
          ydir = -1;
        }
      }
    }

    if(!quit){
      // Show game over
      printf("\e[%iB\e[%iC Game Over! ", ROWS / 2, COLS / 2 - 5);
      printf("\e[%iF", ROWS / 2);
      fflush(stdout);
      getchar();
    }
  }

  // Show cursor
  printf("\e[?25h");

  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);

  return 0;

}