Skip to content

Instantly share code, notes, and snippets.

@martytyty2098
Created November 8, 2022 08:40
Show Gist options
  • Save martytyty2098/baf42f8ff0bfae517971448ee0cdae19 to your computer and use it in GitHub Desktop.
Save martytyty2098/baf42f8ff0bfae517971448ee0cdae19 to your computer and use it in GitHub Desktop.
This is my Object Oriented snake game in C++
#include <iostream>
#include <string>
#include <conio.h>
#include <vector>
#include <chrono>
#include <thread>
#include <ctime>
/* this is default terminal window size, you can change it if you want,
but if these variables are not equal to the actual size of terminal window,
this program will not work properly */
const int screen_width = 120;
const int screen_length = 29; // this variable must be 1 less than the actual length of the terminal window
enum dir { left = -1, up = -screen_width, right = 1, down = screen_width };
struct coordinate
{
int x_coord;
int y_coord;
};
// pauses the program for a while after each frame to make the game playable
inline void sleep(int time)
{
std::this_thread::sleep_for(std::chrono::milliseconds(time));
}
// storing all drawable items as Subclasses of this Superclass
class Drawable {
public:
virtual void draw() = 0;
};
class Field {
public:
Field();
void show();
inline unsigned char& operator()(int x, int y);
const unsigned char background = ' ';
// storing the map as 1d character array
unsigned char field[screen_length * screen_width + 1];
}map;
void Field::show() {
std::cout << field;
}
Field::Field()
{
for (int i = 0; i < screen_length * screen_width; ++i) {
field[i] = background;
}
field[screen_length * screen_width] = '\0';
}
// this function allows me to access a 1d array by x-y coordinates, as if it were 2d
inline unsigned char& Field::operator()(int x, int y)
{
return *(&field[0] + screen_width * x + y);
}
class Worm : public Drawable {
public:
Worm();
virtual void draw();
void move();
void get_direction(char user_tap);
bool check_collision();
unsigned char sumbol;
int length;
private:
// storing body of the snake as a vector
std::vector<coordinate*> body;
dir direction;
int actual_length();
}snake;
// checks if the snake's head hit its tail
bool Worm::check_collision()
{
// if x-y coordinates of the snake's head are equal to any part of its body, this function returns true
for (int i = 1; i < body.size(); ++i) {
if (body[0]->x_coord == body[i]->x_coord && body[0]->y_coord == body[i]->y_coord) {
return true;
}
}
return false;
}
// basic wasd controls
void Worm::get_direction(char user_tap)
{
switch (user_tap) {
case 'w':
if (direction != down) direction = up;
break;
case 'a':
if (direction != right) direction = left;
break;
case 's':
if (direction != up) direction = down;
break;
case 'd':
if (direction != left) direction = right;
break;
}
}
// counts how many parts of the snake's body are actually on the screen at the moment
int Worm::actual_length()
{
int count = 0;
for (int i = 0; i < screen_length * screen_width; ++i) {
if (map.field[i] == sumbol) {
count++;
}
}
return count;
}
// creates a new snake's body part, wich will become its head in the next frame
void Worm::move()
{
// location of new body part depends on the direction the snake is currently facing
coordinate* temp = new coordinate;
temp->x_coord = body[0]->x_coord;
temp->y_coord = body[0]->y_coord;
if (direction == up || direction == down) {
temp->x_coord = body[0]->x_coord + direction / screen_width;
}
if (direction == left || direction == right) {
temp->y_coord = body[0]->y_coord + direction;
}
// when the snake enters edge of the screen, it exits in the opposite
if (temp->x_coord < 0) temp->x_coord = screen_length - 1;
if (temp->x_coord >= screen_length) temp->x_coord = 0;
if (temp->y_coord < 0) temp->y_coord = screen_width - 1;
if (temp->y_coord >= screen_width) temp->y_coord = 0;
body.emplace(body.begin(), temp);
// if the snake has not eaten in this frame, removes the last part of its body
if (actual_length() >= length) {
body.pop_back();
}
}
// draws all body parts of the snake on the screen
void Worm::draw() {
for (int i = 0; i < body.size(); ++i) {
map(body[i]->x_coord, body[i]->y_coord) = sumbol;
}
}
Worm::Worm()
:length(4),
direction(up),
sumbol(219u)
{
// each body part of the snake is represented in x-y coordinates
for (int x_temp = screen_length / 2, end = x_temp + length; x_temp < end; ++x_temp)
{
coordinate* temp = new coordinate;
temp->x_coord = x_temp;
temp->y_coord = screen_width / 2;
body.push_back(temp);
}
}
class Food : public Drawable {
public:
Food();
virtual void draw();
void generate();
coordinate spot[3];
int value;
unsigned char sumbol;
private:
const int max_food_amount;
void remove_eaten();
};
// if the snake touches an apple, sets the x-coordinate of that apple to -1
// if the x-coordinate is -1, this apple is considered non-existent
void Food::remove_eaten()
{
for (int i = 0; i < max_food_amount; ++i)
{
if (map(spot[i].x_coord, spot[i].y_coord) == snake.sumbol)
{
snake.length += value;
spot[i].x_coord = -1;
}
}
}
// draws all apples on the screen
void Food::draw()
{
remove_eaten();
generate();
for (int i = 0; i < max_food_amount; ++i)
{
map(spot[i].x_coord, spot[i].y_coord) = sumbol;
}
}
// randomly generates a new position of non-existent apples on the screen
void Food::generate()
{
for (int i = 0; i < max_food_amount; ++i)
{
// repeat until apple appears in an empty tile
while (spot[i].x_coord < 0 || map(spot[i].x_coord, spot[i].y_coord) == snake.sumbol || map(spot[i].x_coord, spot[i].y_coord) == sumbol)
{
spot[i].x_coord = rand() % screen_length;
spot[i].y_coord = rand() % screen_width;
}
}
}
Food::Food()
:value(1),
sumbol('@'),
max_food_amount(3)
{
for (int i = 0; i < max_food_amount; ++i) {
// if the x-coordinate is -1, this apple is considered non-existent
spot[i].x_coord = -1;
}
generate();
}
// draws all drawable items on the screen
void draw_all(std::vector<Drawable*> vect)
{
for (int i = 0; i < screen_length * screen_width; ++i) {
map.field[i] = map.background;
}
for (int i = 0; i < vect.size(); ++i) {
vect[i]->draw();
}
}
int main()
{
srand((unsigned int)time(0));
Food apples;
// storing all drawable items in this vector
std::vector<Drawable*> drawables{ &snake, &apples };
draw_all(drawables);
while (!snake.check_collision()) {
if (_kbhit()) snake.get_direction(_getch());
snake.move();
draw_all(drawables);
map.show();
sleep(100);
}
std::cout << "\n\nLENGTH : " << snake.length << '\n';
std::cout << "Press ENTER to quit";
while (_getch() != 13);
return 0;
}
@martytyty2098
Copy link
Author

martytyty2098 commented Nov 8, 2022

I'm new to OOP so I may be using it wrong.
Share your comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment