├── .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 [](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