Skip to content

nihguy/cpp-snake-game

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

93 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Udacity C++ Nanodegree Program: Capstone Build Status

This Snake Game is based on stater repo for the Capstone project in the Udacity C++ Nanodegree Program. The current project improve the original classes, uncoupling the SDL from the core classes, so another libraries can be used. It also add some new features such as:

  • Process the snake’s collision check in another thread;
  • Add some simple game state like splash screen, game over, pause game and so on;
  • Use the FSM (Finite State Machine) to uncouple each game states in their own classes and execute just one at a time.
  • Add SDL’s font to the game and store its text on texture, saving memory.

Contents

Project's Class Structure

src # Contain the main source files of the project 
├── actor # Contain the main acton used to play the game 
│   ├── snake.cc # This creates the snake source and wrap this behavior
│   └── snake.hpp # This defines the snake header
├── CMakeLists.txt
├── core # The classes on this folder are independent and don't know how is the third-party library used to execute the game
│   ├── allocation.hpp # The Allocation has a common structure to define the offset and size of the GameObjects
│   ├── allocation.inl # As the Allocation is a template class, the .inl is used to uncouple the source from the header
│   ├── block.cc # It's a generic GameObject that represents a square in the grid
│   ├── block.hpp # It's defines the Block structure
│   ├── button_chooser.cc # It simulates a button using a function and a font. It's kind of Command design pattern
│   ├── button_chooser.hpp # It defines the ButtonChooser and the Button class. The last one is used under the hoods
│   ├── color.cc # It's used to define color along the game.
│   ├── color.hpp # It's based on SFML's color class.
│   ├── controller.hpp # It's an abstract class defining a common interface to get a enum dispatched by some user inputs
│   ├── font.cc # It defines a common interface to use the "font logic" of third-party libraries  
│   ├── font.hpp # It defines the Font class and an abstract Typography class. The last one is used to add third-party libraries behavior to the game
│   ├── food.cc # It's a Block wrapper used to dispatch a check collision in another thread, alerting the Snake when it is eating a food
│   ├── food.hpp # It defines the food structure
│   ├── game.cc # It's the Context from FMS. It holds a GameState stack and run one at each time, creating the screen transitions 
│   ├── game.hpp # It defines the Capstone::Game structure
│   ├── game_font.hpp # It defines a common interface to be used by third-party libraries to implement this logic 
│   ├── game_loop.hpp # It creates a common interface to be used by third-party libraries to create their game loop logics
│   ├── game_object.hpp # It defines a common interface to the object, allowing it to use the GameState correctly 
│   ├── game_state.hpp # It's the abstract class from FSM. It allows the class to be used as a "screen wrapper", loading their content when it get focus
│   ├── game_thread.cc # It's a common interface to get a thread barrier 
│   ├── game_thread.hpp # It's defines the Capstone::GameThread structure
│   ├── random_position.cc # This defines a simple random behavior respecting a limit value, useful to use with grid system
│   ├── random_position.hpp # it defines the Capstone::RandomPosition structure
│   ├── renderer.hpp # It defines a common interface to third-party libraries to implement their logic to render a square on the screen
│   ├── vector2.hpp # This defines a simple coordinate handler. It's base on SFML's Vector2
│   └── vector2.inl # As the Vector2 is a template class, the .inl is used to uncouple the source from the header
├── main.cc # The main file used to compile the project
├── resources # This folder holds the fonts used to display the text on game
│   └── fonts 
│       ├── BitPotion.ttf 
│       └── PressStart2P.ttf
├── sdl
│   ├── controller.cc # This defines the SDL version of the Controller. It dispatch an enum when a known key is pressed by the user
│   ├── controller.hpp # This extends the Capstone::Controller
│   ├── font_maker.cc # This is a simple function to make easier fill the SDL::GameFontBuilder. It defines the font list used by the SDL version
│   ├── font_maker.hpp # It defines the function strucutre
│   ├── game_font_builder.cc # This defines the SDL version of the GameFont. It's used to create a easy access to the fonts, it's useful because the core classes don't know what's the library used to render the font
│   ├── game_font_builder.hpp # This extends the Capstone::GameFont
│   ├── game_loop.cc # It creates the SDL version of the Capstone::GameLoop
│   ├── game_loop.hpp # It defines the GameLoop structure of SDL version
│   ├── README.md
│   ├── renderer.cc # This extends the Capstone::Renderer. It defines all the necessary methods to render a square on the screen, so the core classes don't need to know how to do it, just call for the method
│   ├── renderer.hpp # It defines the structure of the SDL version of the renderer
│   ├── typography.cc  # This extends the Capstone::Typography. It defines the SDL behavior of the font, so the core classes don't need to know how to render it, just execute the methods.
│   └── typography.hpp # It defines the Typograph structure of the SDL version
└── states # This folder holds the GameState inherited classes. It's used as wrapper of the screen behavior 
    ├── end_game_state.cc # It creates both: the Game Over and the Winner state
    ├── end_game_state.hpp # It defined the structure of the Capstone::EndGameState
    ├── main_state.cc # It defines the MainState, in other words, it's where the "snake game" run properly
    ├── main_state.hpp # It defines the structure of the Capstone::MainState
    ├── pause_state.cc # It creates the Pause screen of the game.
    ├── pause_state.hpp # It defines the structure of the Capstone::PauseState
    ├── splash_screen_state.cc # It shows the Capstone info along the dertermined time until change the focus to the next GameState
    └── splash_screen_state.hpp # It defines the structure of the Capstone::SplashScreenState

Dependencies for Running Locally

  • cmake >= 3.7
  • make >= 4.1 (Linux, Mac), 3.81 (Windows)
  • SDL2 >= 2.0
    • All installation instructions can be found here
    • Note that for Linux, an apt, apt-get, dnf or pacman installation is preferred to building from source.
    • On Windows, recommend using MinGW
  • SDL_ttf >= 2.0
    • All installation instructions can be found here
    • Note that for Linux, an apt, apt-get, dnf or pacman installation is preferred to building from source.
    • On Windows, recommend using MinGW
  • SDL_image >= 2.0
    • All installation instructions can be found here
    • Note that for Linux, an apt, apt-get, dnf or pacman installation is preferred to building from source.
    • On Windows, recommend using MinGW
  • gcc/g++ >= 5.4

Basic Build Instructions

# clone the repo
git clone https://github.com/v-borg/cpp-snake-game.git

# change the current directory to the used by the repo
cd cpp-snake-game

# make a build directory and change the current directory to it
mkdir build && cd $_

# execute `cmake` command to prepare the project to be compiled
cmake ..

# execute `make` to compile the project
make

# run the project executable
./src/cpp_snake_game

Build with Unit Testing

The unit testing is useful to ensures the code will not brake while the project is evolving. Moreover, it can help other programmers to understand the code behavior easier.

This repo uses Google Test and Google Mock to do the tests. You can find that tests here, with the main behavior classes' explained, although it is not compiled by default.

If you are going to run this tests with the project, some additional commands are needed:

# make sure to clone the repo using `--recursive-module` from git command
git clone --recurse-submodules https://github.com/v-borg/cpp-snake-game.git

# IMPORTANT!
# if the repos was already cloned without the `--recursive-submodules` command, 
# you can ignore the above command and still use the command below to ensure 
# the Google Test and Google Mock will be available at compile time:
#
# git submodule update --init --recursive

# change the current directory to the used by the repo
cd cpp-snake-game

# make a build directory and change the current directory to it
mkdir build && cd $_

# There are two cmake options to build the unit tests
#
#  `BUILD_TESTS`: compile the project and its test cases at the same time. 
#  `BUILD_TESTS_ONLY`: just compiles the tests.
#
# Choose one between them assigning `ON` as follows:
cmake .. -DBUILD_TESTS=ON

# execute `make` to compile the project
make

# run the tests and the project executable as well
./tests/order
./src/cpp_snake_game

Project Specification

README (All Rubric Points REQUIRED)

Done Criteria Meets Specifications
A README with instructions is included with the project The README is included with the project and has instructions for building/running the project.

If any additional libraries are needed to run the project, these are indicated with cross-platform installation instructions.

You can submit your writeup as markdown or pdf.
The README indicates which project is chosen. The README describes the project you have built.

The README also indicates the file and class structure, along with the expected behavior or output of the program.
The README includes information about each rubric point addressed. The README indicates which rubric points are addressed. The README also indicates where in the code (i.e. files and line numbers) that the rubric points are addressed.

Compiling and Testing (All Rubric Points REQUIRED)

Done Criteria Meets Specifications
The submission must compile and run. The project code must compile and run without errors. We strongly recommend using cmake and make, as provided in the starter repos. If you choose another build system, the code must compile on any reviewer platform.

Loops, Functions, I/O

Done Criteria Meets Specifications Address
The project demonstrates an understanding of C++ functions and control structures. A variety of control structures are used in the project.

The project code is clearly organized into functions.
The font_maker function is used to avoid to expose the fonts theme used by the SDL version of the game. It uses their two arguments to create a well defined font behavior making the proccess of creating the font easier.
The project reads data from a file and process the data, or the program writes data to a file. The project reads data from an external file or writes data to a file as part of the necessary operation of the program.
The project accepts user input and processes the input. The project accepts input from a user as part of the necessary operation of the program. The Controller base class defines a common interface to get the expected input. It ensures the inherited class returns a well known command enum, allowing the GameState inherited classes to consume the command without know what was the pressed key that dispatched this command.

The GameState ensures the inherited class consumes the command dispatched by the Controller inherited class. Uncouple the logic to obtain the input command is useful to allow the game to use any kind of third-party library to execute the core structure.

All the GameState inherited (MainState, EndGameState, PauseState, SplashScreenState) class use the method handle_input to consume the Controller command as needed.

Some GameObjects like Snake and ButtonChooser use the Controller commands as well. It creates a easy way to interact with the text options or to manipulate the actor along the screen.

The SDL version of the game uses the SDL::Controller to dispatch the commands and the SDL::GameLoop to execute this class properly

Object Oriented Programming

Done Criteria Meets Specifications Address
The project uses Object Oriented Programming techniques. The project code is organized into classes with class attributes to hold the data, and class methods to perform tasks. Basically all the files compiled on the project use object-oriented programming allowing the class to control and to uncouple its logic to avoid unwanted repetition.

Classes like SDL::Renderer, SDL::Typography and SDL::GameFontBuilder show this kind of organization
Classes use appropriate access specifiers for class members. All class data members are explicitly specified as public, protected, or private. Although all the classes use the “appropriate” specifier, sometimes the protected access is preferred to private one for the sake of tests.

Classes like Snake and Food are good examples of the “appropriate” use of the specifiers.
Class constructors utilize member initialization lists. All class members that are set to argument values are initialized through member initialization lists. This action is useful to allow the class to assign const variable and to avoid unnecessary pointers.

All the classes use this strategy always when possible on the project. We can see an example of this o BoundingBox class, or Color class.
Classes abstract implementation details from their interfaces. All class member functions document their effects, either through function names, comments, or formal documentation. Member functions do not change program state in undocumented ways. All the classes on the project are formal documented. Some classes are tested as well.
Classes encapsulate behavior. Appropriate data and functions are grouped into classes. Member data that is subject to an invariant is hidden from the user. State is accessed via member functions. The project groups data and behavior into classes. It hides all invariant data and functions to avoid misuse of the methods and data. The ButtonChooser class for example, hides the data (the button vector and its cursor), while it allows the client to add the button, using Button class under the hoods.

The Food class uses its replace_food and shared_check_collision as private method. It’s because it’s used by the thread and should not be accessed directly as well as its data (food, random position and snake variable)
Classes follow an appropriate inheritance hierarchy. Inheritance hierarchies are logical. Composition is used instead of inheritance when appropriate. Abstract classes are composed of pure virtual functions. Override functions are specified. This project aim to uncouple the core interface from each third-party library implementation and it requires some inheritance. Some classes like GameFont, GameLoop, and others are defined to ensure the library version of implementation can be accessed properly.

Classes like SDL::GameFontBuilder and SDL::GameLoop extend the base classes mentioned above, overwriting their methods using the SDL version logic to ensure the correct behavior of the game.

The GameObject, GameState and GameThread ensures all the game core classes can be correctly configured. The Snake class, for example, extends from GameThread overwriting its method to create the thread behavior.

The GameObject and the GameLoop class are examples of the abstract classes composed of pure virtual function, inherited with logical propose.
Overloaded functions allow the same function to operate on different parameters. One function is overloaded with different signatures for the same function name.
Derived class functions override virtual base class functions. One member function in an inherited class overrides a virtual base class member function. As mentioned early, the SDL::GameFontBuilder and SDL::GameLoop extend the base classes GameFont and GameLoop, respectively, overwriting their methods as expected.

The classes from state directory are other good examples. They are inherited from GameState abstract class, and the last one inherits from GameObject, forcing them all overwrite its pure methods as well.
Templates generalize functions in the project. One function is declared with a template that allows it to accept a generic parameter. The project don’t uses a template function properly, but uses some template classes offering a generic programing that's useful to create the distinction between Snake's float Allocation and Food integer Allocation, for example. The Vector2 is another example of template class.

Memory Management

Done Criteria Meets Specifications Address
The project makes use of references in function declarations. At least two variables are defined as references, or two functions use pass-by-reference in the project code. The font_maker is the only function implemented in this project. It uses two enums passed-by-reference: a FontSize and a FontTheme. They both are used in a loop on main.cc file to assign all fonts used by the SDL version of the game to the GameFontBuilder.
The project uses destructors appropriately. At least one class that uses unmanaged dynamically allocated memory, along with any class that otherwise needs to modify state upon the termination of an object, uses a destructor. The Font class require a Typography abstract class as the only argument on its constructor. Because of the abstract nature of the Typography class, it must be used as pointer. Instead of use a smart pointer as another classes, it uses a raw pointer that is deleted on its destructor method to avoid memory leaks.
The project uses scope / Resource Acquisition Is Initialization (RAII) where appropriate. The project follows the Resource Acquisition Is Initialization pattern where appropriate, by allocating objects at compile-time, initializing objects when they are declared, and utilizing scope to ensure their automatic destruction.
The project follows the Rule of 5. For all classes, if any one of the copy constructor, copy assignment operator, move constructor, move assignment operator, and destructor are defined, then all of these functions are defined.
The project uses move semantics to move data, instead of copying it, where possible. For classes with move constructors, the project returns objects of that class by value, and relies on the move constructor, instead of copying the object.
The project uses smart pointers instead of raw pointers. The project uses at least one smart pointer: unique_ptr, shared_ptr, or weak_ptr. The project does not use raw pointers. The project uses a lot of std::shared_ptr and std::unique_ptr. It’s used to avoid memory leak caused by non-deleted raw pointer, and to work with thread as well.

The Food class and all the inherited states are good examples of smart pointer implementation on project. Just one pointer is used as raw pointer on the project, and it’s used intentionally to suppress the Project Specification item above.

Concurrency

Done Criteria Meets Specifications Address
The project uses multithreading. The project uses multiple threads in the execution. The Snake's and the Food extend the abstract GameThread class to implement a thread protected by the base class barrier.

They both implement a method called shared_check_collision and is executed on prepare method. The Food inherited thread checks if it’s colliding with Snake and send a message to the last one, alerting about that. The Snake inherited thread checks if it’s colliding with its body, killing the actor.

The Food classes also defines its own method to create their threads. It invokes the replace_food on replace method, using the std::async template function to create a thread to check if the food won’t be placed over the snake’s head or body.
A promise and future is used in the project. A promise and future is used to pass data from a worker thread to a parent thread in the project code.
A mutex or lock is used in the project. A mutex or lock (e.g. std::lock_guard or std::unique_lock) is used to protect data that is shared across multiple threads in the project code. The std::unique_lock is used sometimes in the project. We can see this use on Snake's shared_check_collision method, Food's shared_check_collision method and Food's replace_food method
A condition variable is used in the project. A std::condition_variable is used in the project code to synchronize thread execution.

About

A 2D Snake game using C++ and SDL

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published