├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── README.md ├── cmake ├── FindSDL2_image.cmake └── FindSDL2_ttf.cmake ├── src ├── CMakeLists.txt ├── actor │ ├── snake.cc │ └── snake.hpp ├── core │ ├── allocation.hpp │ ├── allocation.inl │ ├── block.cc │ ├── block.hpp │ ├── button_chooser.cc │ ├── button_chooser.hpp │ ├── color.cc │ ├── color.hpp │ ├── controller.hpp │ ├── font.cc │ ├── font.hpp │ ├── food.cc │ ├── food.hpp │ ├── game.cc │ ├── game.hpp │ ├── game_font.hpp │ ├── game_loop.hpp │ ├── game_object.hpp │ ├── game_state.hpp │ ├── game_thread.cc │ ├── game_thread.hpp │ ├── random_position.cc │ ├── random_position.hpp │ ├── renderer.hpp │ ├── vector2.hpp │ └── vector2.inl ├── main.cc ├── ncurses │ └── README.md ├── resources │ └── fonts │ │ ├── BitPotion.ttf │ │ └── PressStart2P.ttf ├── sdl │ ├── README.md │ ├── controller.cc │ ├── controller.hpp │ ├── font_maker.cc │ ├── font_maker.hpp │ ├── game_font_builder.cc │ ├── game_font_builder.hpp │ ├── game_loop.cc │ ├── game_loop.hpp │ ├── renderer.cc │ ├── renderer.hpp │ ├── typography.cc │ └── typography.hpp └── states │ ├── end_game_state.cc │ ├── end_game_state.hpp │ ├── main_state.cc │ ├── main_state.hpp │ ├── pause_state.cc │ ├── pause_state.hpp │ ├── splash_screen_state.cc │ └── splash_screen_state.hpp └── tests ├── CMakeLists.txt ├── core ├── allocation_test.cc ├── color_test.cc └── vector2_test.cc └── order.cc /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | vendor -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vendor/googletest"] 2 | path = vendor/googletest 3 | url = https://github.com/v-borg/googletest 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | # For Ubuntu 14.04 C++11/15 support ... 4 | sudo: required 5 | dist: trusty 6 | 7 | compiler: 8 | - gcc 9 | 10 | before_script: 11 | - git submodule update --init --recursive 12 | - sudo apt-get install build-essential 13 | - sudo apt-get install cmake 14 | - mkdir build 15 | - cd build 16 | - cmake .. -DBUILD_TESTS_ONLY=On 17 | - make 18 | 19 | script: ./tests/order -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | project(cpp_snake_game) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | 6 | option(BUILD_TESTS "Use BUILD_TESTS to compile the project and its test cases as well" OFF) # OFF by default 7 | option(BUILD_TESTS_ONLY "Use BUILD_TESTS_ONLY to compile only the test cases without the project" OFF) # OFF by default 8 | 9 | if (BUILD_TESTS OR BUILD_TESTS_ONLY) 10 | message("Building tests") 11 | set(BUILD_GMOCK ON) 12 | add_subdirectory(vendor/googletest) 13 | add_subdirectory(tests) 14 | endif() 15 | 16 | if (NOT BUILD_TESTS_ONLY) 17 | add_subdirectory(src) 18 | endif() 19 | 20 | unset(BUILD_TESTS CACHE) 21 | unset(BUILD_TESTS_ONLY CACHE) 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Udacity C++ Nanodegree Program: Capstone [![Build Status](https://travis-ci.org/v-borg/cpp-snake-game.svg?branch=master)](https://travis-ci.org/v-borg/cpp-snake-game) 4 | 5 | This Snake Game is based on stater [repo](https://github.com/udacity/CppND-Capstone-Snake-Game) for the Capstone project in the [Udacity C++ Nanodegree Program](https://www.udacity.com/course/c-plus-plus-nanodegree--nd213). 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: 6 | 7 | * Process the snake’s collision check in another thread; 8 | * Add some simple game state like `splash screen`, `game over`, `pause game` and so on; 9 | * Use the FSM (Finite State Machine) to uncouple each game states in their own classes and execute just one at a time. 10 | * Add SDL’s font to the game and store its text on texture, saving memory. 11 | 12 | ## Contents 13 | 14 | - [Project's Class Structure](#projects-class-structure) 15 | - [Dependencies](#dependencies-for-running-locally) 16 | - [Basic Build Instructions](#basic-build-instructions) 17 | - [Build with Unit Testing](#build-with-unit-testing) 18 | - [Project Specification](#project-specification) 19 | - [README (All Rubric Points REQUIRED)](#readme-all-rubric-points-required) 20 | - [Compiling and Testing (All Rubric Points REQUIRED)](#compiling-and-testing-all-rubric-points-required) 21 | - [Loops, Functions, I/O](#loops-functions-io) 22 | - [Object Oriented Programming](#object-oriented-programming) 23 | - [Memory Management](#memory-management) 24 | - [Concurrency](#concurrency) 25 | 26 | 27 | ## Project's Class Structure 28 | 29 | ```bash 30 | src # Contain the main source files of the project 31 | ├── actor # Contain the main acton used to play the game 32 | │   ├── snake.cc # This creates the snake source and wrap this behavior 33 | │   └── snake.hpp # This defines the snake header 34 | ├── CMakeLists.txt 35 | ├── core # The classes on this folder are independent and don't know how is the third-party library used to execute the game 36 | │   ├── allocation.hpp # The Allocation has a common structure to define the offset and size of the GameObjects 37 | │   ├── allocation.inl # As the Allocation is a template class, the .inl is used to uncouple the source from the header 38 | │   ├── block.cc # It's a generic GameObject that represents a square in the grid 39 | │   ├── block.hpp # It's defines the Block structure 40 | │   ├── button_chooser.cc # It simulates a button using a function and a font. It's kind of Command design pattern 41 | │   ├── button_chooser.hpp # It defines the ButtonChooser and the Button class. The last one is used under the hoods 42 | │   ├── color.cc # It's used to define color along the game. 43 | │   ├── color.hpp # It's based on SFML's color class. 44 | │   ├── controller.hpp # It's an abstract class defining a common interface to get a enum dispatched by some user inputs 45 | │   ├── font.cc # It defines a common interface to use the "font logic" of third-party libraries 46 | │   ├── 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 47 | │   ├── 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 48 | │   ├── food.hpp # It defines the food structure 49 | │   ├── game.cc # It's the Context from FMS. It holds a GameState stack and run one at each time, creating the screen transitions 50 | │   ├── game.hpp # It defines the Capstone::Game structure 51 | │   ├── game_font.hpp # It defines a common interface to be used by third-party libraries to implement this logic 52 | │   ├── game_loop.hpp # It creates a common interface to be used by third-party libraries to create their game loop logics 53 | │   ├── game_object.hpp # It defines a common interface to the object, allowing it to use the GameState correctly 54 | │   ├── 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 55 | │   ├── game_thread.cc # It's a common interface to get a thread barrier 56 | │   ├── game_thread.hpp # It's defines the Capstone::GameThread structure 57 | │   ├── random_position.cc # This defines a simple random behavior respecting a limit value, useful to use with grid system 58 | │   ├── random_position.hpp # it defines the Capstone::RandomPosition structure 59 | │   ├── renderer.hpp # It defines a common interface to third-party libraries to implement their logic to render a square on the screen 60 | │   ├── vector2.hpp # This defines a simple coordinate handler. It's base on SFML's Vector2 61 | │   └── vector2.inl # As the Vector2 is a template class, the .inl is used to uncouple the source from the header 62 | ├── main.cc # The main file used to compile the project 63 | ├── resources # This folder holds the fonts used to display the text on game 64 | │   └── fonts 65 | │   ├── BitPotion.ttf 66 | │   └── PressStart2P.ttf 67 | ├── sdl 68 | │   ├── controller.cc # This defines the SDL version of the Controller. It dispatch an enum when a known key is pressed by the user 69 | │   ├── controller.hpp # This extends the Capstone::Controller 70 | │   ├── 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 71 | │   ├── font_maker.hpp # It defines the function strucutre 72 | │   ├── 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 73 | │   ├── game_font_builder.hpp # This extends the Capstone::GameFont 74 | │   ├── game_loop.cc # It creates the SDL version of the Capstone::GameLoop 75 | │   ├── game_loop.hpp # It defines the GameLoop structure of SDL version 76 | │   ├── README.md 77 | │   ├── 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 78 | │   ├── renderer.hpp # It defines the structure of the SDL version of the renderer 79 | │   ├── 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. 80 | │   └── typography.hpp # It defines the Typograph structure of the SDL version 81 | └── states # This folder holds the GameState inherited classes. It's used as wrapper of the screen behavior 82 | ├── end_game_state.cc # It creates both: the Game Over and the Winner state 83 | ├── end_game_state.hpp # It defined the structure of the Capstone::EndGameState 84 | ├── main_state.cc # It defines the MainState, in other words, it's where the "snake game" run properly 85 | ├── main_state.hpp # It defines the structure of the Capstone::MainState 86 | ├── pause_state.cc # It creates the Pause screen of the game. 87 | ├── pause_state.hpp # It defines the structure of the Capstone::PauseState 88 | ├── splash_screen_state.cc # It shows the Capstone info along the dertermined time until change the focus to the next GameState 89 | └── splash_screen_state.hpp # It defines the structure of the Capstone::SplashScreenState 90 | ``` 91 | 92 | ## Dependencies for Running Locally 93 | * cmake >= 3.7 94 | * All OSes: [click here for installation instructions](https://cmake.org/install/) 95 | * make >= 4.1 (Linux, Mac), 3.81 (Windows) 96 | * Linux: make is installed by default on most Linux distros 97 | * Mac: [install Xcode command line tools to get make](https://developer.apple.com/xcode/features/) 98 | * Windows: [Click here for installation instructions](http://gnuwin32.sourceforge.net/packages/make.htm) 99 | * SDL2 >= 2.0 100 | * All installation instructions can be found [here](https://wiki.libsdl.org/Installation) 101 | * Note that for Linux, an `apt`, `apt-get`, `dnf` or `pacman` installation is preferred to building from source. 102 | * On Windows, recommend using [MinGW](http://www.mingw.org/) 103 | * SDL_ttf >= 2.0 104 | * All installation instructions can be found [here](https://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf_2.html) 105 | * Note that for Linux, an `apt`, `apt-get`, `dnf` or `pacman` installation is preferred to building from source. 106 | * On Windows, recommend using [MinGW](http://www.mingw.org/) 107 | * SDL_image >= 2.0 108 | * All installation instructions can be found [here](https://www.libsdl.org/projects/SDL_image/docs/SDL_image_frame.html) 109 | * Note that for Linux, an `apt`, `apt-get`, `dnf` or `pacman` installation is preferred to building from source. 110 | * On Windows, recommend using [MinGW](http://www.mingw.org/) 111 | * gcc/g++ >= 5.4 112 | * Linux: gcc / g++ is installed by default on most Linux distros 113 | * Mac: same deal as make - [install Xcode command line tools](https://developer.apple.com/xcode/features/) 114 | * Windows: recommend using [MinGW](http://www.mingw.org/) 115 | 116 | ## Basic Build Instructions 117 | 118 | ```bash 119 | # clone the repo 120 | git clone https://github.com/v-borg/cpp-snake-game.git 121 | 122 | # change the current directory to the used by the repo 123 | cd cpp-snake-game 124 | 125 | # make a build directory and change the current directory to it 126 | mkdir build && cd $_ 127 | 128 | # execute `cmake` command to prepare the project to be compiled 129 | cmake .. 130 | 131 | # execute `make` to compile the project 132 | make 133 | 134 | # run the project executable 135 | ./src/cpp_snake_game 136 | ``` 137 | 138 | ## Build with [Unit Testing](https://en.wikipedia.org/wiki/Unit_testing) 139 | 140 | 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. 141 | 142 | This repo uses [Google Test](https://github.com/google/googletest) and [Google Mock](https://github.com/google/googletest/tree/master/googlemock) to do the tests. 143 | You can find that tests [here](tests/), with the main behavior classes' explained, although it is not compiled by default. 144 | 145 | If you are going to run this tests with the project, some additional commands are needed: 146 | 147 | ```bash 148 | # make sure to clone the repo using `--recursive-module` from git command 149 | git clone --recurse-submodules https://github.com/v-borg/cpp-snake-game.git 150 | 151 | # IMPORTANT! 152 | # if the repos was already cloned without the `--recursive-submodules` command, 153 | # you can ignore the above command and still use the command below to ensure 154 | # the Google Test and Google Mock will be available at compile time: 155 | # 156 | # git submodule update --init --recursive 157 | 158 | # change the current directory to the used by the repo 159 | cd cpp-snake-game 160 | 161 | # make a build directory and change the current directory to it 162 | mkdir build && cd $_ 163 | 164 | # There are two cmake options to build the unit tests 165 | # 166 | # `BUILD_TESTS`: compile the project and its test cases at the same time. 167 | # `BUILD_TESTS_ONLY`: just compiles the tests. 168 | # 169 | # Choose one between them assigning `ON` as follows: 170 | cmake .. -DBUILD_TESTS=ON 171 | 172 | # execute `make` to compile the project 173 | make 174 | 175 | # run the tests and the project executable as well 176 | ./tests/order 177 | ./src/cpp_snake_game 178 | ``` 179 | 180 | ## Project Specification 181 | 182 | ### README (All Rubric Points REQUIRED) 183 | 184 | | _Done_ | _Criteria_ | _Meets Specifications_ | 185 | |:-------: |:--------------------------------------------------------------------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 186 | | ☑ | 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. | 187 | | ☑ | 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. | 188 | | ☑ | 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. | 189 | 190 | 191 | ### Compiling and Testing (All Rubric Points REQUIRED) 192 | 193 | | _Done_ | _Criteria_ | _Meets Specifications_ | 194 | |:-------: |:------------------------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 195 | | ☑ | 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. | 196 | 197 | 198 | ### Loops, Functions, I/O 199 | 200 | | _Done_ | _Criteria_ | _Meets Specifications_ | _Address_ | 201 | |:-------: |:----------------------------------------------------------------------------------------------- |:------------------------------------------------------------------------------------------------------------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 202 | | ☑ | 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](src/sdl/font_maker.cc#L8) 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. | 203 | | ☐ | 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. | | 204 | | ☑ | 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](src/core/controller.hpp#40) defines a common interface to get the expected input. It ensures the inherited class returns a well known command [enum](src/core/controller.hpp#L14), allowing the GameState inherited classes to consume the command without know what was the pressed key that dispatched this command.

The [GameState](src/core/game_state.hpp#L36) ensures the inherited class consumes the command dispatched by the [Controller](src/core/controller.hpp) 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](src/core) structure.

All the GameState inherited ([MainState](src/states/main_state.cc#L72), [EndGameState](src/states/end_game_state.cc#L105), [PauseState](src/states/pause_state.cc#L80), [SplashScreenState](src/states/splash_screen_state.cc#L91)) class use the method [handle_input](src/core/game_state.hpp#L36) to consume the [Controller command](src/core/controller.hpp) as needed.

Some GameObjects like [Snake](src/actor/snake.cc#L96) and [ButtonChooser](src/core/button_chooser.cc#L63) use the [Controller](src/core/controller.hpp#L15) 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](src/sdl/controller.cc#L8) to dispatch the commands and the [SDL::GameLoop](src/sdl/game_loop.cc#L35) to execute this class properly | 205 | 206 | 207 | ### Object Oriented Programming 208 | 209 | | _Done_ | _Criteria_ | _Meets Specifications_ | _Address_ | 210 | |:-------: |:--------------------------------------------------------------------------------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 211 | | ☑ | 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](https://en.wikipedia.org/wiki/Object-oriented_programming) allowing the class to control and to uncouple its logic to avoid unwanted repetition.

Classes like [SDL::Renderer](src/sdl/renderer.hpp), [SDL::Typography](src/sdl/typography.hpp) and [SDL::GameFontBuilder](src/sdl/game_font_builder.hpp) show this kind of organization | 212 | | ☑ | 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](tests).

Classes like [Snake](src/actor/snake.hpp) and [Food](src/core/food.hpp) are good examples of the “appropriate” use of the specifiers. | 213 | | ☑ | 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](src/core/allocation.hpp#L28) class, or [Color](src/core/color.hpp#L30) class. | 214 | | ☑ | 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](tests) as well. | 215 | | ☑ | 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](src/core/button_chooser.hpp) class for example, hides the data (the button vector and its cursor), while it allows the client to add the button, using [Button](src/core/button_chooser.hpp#L17) class under the hoods.

The [Food](src/core/food.hpp) class uses its [replace_food](src/core/food.hpp#L97) and [shared_check_collision](src/core/food.hpp#L85) as `private` method. It’s because it’s used by the thread and should not be accessed directly as well as its data ([food](src/core/food.hpp#L104), [random position](src/core/food.hpp#L109) and [snake](src/core/food.hpp#L114) variable) | 216 | | ☑ | 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](src/core/game_font.hpp), [GameLoop](src/core/game_loop.hpp), and others are defined to ensure the library version of implementation can be accessed properly.

Classes like [SDL::GameFontBuilder](src/sdl/game_font_builder.hpp) and [SDL::GameLoop](src/sdl/game_loop.hpp) extend the base classes mentioned above, overwriting their methods using the SDL version logic to ensure the correct behavior of the game.

The [GameObject](src/core/game_object.hpp), [GameState](src/core/game_state.hpp) and [GameThread](src/core/game_thread.hpp) ensures all the game core classes can be correctly configured. The [Snake](src/actor/snake.hpp) class, for example, extends from GameThread overwriting its method to create the thread behavior.

The [GameObject](src/core/game_object.hpp) and the [GameLoop](src/core/game_loop.hpp) class are examples of the abstract classes composed of pure virtual function, inherited with logical propose. | 217 | | ☐ | Overloaded functions allow the same function to operate on different parameters. | One function is overloaded with different signatures for the same function name. | | 218 | | ☑ | 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](src/sdl/game_font_builder.hpp) and [SDL::GameLoop](src/sdl/game_loop.hpp) extend the base classes [GameFont](src/core/game_font.hpp) and [GameLoop](src/core/game_loop.hpp), respectively, overwriting their methods as expected.

The classes from [state directory](src/states) are other good examples. They are inherited from [GameState](src/core/game_state.hpp) abstract class, and the last one inherits from [GameObject](src/core/game_object.hpp), forcing them all overwrite its pure methods as well. | 219 | | ☑ | 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](src/actor/snake.hpp)'s [float Allocation](src/core/allocation.hpp#L170) and Food [integer Allocation](src/core/allocation.hpp#L168), for example. The [Vector2](src/core/vector2.hpp) is another example of template class. | 220 | 221 | 222 | ### Memory Management 223 | 224 | | _Done_ | _Criteria_ | _Meets Specifications_ | _Address_ | 225 | |:-------: |:------------------------------------------------------------------------------------------ |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 226 | | ☑ | 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](src/sdl/font_maker.hpp#L22) is the only function implemented in this project. It uses two enums passed-by-reference: a [FontSize](src/core/game_font.hpp#L13) and a [FontTheme](src/core/game_font.hpp#L23). They both are used in a loop on [main.cc](src/main.cc#L31) file to assign all fonts used by the SDL version of the game to the [GameFontBuilder](src/sdl/game_font_builder.hpp). | 227 | | ☑ | 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](src/core/font.hpp) class require a [Typography](src/core/font.hpp#L15) 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](src/core/font.cc#L17) method to avoid memory leaks. | 228 | | ☐ | 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. | | 229 | | ☐ | 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. | | 230 | | ☐ | 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. | | 231 | | ☑ | 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](src/core/food.hpp) class and all the [inherited states](src/states) are good examples of smart pointer implementation on project. Just one pointer is used as [raw pointer](src/core/font.hpp#L134) on the project, and it’s used intentionally to suppress the Project Specification item above. | 232 | 233 | 234 | ### Concurrency 235 | 236 | | _Done_ | _Criteria_ | _Meets Specifications_ | _Address_ | 237 | |:-------: |:--------------------------------------------- |:----------------------------------------------------------------------------------------------------------------------------------------------------- |:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | 238 | | ☑ | The project uses multithreading. | The project uses multiple threads in the execution. | The [Snake](src/actor/snake.hpp)'s and the [Food](src/core/food.hpp) extend the abstract [GameThread](src/core/game_thread.hpp) class to implement a thread protected by the [base class barrier](src/core/game_thread.cc#L14).

They both implement a method called `shared_check_collision` and is executed on `prepare` method. The [Food inherited thread](src/core/food.cc#L58) checks if it’s colliding with Snake and send a message to the last one, alerting about that. The [Snake inherited thread](src/actor/snake.cc#L160) 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](src/core/food.hpp#L97) on [replace](src/core/food.hpp#L19) 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. | 239 | | ☐ | 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. | | 240 | | ☑ | 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](src/actor/snake.cc#L163) method, Food's [shared_check_collision](src/core/food.cc#L63) method and Food's [replace_food](src/core/food.cc#L98) method | 241 | | ☐ | A condition variable is used in the project. | A `std::condition_variable` is used in the project code to synchronize thread execution. | | 242 | -------------------------------------------------------------------------------- /cmake/FindSDL2_image.cmake: -------------------------------------------------------------------------------- 1 | # Distributed under the OSI-approved BSD 3-Clause License. See accompanying 2 | # file Copyright.txt or https://cmake.org/licensing for details. 3 | 4 | #.rst: 5 | # FindSDL2_image 6 | # ------------- 7 | # 8 | # Locate SDL2_image library 9 | # 10 | # This module defines: 11 | # 12 | # :: 13 | # 14 | # SDL2_IMAGE_LIBRARIES, the name of the library to link against 15 | # SDL2_IMAGE_INCLUDE_DIRS, where to find the headers 16 | # SDL2_IMAGE_FOUND, if false, do not try to link against 17 | # SDL2_IMAGE_VERSION_STRING - human-readable string containing the 18 | # version of SDL2_image 19 | # 20 | # 21 | # 22 | # For backward compatibility the following variables are also set: 23 | # 24 | # :: 25 | # 26 | # SDL2IMAGE_LIBRARY (same value as SDL2_IMAGE_LIBRARIES) 27 | # SDL2IMAGE_INCLUDE_DIR (same value as SDL2_IMAGE_INCLUDE_DIRS) 28 | # SDL2IMAGE_FOUND (same value as SDL2_IMAGE_FOUND) 29 | # 30 | # 31 | # 32 | # $SDLDIR is an environment variable that would correspond to the 33 | # ./configure --prefix=$SDLDIR used in building SDL. 34 | # 35 | # Created by Eric Wing. This was influenced by the FindSDL.cmake 36 | # module, but with modifications to recognize OS X frameworks and 37 | # additional Unix paths (FreeBSD, etc). 38 | 39 | if(NOT SDL2_IMAGE_INCLUDE_DIR AND SDL2IMAGE_INCLUDE_DIR) 40 | set(SDL2_IMAGE_INCLUDE_DIR ${SDL2IMAGE_INCLUDE_DIR} CACHE PATH "directory cache entry initialized from old variable name") 41 | endif() 42 | find_path(SDL2_IMAGE_INCLUDE_DIR SDL_image.h 43 | HINTS 44 | ENV SDL2IMAGEDIR 45 | ENV SDL2DIR 46 | ${SDL2_DIR} 47 | PATH_SUFFIXES SDL2 48 | # path suffixes to search inside ENV{SDL2DIR} 49 | include/SDL2 include 50 | ) 51 | 52 | if(CMAKE_SIZEOF_VOID_P EQUAL 8) 53 | set(VC_LIB_PATH_SUFFIX lib/x64) 54 | else() 55 | set(VC_LIB_PATH_SUFFIX lib/x86) 56 | endif() 57 | 58 | if(NOT SDL2_IMAGE_LIBRARY AND SDL2IMAGE_LIBRARY) 59 | set(SDL2_IMAGE_LIBRARY ${SDL2IMAGE_LIBRARY} CACHE FILEPATH "file cache entry initialized from old variable name") 60 | endif() 61 | find_library(SDL2_IMAGE_LIBRARY 62 | NAMES SDL2_image 63 | HINTS 64 | ENV SDL2IMAGEDIR 65 | ENV SDL2DIR 66 | ${SDL2_DIR} 67 | PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} 68 | ) 69 | 70 | if(SDL2_IMAGE_INCLUDE_DIR AND EXISTS "${SDL2_IMAGE_INCLUDE_DIR}/SDL2_image.h") 71 | file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL2_image.h" SDL2_IMAGE_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL2_IMAGE_MAJOR_VERSION[ \t]+[0-9]+$") 72 | file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL2_image.h" SDL2_IMAGE_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL2_IMAGE_MINOR_VERSION[ \t]+[0-9]+$") 73 | file(STRINGS "${SDL2_IMAGE_INCLUDE_DIR}/SDL2_image.h" SDL2_IMAGE_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL2_IMAGE_PATCHLEVEL[ \t]+[0-9]+$") 74 | string(REGEX REPLACE "^#define[ \t]+SDL2_IMAGE_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_MAJOR "${SDL2_IMAGE_VERSION_MAJOR_LINE}") 75 | string(REGEX REPLACE "^#define[ \t]+SDL2_IMAGE_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_MINOR "${SDL2_IMAGE_VERSION_MINOR_LINE}") 76 | string(REGEX REPLACE "^#define[ \t]+SDL2_IMAGE_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_IMAGE_VERSION_PATCH "${SDL2_IMAGE_VERSION_PATCH_LINE}") 77 | set(SDL2_IMAGE_VERSION_STRING ${SDL2_IMAGE_VERSION_MAJOR}.${SDL2_IMAGE_VERSION_MINOR}.${SDL2_IMAGE_VERSION_PATCH}) 78 | unset(SDL2_IMAGE_VERSION_MAJOR_LINE) 79 | unset(SDL2_IMAGE_VERSION_MINOR_LINE) 80 | unset(SDL2_IMAGE_VERSION_PATCH_LINE) 81 | unset(SDL2_IMAGE_VERSION_MAJOR) 82 | unset(SDL2_IMAGE_VERSION_MINOR) 83 | unset(SDL2_IMAGE_VERSION_PATCH) 84 | endif() 85 | 86 | set(SDL2_IMAGE_LIBRARIES ${SDL2_IMAGE_LIBRARY}) 87 | set(SDL2_IMAGE_INCLUDE_DIRS ${SDL2_IMAGE_INCLUDE_DIR}) 88 | 89 | include(FindPackageHandleStandardArgs) 90 | 91 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_image 92 | REQUIRED_VARS SDL2_IMAGE_LIBRARIES SDL2_IMAGE_INCLUDE_DIRS 93 | VERSION_VAR SDL2_IMAGE_VERSION_STRING) 94 | 95 | # for backward compatibility 96 | set(SDL2IMAGE_LIBRARY ${SDL2_IMAGE_LIBRARIES}) 97 | set(SDL2IMAGE_INCLUDE_DIR ${SDL2_IMAGE_INCLUDE_DIRS}) 98 | set(SDL2IMAGE_FOUND ${SDL2_IMAGE_FOUND}) 99 | 100 | mark_as_advanced(SDL2_IMAGE_LIBRARY SDL2_IMAGE_INCLUDE_DIR) -------------------------------------------------------------------------------- /cmake/FindSDL2_ttf.cmake: -------------------------------------------------------------------------------- 1 | # Locate SDL_image library 2 | # 3 | # This module defines: 4 | # 5 | # :: 6 | # 7 | # SDL_TTF_LIBRARIES, the name of the library to link against 8 | # SDL_TTF_INCLUDE_DIRS, where to find the headers 9 | # SDL_TTF_FOUND, if false, do not try to link against 10 | # SDL_F_VERSION_STRING - human-readable string containing the version of SDL_ttf 11 | # 12 | # 13 | # 14 | # For backward compatiblity the following variables are also set: 15 | # 16 | # :: 17 | # 18 | # SDLTTF_LIBRARY (same value as SDL_TTF_LIBRARIES) 19 | # SDLTTF_INCLUDE_DIR (same value as SDL_TTF_INCLUDE_DIRS) 20 | # SDLTTF_FOUND (same value as SDL_TTF_FOUND) 21 | # 22 | # 23 | # 24 | # $SDLDIR is an environment variable that would correspond to the 25 | # ./configure --prefix=$SDLDIR used in building SDL. 26 | # 27 | # Created by Eric Wing. This was influenced by the FindSDL.cmake 28 | # module, but with modifications to recognize OS X frameworks and 29 | # additional Unix paths (FreeBSD, etc). 30 | 31 | #============================================================================= 32 | # Copyright 2005-2009 Kitware, Inc. 33 | # Copyright 2012 Benjamin Eikel 34 | # 35 | # Distributed under the OSI-approved BSD License (the "License"); 36 | # see accompanying file Copyright.txt for details. 37 | # 38 | # This software is distributed WITHOUT ANY WARRANTY; without even the 39 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 40 | # See the License for more information. 41 | #============================================================================= 42 | # (To distribute this file outside of CMake, substitute the full 43 | # License text for the above reference.) 44 | 45 | find_path(SDL2_TTF_INCLUDE_DIR SDL_ttf.h 46 | HINTS 47 | ENV SDL2TTFDIR 48 | ENV SDL2DIR 49 | PATH_SUFFIXES SDL2 50 | # path suffixes to search inside ENV{SDLDIR} 51 | include/SDL2 include 52 | PATHS ${SDL2_PATH} 53 | ) 54 | 55 | if (CMAKE_SIZEOF_VOID_P EQUAL 8) 56 | set(VC_LIB_PATH_SUFFIX lib/x64) 57 | else () 58 | set(VC_LIB_PATH_SUFFIX lib/x86) 59 | endif () 60 | 61 | find_library(SDL2_TTF_LIBRARY 62 | NAMES SDL2_ttf 63 | HINTS 64 | ENV SDL2TTFDIR 65 | ENV SDL2DIR 66 | PATH_SUFFIXES lib ${VC_LIB_PATH_SUFFIX} 67 | PATHS ${SDL2_PATH} 68 | ) 69 | 70 | if (SDL2_TTF_INCLUDE_DIR AND EXISTS "${SDL2_TTF_INCLUDE_DIR}/SDL_ttf.h") 71 | file(STRINGS "${SDL2_TTF_INCLUDE_DIR}/SDL_ttf.h" SDL2_TTF_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_TTF_MAJOR_VERSION[ \t]+[0-9]+$") 72 | file(STRINGS "${SDL2_TTF_INCLUDE_DIR}/SDL_ttf.h" SDL2_TTF_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_TTF_MINOR_VERSION[ \t]+[0-9]+$") 73 | file(STRINGS "${SDL2_TTF_INCLUDE_DIR}/SDL_ttf.h" SDL2_TTF_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_TTF_PATCHLEVEL[ \t]+[0-9]+$") 74 | string(REGEX REPLACE "^#define[ \t]+SDL_TTF_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_TTF_VERSION_MAJOR "${SDL2_TTF_VERSION_MAJOR_LINE}") 75 | string(REGEX REPLACE "^#define[ \t]+SDL_TTF_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_TTF_VERSION_MINOR "${SDL2_TTF_VERSION_MINOR_LINE}") 76 | string(REGEX REPLACE "^#define[ \t]+SDL_TTF_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_TTF_VERSION_PATCH "${SDL2_TTF_VERSION_PATCH_LINE}") 77 | set(SDL2_TTF_VERSION_STRING ${SDL2_TTF_VERSION_MAJOR}.${SDL2_TTF_VERSION_MINOR}.${SDL2_TTF_VERSION_PATCH}) 78 | unset(SDL2_TTF_VERSION_MAJOR_LINE) 79 | unset(SDL2_TTF_VERSION_MINOR_LINE) 80 | unset(SDL2_TTF_VERSION_PATCH_LINE) 81 | unset(SDL2_TTF_VERSION_MAJOR) 82 | unset(SDL2_TTF_VERSION_MINOR) 83 | unset(SDL2_TTF_VERSION_PATCH) 84 | endif () 85 | 86 | set(SDL2_TTF_LIBRARIES ${SDL2_TTF_LIBRARY}) 87 | set(SDL2_TTF_INCLUDE_DIRS ${SDL2_TTF_INCLUDE_DIR}) 88 | 89 | include(FindPackageHandleStandardArgs) 90 | 91 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2_ttf 92 | REQUIRED_VARS SDL2_TTF_LIBRARIES SDL2_TTF_INCLUDE_DIRS 93 | VERSION_VAR SDL2_TTF_VERSION_STRING) 94 | 95 | # for backward compatiblity 96 | #set(SDLTTF_LIBRARY ${SDL_TTF_LIBRARIES}) 97 | #set(SDLTTF_INCLUDE_DIR ${SDL_TTF_INCLUDE_DIRS}) 98 | #set(SDLTTF_FOUND ${SDL_TTF_FOUND}) 99 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") 4 | 5 | find_package (Threads REQUIRED) 6 | find_package(SDL2 REQUIRED) 7 | find_package(SDL2_image REQUIRED) 8 | find_package(SDL2_ttf REQUIRED) 9 | 10 | include_directories(${SDL2_INCLUDE_DIRS} ${SDL2_IMAGE_INCLUDE_DIRS} ${SDL2_TTF_INCLUDE_DIR}) 11 | 12 | add_executable(${CMAKE_PROJECT_NAME} main.cc core/controller.hpp core/game.hpp core/game.cc core/renderer.hpp actor/snake.hpp actor/snake.cc core/color.hpp core/color.cc core/vector2.hpp core/vector2.inl core/allocation.hpp core/allocation.inl core/game_state.hpp core/game_object.hpp core/block.cc core/block.hpp states/main_state.cc states/main_state.hpp sdl/renderer.cc sdl/renderer.hpp core/game_loop.hpp sdl/game_loop.cc sdl/game_loop.hpp sdl/controller.cc sdl/controller.hpp core/random_position.cc core/random_position.hpp core/food.cc core/food.hpp core/game_thread.cc core/game_thread.hpp core/font.cc core/font.hpp core/game_font.hpp sdl/typography.cc sdl/typography.hpp sdl/game_font_builder.cc sdl/game_font_builder.hpp sdl/font_maker.cc sdl/font_maker.hpp states/splash_screen_state.cc states/splash_screen_state.hpp core/button_chooser.cc core/button_chooser.hpp states/pause_state.cc states/pause_state.hpp states/end_game_state.cc states/end_game_state.hpp) 13 | target_link_libraries(${CMAKE_PROJECT_NAME} ${SDL2_LIBRARIES} ${SDL2_LIBRARIES} ${SDL2_IMAGE_LIBRARIES} ${SDL2_TTF_LIBRARIES} pthread) 14 | 15 | add_custom_command(TARGET ${CMAKE_PROJECT_NAME} PRE_BUILD 16 | COMMAND ${CMAKE_COMMAND} -E copy_directory 17 | ${CMAKE_SOURCE_DIR}/src/resources $/resources) 18 | -------------------------------------------------------------------------------- /src/actor/snake.cc: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "snake.hpp" 4 | 5 | namespace Capstone { 6 | 7 | // Default constructor 8 | Snake::Snake (): 9 | m_status(SnakeStatus::kWalking), 10 | m_direction(Direction::kUp), 11 | m_speed(0.2f), 12 | m_growing{false}, 13 | alive{true} 14 | { 15 | 16 | } 17 | 18 | // This updates the Snake's behavior 19 | void Snake::update (std::size_t delta_time) 20 | { 21 | if (m_status == SnakeStatus::kColliding) 22 | { 23 | // It changes the head color when the Snake collides 24 | head_color = Color::Red; 25 | alive = false; 26 | return; 27 | } 28 | 29 | // We first capture the head's cell before updating 30 | iVector2 prev_cell = iVector2(offset); 31 | update_head (); 32 | 33 | // After update, we capture the head's cell again 34 | iVector2 current_cell = iVector2(offset); 35 | 36 | // Update all of the body vector items if the snake head has moved to a new cell 37 | if (current_cell != prev_cell) 38 | { 39 | // Add previous head location to vector if its growing (eating food) 40 | if (m_growing){ 41 | push(std::move(prev_cell)); 42 | m_growing = false; 43 | } 44 | 45 | // It ensures the body will follow the Snake's head 46 | for(auto &item: body) { 47 | auto current_cell = item.offset; 48 | item.offset = prev_cell; 49 | prev_cell = current_cell; 50 | } 51 | } 52 | } 53 | 54 | // This render the Snake's head and body, properly 55 | void Snake::render (Renderer& renderer) 56 | { 57 | // Render snake's body 58 | renderer.fill_color (body_color); 59 | for (const Block& point: body) 60 | { 61 | renderer.fill(point); 62 | } 63 | 64 | // Render snake's head 65 | renderer.fill_color (head_color); 66 | renderer.fill(get_allocation_as ()); 67 | } 68 | 69 | // This prepares the Snake to be rendered 70 | void Snake::prepare (Renderer& renderer) 71 | { 72 | // This ensures the Snake will "warp" correctly on the window edge 73 | m_grid = renderer.get_grid (); 74 | 75 | // This adjusts the size to the renderer grid 76 | size = fVector2( 77 | 1.f * renderer.get_screen ().x / renderer.get_grid ().x, 78 | 1.f * renderer.get_screen ().y / renderer.get_grid ().y 79 | ); 80 | 81 | // The first offset is the bottom-left window corner 82 | offset = { 83 | m_grid.x - 1.f, 84 | m_grid.y - 1.f, 85 | }; 86 | 87 | // It changes the Snake's head and body color 88 | body_color = Color::White; 89 | head_color = Color(0x7ACCFFFF); 90 | 91 | // This executes the collision check on another thread 92 | m_threads.emplace_back(std::thread(&Snake::shared_check_collision, this)); 93 | } 94 | 95 | // This defines the own input handler 96 | void Snake::handle_input (const KeyPressed & key) 97 | { 98 | switch (key) 99 | { 100 | case KeyPressed::kUp: 101 | change_direction (Direction::kUp, Direction::kDown); 102 | break; 103 | case KeyPressed::kDown: 104 | change_direction (Direction::kDown, Direction::kUp); 105 | break; 106 | case KeyPressed::kLeft: 107 | change_direction (Direction::kLeft, Direction::kRight); 108 | break; 109 | case KeyPressed::kRight: 110 | change_direction (Direction::kRight, Direction::kLeft); 111 | break; 112 | } 113 | } 114 | 115 | // It allows the class to avoid change the position to the opposite direction 116 | void Snake::change_direction (const Snake::Direction input, const Snake::Direction oposite) 117 | { 118 | if (m_direction != oposite) m_direction = input; 119 | } 120 | 121 | // This updates the offset direction and speed 122 | void Snake::update_head () 123 | { 124 | // Changes the head direction and accelerate it in a constant speed; 125 | switch(m_direction) 126 | { 127 | case Direction::kUp: 128 | offset.y -= m_speed; 129 | break; 130 | case Direction::kDown: 131 | offset.y += m_speed; 132 | break; 133 | case Direction::kLeft: 134 | offset.x -= m_speed; 135 | break; 136 | case Direction::kRight: 137 | offset.x += m_speed; 138 | break; 139 | } 140 | 141 | // Wraps the snake around to the beginning if going off the screen. 142 | offset.x = fmod(offset.x + m_grid.x, m_grid.x); 143 | offset.y = fmod(offset.y + m_grid.y, m_grid.y); 144 | } 145 | 146 | // It allows another GameThread to dispatch SnakeStatus when 147 | // it collides with Snake 148 | void Snake::set_status (SnakeStatus&& status) 149 | { 150 | m_status = status; 151 | } 152 | 153 | // It adds the block to the Snake's body vector 154 | void Snake::push (const iVector2&& offset) 155 | { 156 | body.emplace_back (Block(iVector2(size), offset)); 157 | } 158 | 159 | // This checks if the Snake collides with their body 160 | void Snake::shared_check_collision () 161 | { 162 | while(true) 163 | { 164 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 165 | 166 | std::unique_lock lock(m_mutex); 167 | // Check if the snake has died 168 | for (const auto &item: body) 169 | { 170 | if (item.offset == iVector2(offset)) 171 | { 172 | m_status = SnakeStatus::kColliding; 173 | } 174 | } 175 | } 176 | } 177 | 178 | const SnakeStatus Snake::get_status () const 179 | { 180 | return m_status; 181 | } 182 | 183 | // This reset the Snake behavior 184 | void Snake::reset () 185 | { 186 | body.clear (); 187 | alive = true; 188 | m_speed = 0.2f; 189 | m_growing = false; 190 | offset = { m_grid.x - 1.f, m_grid.y - 1.f}; 191 | head_color = Color(0x7ACCFFFF); 192 | m_status = SnakeStatus::kWalking; 193 | m_direction = Direction::kUp; 194 | } 195 | 196 | // This alert the Snake it's growing 197 | void Snake::growing () 198 | { 199 | m_growing = true; 200 | m_status = SnakeStatus::kWalking; 201 | m_speed += 0.01; 202 | } 203 | 204 | } // namespace Capstone -------------------------------------------------------------------------------- /src/actor/snake.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_ACTOR_SNAKE_HPP 3 | #define CPP_SNAKE_GAME_ACTOR_SNAKE_HPP 4 | 5 | #include 6 | 7 | #include "../core/block.hpp" 8 | #include "../core/controller.hpp" 9 | #include "../core/game_thread.hpp" 10 | 11 | namespace Capstone 12 | { 13 | 14 | /** 15 | * This is the main GameObject in the game. 16 | * 17 | * The player can handle the GameObject using the keyboard 18 | */ 19 | class Snake: public GameThread, public fAllocation { 20 | public: 21 | 22 | /** 23 | * It defines the four possible direction 24 | */ 25 | enum class Direction { kUp, kDown, kLeft, kRight }; 26 | 27 | /** 28 | * Default constructor 29 | */ 30 | Snake(); 31 | 32 | /** 33 | * This updates the snakes behavior 34 | * 35 | * It controls all the Snake behavior 36 | * 37 | * @param delta_time The elapsed time between each GameLoop 38 | */ 39 | void update(std::size_t delta_time) override; 40 | 41 | /** 42 | * This returns the current SnakeStatus 43 | * 44 | * It changes always the Snake collides with another GameObject 45 | * 46 | * @return SnakeStatus The SnakeStatus dispatched on collision 47 | */ 48 | const SnakeStatus get_status () const override; 49 | 50 | /** 51 | * This render the Snake's head and body 52 | * 53 | * @param renderer The renderer instance 54 | */ 55 | void render(Renderer &renderer) override; 56 | 57 | /** 58 | * This prepares the Snake to be rendered. 59 | * 60 | * @param renderer The renderer instance 61 | */ 62 | void prepare(Renderer &renderer) override; 63 | 64 | /** 65 | * This creates the own input handler. 66 | * 67 | * It allows the Snake easily to change direction 68 | * 69 | * @param key The key pressed by the user 70 | */ 71 | void handle_input(const KeyPressed & key); 72 | 73 | /** 74 | * This alerts the Snake to add a Block to their body vector. 75 | */ 76 | void growing(); 77 | 78 | /** 79 | * This allows the GameThread inherited class to assign the correct 80 | * SnakeStatus to the Snake. 81 | * 82 | * @param status The SnakeStatus dispatched when Snake collides 83 | */ 84 | void set_status(SnakeStatus &&status); 85 | 86 | /** 87 | * Reset the Snake 88 | */ 89 | void reset(); 90 | 91 | public: 92 | /** 93 | * The Snake's body vector 94 | */ 95 | std::vector body; 96 | 97 | /** 98 | * This defines the Snake's body and head default color 99 | */ 100 | Color body_color; 101 | Color head_color; 102 | 103 | /** 104 | * This defines if the Snake is alive 105 | */ 106 | bool alive; 107 | 108 | private: 109 | /** 110 | * The current Snake direction 111 | */ 112 | Direction m_direction = Direction::kUp; 113 | 114 | /** 115 | * This allows the Snake to avoid change the position to the opposite 116 | * direction. 117 | * 118 | * @param input The expected direction 119 | * @param oposite The avoided opposite direction 120 | */ 121 | void change_direction(const Direction input, const Direction oposite); 122 | 123 | 124 | /** 125 | * This add a Block to the Snake's body 126 | * 127 | * This will grow the body using the previous head offset 128 | * @param offset The previous Snake's head offset 129 | */ 130 | void push (const iVector2&& offset); 131 | 132 | /** 133 | * This checks collision between Snake's head and body 134 | * 135 | * It's executed in another thread 136 | */ 137 | void shared_check_collision (); 138 | 139 | /** 140 | * This update the Snake's position 141 | * 142 | * The method uses the direction to increment the speed to the correct 143 | * coordinate. 144 | */ 145 | void update_head(); 146 | 147 | private: 148 | /** 149 | * This keeps a reference to the game grid size. It's useful to warp 150 | * the Snake when it collides with the window border. 151 | */ 152 | iVector2 m_grid; 153 | 154 | /** 155 | * This hold the Snake's speed. It increases while the Snake's growing. 156 | */ 157 | float m_speed; 158 | 159 | /** 160 | * This holds if the Snake should grow. 161 | */ 162 | bool m_growing; 163 | 164 | /** 165 | * This defines the Snake's status 166 | * 167 | * It changes the value when Snake collides with another GameObject 168 | */ 169 | SnakeStatus m_status; 170 | }; 171 | 172 | } // namespace Capstone 173 | 174 | #endif // CPP_SNAKE_GAME_ACTOR_SNAKE_HPP 175 | -------------------------------------------------------------------------------- /src/core/allocation.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_CORE_ALLOCATION_HPP 3 | #define CPP_SNAKE_GAME_CORE_ALLOCATION_HPP 4 | 5 | #include "vector2.hpp" 6 | 7 | namespace Capstone 8 | { 9 | 10 | /** 11 | * The bounding box 12 | * 13 | * It holds each edge's offset of the object, starting from top 14 | * to bottom, from left to right of screen converting the values 15 | * to integer to make collision check easier 16 | */ 17 | struct BoundingBox 18 | { 19 | /** 20 | * This defines the constant edge of each object's offset 21 | * converting the values to integer. 22 | * 23 | * @param top The top edged of the object in pixel 24 | * @param bottom The bottom edged of the object in pixel 25 | * @param left The left edged of the object in pixels 26 | * @param right The right edged of the object in pixels 27 | */ 28 | BoundingBox(const int &top, const int &bottom, const int &left, const int &right): 29 | top(top), 30 | left(left), 31 | bottom(bottom), 32 | right(right) 33 | { 34 | 35 | } 36 | 37 | /** 38 | * There are the constant edge of each object's offset 39 | */ 40 | const int top; 41 | const int bottom; 42 | const int right; 43 | const int left; 44 | }; 45 | 46 | 47 | /** 48 | * Utility template class for manipulating offset and size of an object. 49 | * 50 | * This defines the object's location and make collision's check easier. 51 | * 52 | * @tparam T Commonly a number type 53 | */ 54 | template 55 | class Allocation 56 | { 57 | public: 58 | /** 59 | * Default constructor 60 | * 61 | * This defines the offset and the size as zero. 62 | */ 63 | Allocation(); 64 | 65 | /** 66 | * Parameterized constructor 67 | * 68 | * This defines the offset and the size of the object. 69 | * 70 | * @param size The width and height of object 71 | * @param offset The offset of the object 72 | */ 73 | Allocation(Vector2 size, Vector2 offset); 74 | 75 | /** 76 | * This constructs an Allocation from another one 77 | * 78 | * This constructor doesn't replace the copy constructor, it's called 79 | * only when U != T. A call to this constructor will fail to compile 80 | * if U is not convertible to T. 81 | * 82 | * @tparam U The original type; 83 | * @param vector Allocation that will be converted to current type. 84 | */ 85 | template 86 | explicit Allocation (const Allocation& vector); 87 | 88 | /** 89 | * This copy the current offset and size values into a new Allocation 90 | * from another type. 91 | * 92 | * It uses the type deduction to convert the current Allocation to 93 | * another type as follows: 94 | * 95 | * auto int_alloc = Capstone::fAllocation.get_allocation_as(); 96 | * 97 | * This example will convert the Allocation of float type to the Allocation 98 | * of int type. 99 | * 100 | * @tparam U The Allocation type expected 101 | * @return Allocation 102 | */ 103 | template 104 | const Allocation get_allocation_as () const; 105 | 106 | 107 | /** 108 | * This returns the bounding box of the current object. 109 | * 110 | * The bounding box defines each edge's offset of the object from top 111 | * to bottom, left to right of screen. 112 | * 113 | * @return BoundingBox The value of each offset converted to integer 114 | */ 115 | const BoundingBox get_bounding_box() const; 116 | 117 | /** 118 | * This checks collision of two Allocations 119 | * 120 | * This identifies if the current object is colliding with another one 121 | * 122 | * @tparam U The Allocation type expected 123 | * @param other Allocation that will be compared 124 | * @return boolean 125 | */ 126 | template 127 | bool check_collision(const Allocation& other) const; 128 | 129 | public: 130 | /** 131 | * This defines the width and height of an element using a template class 132 | * Vector2 133 | */ 134 | Vector2 size; 135 | 136 | /** 137 | * This defines the offset of an element using a template class Vector2 138 | */ 139 | Vector2 offset; 140 | }; 141 | 142 | /** 143 | * Overload of binary operator == 144 | * 145 | * This operator compares strict equality between two Allocations. 146 | * 147 | * @param left Left operand (a Allocation) 148 | * @param right Right operand (a Allocation) 149 | */ 150 | template 151 | bool operator ==(const Allocation& left, const Allocation& right); 152 | 153 | /** 154 | * Overload of binary operator != 155 | * 156 | * This operator compares strict difference between two Allocations. 157 | * 158 | * @param left Left operand (a Allocation) 159 | * @param right Right operand (a Allocation) 160 | */ 161 | template 162 | bool operator !=(const Allocation& left, const Allocation& right); 163 | 164 | #include "allocation.inl" 165 | 166 | 167 | // Defines the most common Allocation types 168 | typedef Allocation iAllocation; 169 | typedef Allocation uAllocation; 170 | typedef Allocation fAllocation; 171 | 172 | } // namespace Capstone 173 | 174 | #endif // CPP_SNAKE_GAME_CORE_ALLOCATION_HPP 175 | -------------------------------------------------------------------------------- /src/core/allocation.inl: -------------------------------------------------------------------------------- 1 | 2 | #include "allocation.hpp" 3 | 4 | // This defines the default constructor with size and offset as zero 5 | template 6 | Capstone::Allocation::Allocation (): 7 | size({ 0, 0 }), 8 | offset({ 0, 0 }) 9 | { 10 | 11 | } 12 | 13 | // It allows the client to define the offset and size of an object 14 | template 15 | Capstone::Allocation::Allocation (Capstone::Vector2 size, Capstone::Vector2 offset): 16 | size(size), 17 | offset(offset) 18 | { 19 | 20 | } 21 | 22 | // This copy and convert a type of Allocation to another one 23 | template 24 | template 25 | Capstone::Allocation::Allocation (const Capstone::Allocation& vector): 26 | size(static_cast(vector.size.x), static_cast(vector.size.y)), 27 | offset(static_cast(vector.offset.x), static_cast(vector.offset.y)) 28 | { 29 | 30 | } 31 | 32 | template 33 | template 34 | const Allocation Capstone::Allocation::get_allocation_as () const 35 | { 36 | // This returns a "copy" of the current Allocation with the type assigned 37 | // to template type deduction `U` 38 | return Allocation(*this); 39 | } 40 | 41 | // This creates the offset of each box edge and returns the BoundingBox as well 42 | template 43 | const Capstone::BoundingBox Capstone::Allocation::get_bounding_box() const 44 | { 45 | auto cast = get_allocation_as (); 46 | 47 | return { 48 | cast.offset.y * cast.size.y, // top 49 | cast.offset.y * cast.size.y + cast.size.y, // bottom 50 | cast.offset.x * cast.size.x, // left 51 | cast.offset.x * cast.size.x + cast.size.x // right 52 | }; 53 | } 54 | 55 | // This checks if the current object and another one are colliding 56 | template 57 | template 58 | bool Capstone::Allocation::check_collision (const Capstone::Allocation& other) const 59 | { 60 | auto current = get_bounding_box (); 61 | auto target = other.get_bounding_box (); 62 | 63 | return (current.right > target.left) && (target.right > current.left) && // It checks X coordinate 64 | (current.bottom > target.top) && (target.bottom > current.top); // It checks Y coordinate 65 | } 66 | 67 | // This compares the size and offset of two Allocation of the same type 68 | template 69 | bool operator ==(const Capstone::Allocation& left, const Capstone::Allocation& right) 70 | { 71 | return (left.size == right.size) && (left.offset == right.offset); 72 | } 73 | 74 | // This checks if the size and offset of two Allocations are different when they both have the same type 75 | template 76 | bool operator !=(const Capstone::Allocation& left, const Capstone::Allocation& right) 77 | { 78 | return !(left == right); 79 | } 80 | -------------------------------------------------------------------------------- /src/core/block.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "block.hpp" 3 | 4 | namespace Capstone 5 | { 6 | 7 | Block::Block (): 8 | iAllocation::Allocation{{ 0 }, { 0 }} 9 | { 10 | 11 | } 12 | 13 | Block::Block (iVector2 size, iVector2 offset): 14 | iAllocation::Allocation{ size, offset } 15 | { 16 | 17 | } 18 | 19 | void Block::update (std::size_t delta_time) 20 | { 21 | 22 | } 23 | 24 | void Block::render (Renderer& renderer) 25 | { 26 | // Defines the color and display the current GameObject into the screen 27 | renderer.fill_color (color); 28 | renderer.fill(*this); 29 | } 30 | 31 | void Block::prepare (Renderer& renderer) 32 | { 33 | size = iVector2( 34 | renderer.get_screen ().x / renderer.get_grid ().x, 35 | renderer.get_screen ().y / renderer.get_grid ().y 36 | ); 37 | } 38 | 39 | } // namespace Capstone 40 | -------------------------------------------------------------------------------- /src/core/block.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_CORE_BLOCK_HPP 3 | #define CPP_SNAKE_GAME_CORE_BLOCK_HPP 4 | 5 | #include "renderer.hpp" 6 | #include "allocation.hpp" 7 | #include "game_object.hpp" 8 | 9 | namespace Capstone 10 | { 11 | class Block: public iAllocation, public GameObject 12 | { 13 | public: 14 | /** 15 | * Default constructor 16 | */ 17 | Block(); 18 | 19 | /** 20 | * Parameterized constructor 21 | * 22 | * @param size The height and the width of the object 23 | * @param offset The horizontal and vertical offset of the object 24 | */ 25 | explicit Block(iVector2 size, iVector2 offset); 26 | 27 | /** 28 | * This method is implemented to supply GameObject interface, 29 | * but it's not used by Block class 30 | * 31 | * @param delta_time The elapsed time since the last game loop 32 | */ 33 | void update(std::size_t delta_time) override; 34 | 35 | /** 36 | * This renders the current GameObject 37 | * 38 | * It allows the current class to define the GameObject color and 39 | * display it on the screen. 40 | * 41 | * @param renderer The common Renderer instance 42 | */ 43 | void render(Renderer &renderer) override; 44 | 45 | /** 46 | * This method is implemented to supply GameObject interface, 47 | * but it's not used by Block class 48 | * 49 | * @param Renderer The common Renderer instance 50 | */ 51 | void prepare(Renderer &renderer) override; 52 | 53 | public: 54 | /** 55 | * This defines the color of the current GameObject. 56 | */ 57 | Color color; 58 | }; 59 | 60 | } // namespace Capstone 61 | 62 | #endif // CPP_SNAKE_GAME_CORE_BLOCK_HPP 63 | -------------------------------------------------------------------------------- /src/core/button_chooser.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "button_chooser.hpp" 3 | 4 | namespace Capstone 5 | { 6 | 7 | // This creates a Button using a font and a function 8 | Button::Button (std::unique_ptr font, const std::function&& command): 9 | font{std::move(font)}, 10 | m_command{command} 11 | { 12 | 13 | } 14 | 15 | // This executes the defined Button command 16 | void Button::execute () 17 | { 18 | m_command(); 19 | } 20 | 21 | 22 | // This defines the first button on vector as the active one 23 | ButtonChooser::ButtonChooser (): 24 | m_cursor{0}, 25 | active{Color(0xFFCC00FF)} 26 | { 27 | 28 | } 29 | 30 | void ButtonChooser::update (std::size_t delta_time) 31 | { 32 | // This ensures the buttons will update their colors 33 | for (auto &button: m_buttons) 34 | { 35 | button.font->update (delta_time); 36 | } 37 | } 38 | 39 | // This renders the buttons 40 | void ButtonChooser::render (Renderer& renderer) 41 | { 42 | for (auto &button: m_buttons) 43 | { 44 | button.font->render (renderer); 45 | } 46 | } 47 | 48 | void ButtonChooser::prepare (Renderer& renderer) 49 | { 50 | // It stores the original font color. It's useful when the cursor 51 | // moves, selecting a new Button as active and the previous one need 52 | // to back to the original color. 53 | for (auto &button: m_buttons) 54 | { 55 | button.color = button.font->color; 56 | } 57 | 58 | // This defines the first button as the active one 59 | m_buttons[m_cursor].font->color = active; 60 | } 61 | 62 | 63 | void ButtonChooser::handle_input (const KeyPressed &key) 64 | { 65 | if (key == KeyPressed::kUp || key == KeyPressed::kDown) 66 | { 67 | // This back the original color to the previous button in focus 68 | m_buttons[m_cursor].font->color = m_buttons[m_cursor].color; 69 | } 70 | 71 | switch (key) 72 | { 73 | // This defines a vertical carousel effect to select the Button in focus 74 | case KeyPressed::kUp: 75 | if (m_cursor == 0) m_cursor = static_cast(m_buttons.size()); 76 | --m_cursor; 77 | break; 78 | 79 | // This defines a vertical carousel effect to select the Button in focus 80 | case KeyPressed::kDown: 81 | ++m_cursor; 82 | break; 83 | 84 | // This execute the Button in focus 85 | case KeyPressed::kEnter: 86 | m_buttons[m_cursor].execute (); 87 | return; 88 | } 89 | 90 | // This defines the cursor of the button in focus 91 | m_cursor = m_cursor % static_cast(m_buttons.size()); 92 | m_buttons[m_cursor].font->color = active; 93 | } 94 | 95 | 96 | void ButtonChooser::add_button (std::unique_ptr font, const std::function&& command) 97 | { 98 | // This creates a Button and push it to the vector 99 | m_buttons.emplace_back (Button(std::move(font), std::move(command))); 100 | } 101 | } // namespace Capstone 102 | -------------------------------------------------------------------------------- /src/core/button_chooser.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef CPP_SNAKE_GAME_CORE_BUTTON_CHOOSER_HPP 3 | #define CPP_SNAKE_GAME_CORE_BUTTON_CHOOSER_HPP 4 | 5 | #include 6 | #include "game_object.hpp" 7 | #include "font.hpp" 8 | #include "controller.hpp" 9 | 10 | namespace Capstone 11 | { 12 | 13 | /** 14 | * This tries to imitate a Command design pattern, allowing a font 15 | * execute a command defined as a function. 16 | */ 17 | class Button 18 | { 19 | public: 20 | /** 21 | * Parametrized constructor 22 | * 23 | * This defines a font to be displayed on screen and a command to be 24 | * executed, simulating a button behavior. 25 | * 26 | * @param font This defines a well configured font. 27 | * @param command This defines a function to be executed when necessary 28 | */ 29 | Button(std::unique_ptr font, const std::function&& command); 30 | 31 | /** 32 | * This executes the function defined on constructor 33 | * 34 | * It allows a font can be used to execute a command, without creating 35 | * a brand new class to encapsulate this behavior. 36 | */ 37 | void execute(); 38 | 39 | public: 40 | /** 41 | * This saves the original font color, so if it changes 42 | * it's possible to rollback when necessary. 43 | */ 44 | Color color; 45 | 46 | /** 47 | * This hold the font to be displayed as Button label. 48 | */ 49 | std::unique_ptr font; 50 | 51 | protected: 52 | /** 53 | * This defines the command to be dispatched when the `execute` method 54 | * is invoked. 55 | */ 56 | std::function m_command; 57 | }; 58 | 59 | /** 60 | * This class allows a font to behave like a button, executing 61 | * a command when necessary. 62 | */ 63 | class ButtonChooser: public GameObject 64 | { 65 | public: 66 | /** 67 | * Default constructor 68 | */ 69 | ButtonChooser(); 70 | 71 | /** 72 | * Destructor 73 | */ 74 | virtual ~ButtonChooser() = default; 75 | 76 | /** 77 | * This updates the Button behavior 78 | * 79 | * It ensures the Button in focus will assume the "active" color, 80 | * and the last selected will back their original color as well. 81 | * 82 | * @param delta_time The elapsed time between each GameLoop 83 | */ 84 | void update (std::size_t delta_time) override; 85 | 86 | 87 | /** 88 | * This render the Button 89 | * 90 | * It renders all Button on the vector. 91 | * 92 | * @param renderer The renderer instance 93 | */ 94 | void render (Renderer& renderer) override; 95 | 96 | /** 97 | * This prepares the Buttons to be rendered 98 | * 99 | * It assigns the active color, to the first button on the vector. 100 | * 101 | * @param renderer The renderer instance 102 | */ 103 | void prepare (Renderer& renderer) override; 104 | 105 | /** 106 | * It allows the current class to define its own input handler 107 | * 108 | * It ensures the Button can be selected and executed properly. 109 | * 110 | * @param key The command dispatched that can be used to handle the Button 111 | */ 112 | void handle_input(const KeyPressed &key); 113 | 114 | /** 115 | * This creates a Button. 116 | * 117 | * This adds a font and a function, mixing them to create a button and 118 | * push them to the vector. 119 | * 120 | * @param font The font smart pointer to represent the Button label 121 | * @param command The command used to create the Button behavior when dispatched 122 | */ 123 | void add_button(std::unique_ptr font, const std::function&& command); 124 | 125 | public: 126 | /** 127 | * This defines the Button's color is focus 128 | */ 129 | Color active; 130 | 131 | protected: 132 | /** 133 | * The index of the activated button on vector 134 | */ 135 | int m_cursor; 136 | 137 | /** 138 | * The button vector 139 | */ 140 | std::vector