C++ Snake Games with Source Code

The snake game was created by programmers in 1970 on Nokia 6110, is still one of the most famous games.

Its creators kept on advancing the game to such an extent that the game is now available with and without graphics, on the featured phones to smartphones and even laptops.

Let’s learn some basic information regarding Snake Games without graphics. Initially, the snake game was developed in C++ language, and still, in advanced games, c++ game engines are used.

How to play a snake game in general?

Following are the guidelines which are required to play a Snake Game:

  1. The snake game is a solo player game.
  2. It consists of a snake (you) that has to eat an apple to grow.
  3. The snake has to move within its given parameters which is a rectangular box.
  4. The snake aims to eat as many apples as it can without touching the walls or its tail.
  5. Keys AWSD are used to move this snake.

A sample of the snake game with minimum graphics created in C++:

snake game in C++ with source code

Snake Game in C++ Without Graphics

C++ Snake Games with source code can be developed by using several ways. One of the ways among these is developing Snake Game without Graphics. 

Developing a Snake Game in C++

A basic snake game in C++ without graphics can be developed by following these functionalities:

The functionalities of the game

  1. A symbol representing the snake is 0 (zero).
  2. A symbol representing the fruit is * (asterisk).
  3. (W, A, S, D) keys help the snake to propagate in any direction.
  4. In case the snake eats a fruit, 10 points will be awarded to the user.
  5. Automatically, the fruit will be produced within the parameters.
  6. The game will be ended if the snake touches its tail or the boundary. 
Snake game in C source Code - C++ Snake Games with Source Code

Steps Leading to the Creation of Snake game

Four steps are basically needed to create a Snake Game without graphics. Following are those steps:

  • There are four user-defined functions.
  • To play the game, build a rectangular boundary.
  • Random generation of the fruit.
  • Increment in the score and growth of the snake on eating the fruit.

User-defined Functions

Following user-defined functions are used in making a Snake Game.

snake game in C++ without graphics
  • Draw( )  function is used for the creation of the boundaries. The code written below is used to build the boundaries.
// boundary using draw()
#include <stdio.h>

#include <stdlib.h>

int i, j, height = 17;
int width = 20, gameover, score;

// Function to draw a boundary
void draw() {
  // system("cls");
  for (i = 0; i < height; i++) {
    for (j = 0; j < width; j++) {
      if (i == 0 || i == width - 1 || j == 0 ||
        j == height - 1) {
        printf("#");
      } else {
        printf(" ");
      }
    }
    printf("\n");
  }
}

// Driver Code
int main() {
  // Function Call
  draw();

  return 0;
}
  • Setup( ) function will position the fruit within the boundaries. To generate the fruit within the boundary, the following code of the program will be used.
void setup() {
  gameover = 0;

  // Stores height and width
  x = height / 2;
  y = width / 2;
  label1:
    fruitx = rand() % 20;
  if (fruitx == 0)
    goto label1;
  label2:
    fruity = rand() % 20;
  if (fruity == 0)
    goto label2;
  score = 0;
}
  • Input( ) function helps to take the input from the keyboard keys that are A, W, S, D. The piece of code written below shows the input.
void input() {
  if (kbhit()) {
    switch (getch()) {
    case 'a':
      flag = 1;
      break;
    case 's':
      flag = 2;
      break;
    case 'd':
      flag = 3;
      break;
    case 'w':
      flag = 4;
      break;
    case 'x':
      gameover = 1;
      break;
    }
  }
}
  • Logic( ) function is used to set the snake’s movement, score increment, ending the game when the snake touches the boundary or its tail, Radom fruit generation after the snake eats the fruit and exits the game. The function for logic is in the stated below:
void logic() {
  sleep(0.01);
  switch (flag) {
  case 1:
    y--;
    break;
  case 2:
    x++;
    break;
  case 3:
    y++;
    break;
  case 4:
    x--;
    break;
  default:
    break;
  }

  // If the game is over
  if (x < 0 || x > height ||
    y < 0 || y > width)
    gameover = 1;

  // If snake reaches the fruit
  // then update the score
  if (x == fruitx && y == fruity) {
    label3: fruitx = rand() % 20;
    if (fruitx == 0)
      goto label3;

    // After eating the above fruit
    // generate new fruit
    label4: fruity = rand() % 20;
    if (fruity == 0)
      goto label4;
    score += 10;
  }
}
  • Main( ) starts the execution of the program as it calls all the functions being used in that program. 
void main() {
  int m, n;

  // Generate boundary
  setup();

  // Until the game is over
  while (!gameover) {

    // Function Call
    draw();
    input();
    logic();
  }
}

Built-in Functions

Following built-in functions are used to create Snake Game.

  1. kbhit ():

The function is used in C++ or C language for the key determination if they have been pressed or not. A non-zero value is returned by pressing the key. conio.h header filer is used as a function in the program.

  1. rand ():

On using the header file stdlib.h, the function rand() is declared. This function helps to return a random integer value every time it is called.

Header Files and Variables

Following header files and variables are used in this program.

Summing up everything:

// C program to build the complete
// snake game
#include <conio.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

int i, j, height = 17, width = 20;
int gameover, score;
int x, y, fruitx, fruity, flag;

// Function to generate the fruit
// within the boundary
void setup() {
  gameover = 0;

  // Stores height and width
  x = height / 2;
  y = width / 2;
  label1:
    fruitx = rand() % 20;
  if (fruitx == 0)
    goto label1;
  label2:
    fruity = rand() % 20;
  if (fruity == 0)
    goto label2;
  score = 0;
}

// Function to draw the boundaries
void draw() {
  system("cls");
  for (i = 0; i < height; i++) {
    for (j = 0; j < width; j++) {
      if (i == 0 || i == width - 1 ||
        j == 0 ||
        j == height - 1) {
        printf("#");
      } else {
        if (i == x && j == y)
          printf("0");
        else if (i == fruitx &&
          j == fruity)
          printf("*");
        else
          printf(" ");
      }
    }
    printf("\n");
  }

  // Print the score after the
  // game ends
  printf("score = %d", score);
  printf("\n");
  printf("press X to quit the game");
}

// Function to take the input
void input() {
  if (kbhit()) {
    switch (getch()) {
    case 'a':
      flag = 1;
      break;
    case 's':
      flag = 2;
      break;
    case 'd':
      flag = 3;
      break;
    case 'w':
      flag = 4;
      break;
    case 'x':
      gameover = 1;
      break;
    }
  }
}

// Function for the logic behind
// each movement
void logic() {
  sleep(0.01);
  switch (flag) {
  case 1:
    y--;
    break;
  case 2:
    x++;
    break;
  case 3:
    y++;
    break;
  case 4:
    x--;
    break;
  default:
    break;
  }

  // If the game is over
  if (x < 0 || x > height ||
    y < 0 || y > width)
    gameover = 1;

  // If snake reaches the fruit
  // then update the score
  if (x == fruitx && y == fruity) {
    label3: fruitx = rand() % 20;
    if (fruitx == 0)
      goto label3;

    // After eating the above fruit
    // generate new fruit
    label4: fruity = rand() % 20;
    if (fruity == 0)
      goto label4;
    score += 10;
  }
}

// Driver Code
void main() {
  int m, n;

  // Generate boundary
  setup();

  // Until the game is over
  while (!gameover) {
    // Function Call
    draw();
    input();
    logic();
  }
}

Result:

Snake game without graphics in C++

Snake Game with Graphics in C++

With the evolution of technology, the snake game has also been advanced as it has been sprinkled with the spices of graphics in it which makes the game more colorful and attractive for the young ones. 

Moreover, the use of graphics in the C++ Snake Game with Source Code has fulfilled all the needs that the snake game requires in this modern gaming era. 

Basic Functionalities of Snake Game with Graphics

Only some changes are made in the functionalities of Snake Game in C++ with Graphics which are as followed:

  1. A colorful snake is developed with the help of graphics.
  2. Randf() function of the graphics is used to create fruit.
  3. With the help of Up, Down, Left, and Right keyboard keys the snake can move in the direction we want.
  4. There will be an increment of 14 points when the snake eats a fruit.

Creation of Basic Controls

Some basic controls can be created in the Snake Game in C++ with Graphics which are as followed:

  1. P Control

By clicking on the P key from the keyboard we can pause our game at any time.

  1. G Control

G key is used to resume the paused game.

  1. E Control

E character is used to exit the snake game.

Source Code

Result:

snake game in C++ with graphics

Snake Games in C++ Using Classes

After writing a lot about the programming and codes of snake games for my dear users, I would verily like to discuss the classes which are used in C++ Snake Game.

Here I will thoroughly talk over the development of Snake Game Using C++ Classes. Several steps of classes lead to a professionally developed Snake Game in C++. Some of them are as follows.

Class Responsible for Starting and Running the Game

So, let’s debate over the class that is mainly responsible for initiating and running the game.

  • For the startup of the game OnCreate() function is called.
  • Once per frame functions OnUpdate() and OnRender() are called and the game loop should be its part.

When the inner game loop is quitting and the program is about to exit OnDestroy() function call is made.

class SnakeGame: public rge::DXGraphicsEngine {
  public: SnakeGame();
  bool OnCreate();
  bool OnUpdate();
  bool OnRender();
  void OnDestroy();

  protected: Snake snake;
  FieldGrid field;
  int score;
  bool gameOver;
  int updateFreq;
  int updateCounter;
};

//////////////////////// .cpp

SnakeGame::SnakeGame(): DXGraphicsEngine(), field(10, 10), snake(3, rge::v2di(3, 1), Direction::RIGHT), score(0), gameOver(false), updateFreq(10), updateCounter(0) {

}

bool SnakeGame::OnCreate() {
  field.AddSnake(snake.GetBody());
  field.GenerateFood();
  return true;
}

bool SnakeGame::OnUpdate() {

  //check user input
  if (GetKey(rge::W).pressed) {
    snake.SetDirection(Direction::UP);
  }
  if (GetKey(rge::S).pressed) {
    snake.SetDirection(Direction::DOWN);
  }
  if (GetKey(rge::A).pressed) {
    snake.SetDirection(Direction::LEFT);
  }
  if (GetKey(rge::D).pressed) {
    snake.SetDirection(Direction::RIGHT);
  }

  updateCounter++;
  if (!gameOver && updateCounter >= updateFreq) {
    updateCounter = 0;
    //clear snake body from field
    field.ClearSnake(snake.GetBody());
    //move
    snake.MoveSnake();
    //add snake body to field
    field.AddSnake(snake.GetBody());
    //testcollision
    CollisionMessage cm = field.CheckCollision(snake.GetHead());
    gameOver = cm.gameOver;
    score += cm.scoreChange ? snake.GetLength() * 10 : 0;
    if (cm.tileType == TileType::Food) {
      field.GenerateFood();
      snake.ExtendSnake();
    }
  }
  return true;
}

bool SnakeGame::OnRender() {
  std::cout << score << std::endl;
  field.Draw( & m_colorBuffer, 100, 20, 10);
  snake.DrawHead( & m_colorBuffer, 100, 20, 10);
  return true;
}

Snake and Direction Class

The snake class in this game is responsible for the movement and extension (growth) of the snake. An enum for the snake’s direction in which it is moving is also used. It is preferable to use these classes.

enum class Direction {
  UP, DOWN, LEFT, RIGHT
};

class Snake {
  public:
    Snake();
  Snake(int length, rge::v2di position, Direction direction);
  rge::v2di GetHead() {
    return head;
  }
  std::vector < rge::v2di > GetBody() {
    return body;
  }
  void MoveSnake();
  void ExtendSnake();
  Direction GetDirection() {
    return direction;
  }
  void SetDirection(Direction direction);
  int GetLength() {
    return body.size() + 1;
  }
  void DrawHead(rge::Buffer * buffer, int x, int y, int size);

  protected:
    std::vector < rge::v2di > body;
  rge::v2di head;
  Direction direction;
  Direction oldDirection;
};
Snake::Snake(): head(rge::v2di(0, 0)), direction(Direction::UP), oldDirection(Direction::UP), body(std::vector < rge::v2di > ()) {
  body.push_back(rge::v2di(head.x, head.y + 1));
}

Snake::Snake(int length, rge::v2di position, Direction direction): head(position), direction(direction), oldDirection(direction), body(std::vector < rge::v2di > ()) {
  for (int i = 0; i < length - 1; ++i) {
    rge::v2di bodyTile;
    switch (direction) {
    case Direction::UP: {
      bodyTile.x = head.x;
      bodyTile.y = head.y + (i + 1);
      break;
    }
    case Direction::DOWN: {
      bodyTile.x = head.x;
      bodyTile.y = head.y - (i + 1);
      break;
    }
    case Direction::LEFT: {
      bodyTile.y = head.y;
      bodyTile.x = head.x + (i + 1);
      break;
    }
    case Direction::RIGHT: {
      bodyTile.y = head.y;
      bodyTile.x = head.x - (i + 1);
      break;
    }
    }
    body.push_back(bodyTile);
  }
}

void Snake::MoveSnake() {
  oldDirection = direction;
  for (int i = body.size() - 1; i > 0; --i) {
    body[i] = body[i - 1];
  }
  body[0] = head;

  switch (direction) {
  case Direction::UP: {
    head.y--;
    break;
  }
  case Direction::DOWN: {
    head.y++;
    break;
  }
  case Direction::LEFT: {
    head.x--;
    break;
  }
  case Direction::RIGHT: {
    head.x++;
    break;
  }
  }
}

void Snake::ExtendSnake() {
  body.push_back(body[body.size() - 1]);
}

void Snake::SetDirection(Direction direction) {
  switch (this -> oldDirection) {
  case Direction::UP:
  case Direction::DOWN: {
    if (direction != Direction::UP && direction != Direction::DOWN) {
      this -> direction = direction;
    }
    break;
  }
  case Direction::LEFT:
  case Direction::RIGHT: {
    if (direction != Direction::LEFT && direction != Direction::RIGHT) {
      this -> direction = direction;
    }
    break;
  }
  }
}

void Snake::DrawHead(rge::Buffer * buffer, int x, int y, int size) {
  rge::Color c(100, 100, 200);
  buffer -> DrawRegion(x + head.x * size, y + head.y * size, x + head.x * size + size, y + head.y * size + size, c.GetHex());
}

Field Grid Class

Fruit generation, collision detection, and storing the stat of the map are the responsibilities of the field grid class. A collision message is generated when a snake collides with itself or the boundary after which the message “game over” is displayed on the screens.

class FieldGrid {
  public:
    FieldGrid();
  FieldGrid(int width, int height);
  ~FieldGrid();
  void GenerateFood();
  CollisionMessage CheckCollision(rge::v2di head);
  void ClearSnake(std::vector < rge::v2di > body);
  void AddSnake(std::vector < rge::v2di > body);
  void Draw(rge::Buffer * buffer, int x, int y, int size);
  protected:
    std::vector < std::vector < Tile * >> field;
  int width;
  int height;
};

//////////// .cpp

FieldGrid::FieldGrid(): width(10), height(10), field(std::vector < std::vector < Tile * >> ()) {
  for (int i = 0; i < width; ++i) {
    field.push_back(std::vector < Tile * > ());
    for (int j = 0; j < height; ++j) {
      field[i].push_back(new EmptyTile());
    }
  }
}

FieldGrid::FieldGrid(int width, int height): width(width), height(height), field(std::vector < std::vector < Tile * >> ()) {
  for (int i = 0; i < width; ++i) {
    field.push_back(std::vector < Tile * > ());
    for (int j = 0; j < height; ++j) {
      field[i].push_back(new EmptyTile());
    }
  }
}

FieldGrid::~FieldGrid() {
  for (int i = 0; i < field.size(); ++i) {
    for (int j = 0; j < field[i].size(); ++j) {
      delete field[i][j];
    }
    field[i].clear();
  }
  field.clear();
}

void FieldGrid::GenerateFood() {
  int x = rand() % width;
  int y = rand() % height;
  while (!field[x][y] -> IsFree()) {
    x = rand() % width;
    y = rand() % height;
  }
  delete field[x][y];
  field[x][y] = new FoodTile();
}

CollisionMessage FieldGrid::CheckCollision(rge::v2di head) {
  if (head.x < 0 || head.x >= width || head.y < 0 || head.y >= height) {
    CollisionMessage cm;
    cm.scoreChange = false;
    cm.gameOver = true;
    return cm;
  }
  return field[head.x][head.y] -> OnCollide();
}

void FieldGrid::ClearSnake(std::vector < rge::v2di > body) {
  for (int i = 0; i < body.size(); ++i) {
    delete field[body[i].x][body[i].y];
    field[body[i].x][body[i].y] = new EmptyTile();
  }
}

void FieldGrid::AddSnake(std::vector < rge::v2di > body) {
  for (int i = 0; i < body.size(); ++i) {
    delete field[body[i].x][body[i].y];
    field[body[i].x][body[i].y] = new SnakeTile();
  }
}

void FieldGrid::Draw(rge::Buffer * buffer, int x, int y, int size) {
  for (int xi = 0; xi < width; ++xi) {
    for (int yi = 0; yi < height; ++yi) {
      int xp = x + xi * size;
      int yp = y + yi * size;
      field[xi][yi] -> Draw(buffer, xp, yp, size);
    }
  }
}

Tile Type Class

The usage of tile type class is made within the field grid. Generally, there are three types of tile type enum. 

  • Empty tile
  • Food tile

When the snake hits the food tile that is a fruit, a feeding event takes place where the snake eats the fruit and grows in length.

  • Snake tile

When the snake hits a snake tile that is its tail, a collision is detected and a collision message is generated. Hence, either the game finishes or the user loses one of its lives.

class Tile {
  public:
    virtual CollisionMessage OnCollide() = 0;
  virtual bool IsFree() = 0;
  void Draw(rge::Buffer * buffer, int x, int y, int size) {
    buffer -> DrawRegion(x, y, x + size, y + size, color.GetHex());
  }

  protected:
    rge::Color color;
};

class EmptyTile: public Tile {
  public: EmptyTile() {
    this -> color = rge::Color(50, 50, 50);
  }

  CollisionMessage OnCollide() {
    CollisionMessage cm;
    cm.scoreChange = false;
    cm.gameOver = false;
    cm.tileType = TileType::Empty;
    return cm;
  }

  bool IsFree() {
    return true;
  }
};

class FoodTile: public Tile {
  public: FoodTile() {
    this -> color = rge::Color(50, 200, 70);
  }
  CollisionMessage OnCollide() {
    CollisionMessage cm;
    cm.scoreChange = true;
    cm.gameOver = false;
    cm.tileType = TileType::Food;
    return cm;
  }

  bool IsFree() {
    return false;
  }
};

class SnakeTile: public Tile {
  public: SnakeTile() {
    this -> color = rge::Color(120, 130, 250);
  }

  CollisionMessage OnCollide() {
    CollisionMessage cm;
    cm.scoreChange = false;
    cm.gameOver = true;
    cm.tileType = TileType::Snake;
    return cm;
  }

  bool IsFree() {
    return false;
  }
};

Collision Message Class

Whenever the snake’s head collides with any of the tile collision messages are sent to the game. This shows that the collision message class is dependent upon the tile type class and is mainly associated with the snake game.

enum class TileType {
  Empty,
  Snake,
  Food
};

class CollisionMessage {
  public:
    bool scoreChange;
  bool gameOver;
  TileType tileType;
};

Your efforts in reading all classic Snake Games in C++ are appreciated as it is really time-consuming. Moreover, all the #includes and main methods are omitted from the pieces of the codes as they would just take up extra space. 

If you want to write a wholesome program, then I would ask you to add all the #includes the main methods, and the codes mentioned above.

Conclusion

I hope you are now well versed in the development of C++ snake games as we also have shared their source codes. So try making your own games using this information and let us know if you need further assistance.

Scroll to Top