5
\$\begingroup\$

I have created a Snake console game using object-orientated programming methods. This is the first time I have ever used OOP so I just wanted to know if I was using the method correctly as well as hear any other advise I could possibly receive as this is my first time doing this. There are 4 header files and 4 .cpp files used.

Direction.h:

#pragma once
enum class Direction {
    STOP = 0, UP, DOWN, LEFT, RIGHT
};

Food.h:

#pragma once
class Food{
private:
    int foodX;
    int foodY;
public:
    Food(int x, int y);
    Food();
    int posX() { return foodX; };
    int posY() { return foodY; };
    void Refresh(int x, int y);
};

Food.cpp

#include "Food.h"

Food::Food(int x, int y)
{
    foodX = x;
    foodY = y;
}

Food::Food()
    :foodX{ 2 }, foodY{ 2 }
{
}

void Food::Refresh(int x, int y)
{
    foodX = x;
    foodY = y;
}

Snake.h

#pragma once
#include "Direction.h"

class Snake{
private:
    int headX;
    int headY;
    int length;
    Direction dir;
public:
    Snake(int x, int y);
    Snake();
    int posX() { return headX; };
    int posY() { return headY; };
    int len() { return length; };
    void Update();
    void Eat();
    void Change_Dir(Direction direction);
};

Snake.cpp

#include "Snake.h"

Snake::Snake()
    : headX{ 1 }, headY{ 1 }, length{ 1 }, dir{ Direction::STOP }
{
}

Snake::Snake(int x, int y)
    : length{ 1 }, dir{ Direction::STOP }
{
    headX = x;
    headY = y;
}

void Snake::Update()
{
    switch (dir) {
    case(Direction::LEFT):
        headX--;
        break;
    case(Direction::RIGHT):
        headX++;
        break;
    case(Direction::UP):
        headY--;
        break;
    case(Direction::DOWN):
        headY++;
        break;
    case(Direction::STOP):
        break;
    }

}

void Snake::Eat()
{
    length++;
}

void Snake::Change_Dir(Direction direction)
{
    dir = direction;
}

Game.h

#pragma once

#include "Snake.h"
#include "Food.h"
#include <vector>

class Game {
private:
    Snake snake;
    Food food;
    int board_height;
    int board_width;
    std::vector<int> Snake_Tail_X;
    std::vector<int> Snake_Tail_Y;
    void Snake_Tail_Update();
    void Board_Colission();
    void Snake_Colission();
    void Eaten();
    void Input_Check();
    void Draw();
    void Logic();
    bool Game_Over;
public:
    Game();
    bool Game_State() { return Game_Over; };
    void Play();
};

Game.cpp

#include "Game.h"
#include <iostream>
#include <conio.h>

Game::Game()
    :snake{ 2, 5 }, food{ 5, 5 }, board_height{ 20 }, board_width{ 20 }, Game_Over{ false }, Snake_Tail_X{}, Snake_Tail_Y{}
{
}

void Game::Play()
{
    Draw();
    Logic();
}

void Game::Draw()
{
    int i{ 0 }, j{ 0 }, k{ 0 };

    system("cls");

    for (i = 0; i < board_height; i++) {

        for (j = 0; j < board_width; j++) {

            if (j == snake.posX() && i == snake.posY())
                std::cout << "O";
            else if (j == food.posX() && i == food.posY())
                std::cout << "F";
            else if (i == 0 || j == 0 || i == board_height - 1 || j == board_width - 1)
                std::cout << "#";
            else{
                bool tail = false;

                for (k = 0; k < snake.len() - 1; k++) {
                    if (Snake_Tail_X[k] == j && Snake_Tail_Y[k] == i) {
                        std::cout << "o";
                        tail = true;
                    }   
                }

                if(tail == false)
                    std::cout << " ";
            }




        }

        std::cout << std::endl;

    }
    std::cout <<  "Score: " << snake.len()-1;
}

void Game::Logic() 
{
    Input_Check();
    Snake_Tail_Update();
    snake.Update();
    Board_Colission();
    Snake_Colission();
    Eaten();
}

void Game::Snake_Tail_Update()
{
    if (!Snake_Tail_X.empty()) {

        int i{ 0 }, tempX{ 0 }, tempY{ 0 }, temp2X{ 0 }, temp2Y{ 0 };
        tempX = Snake_Tail_X[0];
        tempY = Snake_Tail_Y[0];
        Snake_Tail_X[0] = snake.posX();
        Snake_Tail_Y[0] = snake.posY();

        for (i = 1; i < snake.len()-1; i++) {

            temp2X = Snake_Tail_X[i];
            temp2Y = Snake_Tail_Y[i];

            Snake_Tail_X[i] = tempX;
            Snake_Tail_Y[i] = tempY;

            tempX = temp2X;
            tempY = temp2Y;

        }

    }
}

void Game::Board_Colission(){

    if (snake.posX() == 0 || snake.posX() == board_width-1 || snake.posY() == 0 || snake.posY() == board_height-1) {
        Game_Over = true;
    }
}

void Game::Snake_Colission()
{
    int i{ 0 };

    for (i = 0; i < snake.len() - 1; i++) {
        if (snake.posX() == Snake_Tail_X[i] && snake.posY() == Snake_Tail_Y[i])
            Game_Over = true;
    }

}

void Game::Eaten()
{
    if (snake.posX() == food.posX() && snake.posY() == food.posY()) {
        food.Refresh(rand() % (board_width - 2) + 1, rand() % (board_height - 2) + 1);
        snake.Eat();
        Snake_Tail_X.push_back(1);
        Snake_Tail_Y.push_back(1);
    }
}

void Game::Input_Check(){
    if (_kbhit()) {

        switch(_getch()){
        case('a'):
            snake.Change_Dir(Direction::LEFT);
            break;
        case('d'):
            snake.Change_Dir(Direction::RIGHT);
            break;
        case('w'):
            snake.Change_Dir(Direction::UP);
            break;
        case('s'):
            snake.Change_Dir(Direction::DOWN);
            break;
        default:
            break;
        }
    }
}

Main.cpp

#include <iostream>
#include "Game.h"

int main()
{
    Game game;

    while (!game.Game_State()) {
        game.Play();

    }

    return 0;

} 
```
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Just wondering, did you write this on Linux? \$\endgroup\$
    – pacmaninbw
    Mar 22, 2020 at 14:07
  • \$\begingroup\$ Hi pacmaninbw, I wrote this on Windows in Visual Studio 2019 \$\endgroup\$
    – JeffASon
    Mar 22, 2020 at 14:21

1 Answer 1

4
\$\begingroup\$

Const methods

These:

    int posX() { return foodX; };
    int posY() { return foodY; };
// ...
    int posX() { return headX; };
    int posY() { return headY; };
    int len() { return length; };

don't modify anything in this, so make them const:

    int posX() const { return foodX; };
    int posY() const { return foodY; };

Setters

This:

void Refresh(int x, int y);

is unusual. You're forcing the user to update both x and y at the same time. In a different universe where you care about atomic interactions in a multithreaded application, this might matter, but here it doesn't. Effectively since you have unlimited read/write ability to this class, it's not even worth making individual setter methods - just boil this down to a struct with two public member variables and be done with it.

Nomenclature

    bool Game_Over;

is styled to look like a method but it should actually match the capitalization of your other member variables (i.e. lowercase).

Game_State would make sense as a name if you were returning an enum, but since you aren't: it should probably be called something like isGameOver().

Characters instead of strings

This

std::cout << "O";

should be

std::cout << 'O';

Unused includes

Remove

#include <iostream>

from your main.cpp.

\$\endgroup\$
1
  • \$\begingroup\$ Hi, thank you for the feedback, I really appreciate it. \$\endgroup\$
    – JeffASon
    Mar 22, 2020 at 19:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.