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;
}