2
\$\begingroup\$

So I've made a snake game with object-oriented approach. I would be glad if you could take a look at the code and give me any advices/tips/point out errors/etc. :)

main.cpp:

#include "SnakeGame.hpp"


int main(int argc, char **argv)
{
    srand(time(0));

    int borderSize = 15;
    CSnakeGame game(borderSize);

    while (!game.hasLost()) {
        /* I know I should calculate the sleep time, what is 
           the best way to do this? */
        Sleep(100);
    }
}

SnakeGame.hpp:

#pragma once

#include <Windows.h>
#include <iostream>
#include <vector>
#include <conio.h>
#include <ctime>

enum EDirections
{
    STOP, UP, RIGHT, DOWN, LEFT
};

struct SPoint
{
    int x, y;
    SPoint(int x = 0, int y = 0): x(x), y(y) {}
};

class CSnakeGame
{
private:
    HANDLE hStdOutput;
    int borderSize, snakeDirection, score;
    std::vector<SPoint> vecSnake;
    SPoint fruit;

    void gotoxy(short, short);
    void printChar(SPoint, char);
    void drawBorder();
    void drawGame();
    void spawnFruit();
    bool hasCollided();
    bool canMove();

public:
    CSnakeGame(int);
    bool hasLost();
};

SnakeGame.cpp:

#include "SnakeGame.hpp"

CSnakeGame::CSnakeGame(int borderSize)
{
    this->hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    this->borderSize = borderSize;

    this->vecSnake.push_back(SPoint(this->borderSize / 2, this->borderSize / 2));
    this->snakeDirection = STOP;

    this->score = 0;

    drawBorder();
    spawnFruit();
}

void CSnakeGame::gotoxy(short x, short y)
{
    SetConsoleCursorPosition(this->hStdOutput, { x, y });
}

void CSnakeGame::printChar(SPoint point, char character)
{
    gotoxy(point.x, point.y);
    std::cout << character;
}

void CSnakeGame::drawBorder()
{
    for (int i = 0; i <= this->borderSize + 1; i++) {
        for (int j = 0; j <= this->borderSize + 1; j++) {
            if (i % (this->borderSize + 1) == 0 || j % (this->borderSize + 1) == 0)
                std::cout << "#";
            else
                std::cout << " ";
        }

        std::cout << std::endl;
    }
}

void CSnakeGame::drawGame()
{
    SPoint snakeHead = this->vecSnake[0];
    printChar(snakeHead, 'O');

    for (int i = 1; i < this->vecSnake.size(); i++) {
        printChar(this->vecSnake[i], 'o');
    }

    gotoxy(0, this->borderSize + 3);
    std::cout << "Dimensions: " << this->borderSize << " x " << this->borderSize << std::endl;
    std::cout << "Score: " << this->score;
}

void CSnakeGame::spawnFruit()
{
    this->fruit = { rand() % this->borderSize + 1, rand() % this->borderSize + 1 };
    printChar(this->fruit, 'X');
}

bool CSnakeGame::hasCollided()
{
    SPoint snakeHead = this->vecSnake[0];
    if (snakeHead.x < 1 || snakeHead.x > this->borderSize || snakeHead.y < 1 || snakeHead.y > this->borderSize)
        return true;

    for (int i = 1; i < this->vecSnake.size(); i++) {
        if (this->vecSnake[i].x == snakeHead.x && this->vecSnake[i].y == snakeHead.y)
            return true;
    }

    return false;
}

bool CSnakeGame::canMove()
{
    if (_kbhit()) {
        switch (_getch()) {
            case 'w': if (this->snakeDirection != DOWN) this->snakeDirection = UP; break;
            case 'd': if (this->snakeDirection != LEFT) this->snakeDirection = RIGHT; break;
            case 's': if (this->snakeDirection != UP) this->snakeDirection = DOWN; break;
            case 'a': if (this->snakeDirection != RIGHT) this->snakeDirection = LEFT; break;
        }
    }

    SPoint snakeHead = this->vecSnake[0];
    snakeHead.x += this->snakeDirection == LEFT ? -1 : this->snakeDirection == RIGHT ? 1 : 0;
    snakeHead.y += this->snakeDirection == UP ? -1 : this->snakeDirection == DOWN ? 1 : 0;

    if (snakeHead.x != this->fruit.x || snakeHead.y != this->fruit.y) {
        printChar(this->vecSnake[this->vecSnake.size() - 1], ' ');
        this->vecSnake.pop_back();
    }
    else {
        this->score++;
        spawnFruit();
    }

    this->vecSnake.insert(this->vecSnake.begin(), snakeHead);

    return !hasCollided();
}

bool CSnakeGame::hasLost()
{
    if (!canMove())
        return true;

    drawGame();

    return false;
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy