Compare commits

..

No commits in common. "master" and "V1.0" have entirely different histories.
master ... V1.0

13 changed files with 540 additions and 715 deletions

17
.gitignore vendored
View File

@ -33,20 +33,7 @@
*.json
*.ps1
# ---> CMake
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
build
# Extras
.vs*
.cache
build
bin

View File

@ -4,7 +4,7 @@ project(
snakeplusplus
LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23 CACHE STRING "The C++ standard to use")
set(CMAKE_CXX_STANDARD 11 CACHE STRING "The C++ standard to use")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

View File

@ -1,13 +1,13 @@
#include "botinterface.hpp"
#include "common.hpp"
#include "gamestate.hpp"
#include <array>
#include <cstdlib>
#include <iostream>
#include <queue>
#include <stdexcept>
#include <SFML/System/Vector2.hpp>
namespace snakeplusplus
{
PlayerDirection lastKnownDirection = kNone;
AISnake::AISnake() {
@ -17,10 +17,8 @@ AISnake::AISnake() {
PlayerDirection AISnake::GetInput(const sf::Vector2f* source)
{
sf::Vector2f directionDelta;
if (!source)
return kUp;
while (*source == path.top() && !path.empty()) { path.pop(); }
if (path.empty()) { path.push(GetAnyOpenPath(*source)); }
if (*source == path.top()) { path.pop(); }
if (path.empty()) { return kUp; } // Snake is trapped
directionDelta = *source - path.top();
path.pop();
if ((directionDelta.y == 1)
@ -38,66 +36,42 @@ PlayerDirection AISnake::GetInput(const sf::Vector2f* source)
return lastKnownDirection;
}
void AISnake::UpdateProbability(int snakeSize)
{
probabilityBFS = 1 - ((double) snakeSize) / 1000;
return;
}
void AISnake::AdjustProbability(double amount)
{
probabilityBFS += amount;
if (probabilityBFS > 1.0) { probabilityBFS = 1.0; }
if (probabilityBFS < 0.0) { probabilityBFS = 0.0; }
return;
}
void AISnake::AddIteration(const int size)
{
if (size > 40)
{
UpdateAverage(size);
double adjustmentAmount = 0.002;
if (average > size) { AdjustProbability(adjustmentAmount); }
else { AdjustProbability(-adjustmentAmount); }
}
std::cout << "[LOG - AI] Current average: " << average << std::endl;
std::cout << "[LOG - AI] Previous iteration size: " << size << std::endl;
}
void AISnake::ResetPath(void) {
while (!path.empty()) { path.pop(); }
}
// Gets a new path for the bot to follow
// Uses DFS algorithm
void AISnake::GetNewPath(const sf::Vector2f& source)
void AISnake::GetNewPath(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries, const int snakeSize)
{
// Search for food
// Probability-based approach for fun
double roll = ((double) GenerateRandomNumber(RAND_MAX)) / ((double) RAND_MAX);
if (roll <= probabilityBFS) { BFS(source); }
else { DFS(source); }
UnvisitBoard();
if (pathFailed) {
pathFailed = false;
EmptyPath();
path.push(GetAnyOpenPath(source));
if (snakeSize < 135) {
BFS(gameBoard, source, boundaries);
} else {
TrimPath();
if (path.empty())
path.push(GetAnyOpenPath(source));
DFS(gameBoard, source, boundaries);
}
// Create path for food
path.push(botPathUnsanitized.top());
botPathUnsanitized.pop();
while (!botPathUnsanitized.empty()) {
sf::Vector2f deltaVector = botPathUnsanitized.top() - path.top();
int delta = abs(deltaVector.x) + abs(deltaVector.y);
if (delta == 1) {
path.push(botPathUnsanitized.top());
}
botPathUnsanitized.pop();
}
}
void AISnake::BFS(const sf::Vector2f& source) {
void AISnake::BFS(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries) {
std::queue<sf::Vector2f> search;
std::vector<std::vector<bool>> visited(boundaries.y, std::vector<bool> (boundaries.x, false));
bool foodFound = false;
search.push(source);
while (!search.empty()) {
sf::Vector2f currentLocation = search.front();
search.pop();
if (g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited)
continue;
if (foodFound) { break; }
if (visited.at(currentLocation.y).at(currentLocation.x)) { continue; }
if (gameBoard.at(currentLocation.y).at(currentLocation.x) == 'X') {
foodFound = true;
}
botPathUnsanitized.push(currentLocation);
std::array<sf::Vector2f, 4> localLocations;
localLocations.fill(currentLocation);
@ -105,120 +79,71 @@ void AISnake::BFS(const sf::Vector2f& source) {
localLocations[1].x += 1;
localLocations[2].y -= 1;
localLocations[3].x -= 1;
for (sf::Vector2f nearby : localLocations) {
for (auto i : localLocations) {
try {
GameSpace* space = &g_pEngine->gameBoard.at(nearby.y).at(nearby.x);
if (space->m_bFood) {
botPathUnsanitized.push(nearby);
return;
if (gameBoard.at(i.y).at(i.x) == 'X') {
botPathUnsanitized.push(i);
foodFound = true;
}
if (nearby.x < 1 || nearby.y < 1)
continue;
if (space->m_bVisited)
continue;
if (space->m_bSnake)
continue;
search.push(nearby);
} catch (const std::out_of_range& error) {
continue; // Out of bounds
}
}
g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited = true;
for (sf::Vector2f newLocation : localLocations) {
try {
if ((!visited.at(newLocation.y).at(newLocation.x))
&& (gameBoard.at(newLocation.y).at(newLocation.x) == ' ')) {
search.push(newLocation);
}
} catch (const std::out_of_range& error) {
continue; // Out of bounds
}
}
visited.at(currentLocation.y).at(currentLocation.x) = true;
}
pathFailed = true;
}
void AISnake::DFS(const sf::Vector2f& source) {
void AISnake::DFS(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries) {
std::stack<sf::Vector2f> search;
std::vector<std::vector<bool>> visited(boundaries.y, std::vector<bool> (boundaries.x, false));
bool foodFound = false;
search.push(source);
while (!search.empty()) {
sf::Vector2f currentLocation = search.top();
search.pop();
if (g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited)
continue;
if (foodFound) { break; }
if (visited.at(currentLocation.y).at(currentLocation.x)) { continue; }
if (gameBoard.at(currentLocation.y).at(currentLocation.x) == 'X') {
foodFound = true;
}
botPathUnsanitized.push(currentLocation);
std::array<sf::Vector2f, 4> localLocations;
localLocations.fill(currentLocation);
localLocations.at(0).y += 1;
localLocations.at(1).x += 1;
localLocations.at(2).y -= 1;
localLocations.at(3).x -= 1;
for (sf::Vector2f nearby : localLocations) {
localLocations[0].y += 1;
localLocations[1].x += 1;
localLocations[2].y -= 1;
localLocations[3].x -= 1;
for (auto i : localLocations) {
try {
GameSpace* space = &g_pEngine->gameBoard.at(nearby.y).at(nearby.x);
if (space->m_bFood) {
botPathUnsanitized.push(nearby);
return;
if (gameBoard.at(i.y).at(i.x) == 'X') {
botPathUnsanitized.push(i);
foodFound = true;
}
if (nearby.x < 1 || nearby.x > g_pEngine->GetGameBoundaries().x - 2)
continue;
if (space->m_bVisited)
continue;
if (space->m_bSnake)
continue;
search.push(nearby);
} catch (const std::out_of_range& error) {
continue; // Out of bounds
}
}
g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited = true;
}
pathFailed = true;
}
sf::Vector2f AISnake::GetAnyOpenPath(const sf::Vector2f& source) {
sf::Vector2f bail;
std::array<sf::Vector2f, 4> paths;
paths.fill(source);
paths[0].x -= 1;
paths[1].x += 1;
paths[2].y -= 1;
paths[3].y += 1;
for (auto path : paths) {
for (sf::Vector2f newLocation : localLocations) {
try {
bail = path;
if (g_pEngine->gameBoard.at(path.y).at(path.x).m_bSnake)
continue;
return path;
if ((!visited.at(newLocation.y).at(newLocation.x))
&& (gameBoard.at(newLocation.y).at(newLocation.x) == ' ')) {
search.push(newLocation);
}
} catch (const std::out_of_range& error) {
continue; // Out of bounds
}
}
return bail; // Snake is trapped, give up and die
}
void AISnake::UnvisitBoard(void) {
for (std::vector<GameSpace>& i : g_pEngine->gameBoard)
for (GameSpace& j : i)
j.m_bVisited = false;
}
void AISnake::UpdateAverage(const int size) {
totalLength += size;
amountPlayed += 1;
average = (double)totalLength / amountPlayed;
}
void AISnake::TrimPath(void) {
bool reachedSnake = false;
path.push(botPathUnsanitized.top()); // Push food location
while (!botPathUnsanitized.empty()) {
if (!reachedSnake) {
sf::Vector2f location = botPathUnsanitized.top();
if (g_pEngine->gameBoard[location.y][location.x].m_bSnake)
reachedSnake = true;
sf::Vector2f deltaVector = location - path.top();
int delta = abs(deltaVector.x) + abs(deltaVector.y);
if (delta == 1)
path.push(location);
}
botPathUnsanitized.pop();
visited.at(currentLocation.y).at(currentLocation.x) = true;
}
}
void AISnake::EmptyPath(void) {
while (!botPathUnsanitized.empty())
botPathUnsanitized.pop();
}

View File

@ -3,32 +3,22 @@
#include "common.hpp"
#include <stack>
#include <vector>
#include <SFML/System/Vector2.hpp>
namespace snakeplusplus
{
class AISnake {
public:
std::stack<sf::Vector2f> path;
AISnake();
void GetNewPath(const sf::Vector2f& source);
void GetNewPath(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries, const int snakeSize);
PlayerDirection GetInput(const sf::Vector2f* source);
void UpdateProbability(int snakeSize);
void AdjustProbability(double amount);
void AddIteration(const int size);
void ResetPath(void);
int amountPlayed = 0;
private:
int totalLength = 0;
double average = 0;
double probabilityBFS = 0.800;
bool pathFailed = false;
std::stack<sf::Vector2f> botPathUnsanitized;
void BFS(const sf::Vector2f& source);
void DFS(const sf::Vector2f& source);
sf::Vector2f GetAnyOpenPath(const sf::Vector2f& source);
void UnvisitBoard(void);
void UpdateAverage(const int size);
void TrimPath(void);
void EmptyPath(void);
void BFS(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries);
void DFS(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries);
};
}
#endif

View File

@ -2,6 +2,8 @@
#include <random>
#include "common.hpp"
namespace snakeplusplus
{
std::default_random_engine generator;
void InitializeGenerator(void)
{
@ -13,16 +15,7 @@ int GenerateRandomNumber(int generationLimit)
{
int generatedNumber;
std::uniform_int_distribution<> distribution(0, generationLimit - 1);
generatedNumber = distribution(generator);
generatedNumber = distribution(snakeplusplus::generator);
return generatedNumber;
}
GameSpace::GameSpace(void) {
Reset();
}
void GameSpace::Reset(void) {
m_bFood = 0;
m_bSnake = 0;
m_bVisited = 0;
}

View File

@ -1,6 +1,8 @@
#ifndef COMMON_HPP
#define COMMON_HPP
namespace snakeplusplus
{
void InitializeGenerator(void);
int GenerateRandomNumber(int generationLimit);
@ -13,17 +15,6 @@ enum PlayerDirection
kRight = 4
};
struct GameSpace {
GameSpace();
unsigned char m_bFood : 1 = 0;
unsigned char m_bSnake : 1 = 0;
unsigned char m_bVisited : 1 = 0; // Used for BFS/DFS
unsigned char _3 : 1 = 0;
unsigned char _4 : 1 = 0;
unsigned char _5 : 1 = 0;
unsigned char _6 : 1 = 0;
unsigned char _7 : 1 = 0;
void Reset(void);
};
}
#endif

View File

@ -6,6 +6,8 @@
#include "playerinterface.hpp"
#include "gamestate.hpp"
namespace snakeplusplus
{
GameEngine::GameEngine()
{
InitializeGenerator();
@ -15,7 +17,6 @@ GameEngine::GameEngine()
void GameEngine::Start()
{
PrepareGameBoard();
if (!state.m_bNoDisplay)
graphics.StartGameWindow();
Loop();
return;
@ -23,111 +24,104 @@ void GameEngine::Start()
void GameEngine::Reset()
{
if (!state.m_bIsBotControlled)
graphics.CheckContinue();
else
bot.AddIteration(player.body.size());
graphics.CheckContinue(isBotControlled);
player.Reset();
PrepareGameBoard();
state.m_bIsGameOver = false;
if (state.m_bIsBotControlled) {
while (!bot.path.empty())
bot.path.pop();
if (state.m_bNoDisplay)
graphics.SetShowGame(false);
graphics.SetShowGame((bot.amountPlayed + 1) % 50 == 0);
if (isBotControlled) {
while (!bot.path.empty()) { bot.path.pop(); }
}
PrepareGameBoard();
isGameOver = false;
return;
}
void GameEngine::Loop(void)
{
int currentScore = 0;
while (graphics.IsOpen() || state.m_bNoDisplay)
while (graphics.IsOpen())
{
if (state.m_bIsGameOver) { Reset(); }
if (isGameOver) { Reset(); }
UpdatePlayerSpeed();
PlaceNewSnakePart(MovePlayer());
RegenerateFood();
currentScore = player.body.size() * 100;
if (!state.m_bNoDisplay)
graphics.DisplayGameState(gameBoard, currentScore);
graphics.DisplayGameState(gameBoard);
}
return;
}
sf::Vector2f GameEngine::MovePlayer(void)
{
return sf::Vector2f(player.headLocation.x + player.speed.x, player.headLocation.y + player.speed.y);
sf::Vector2f newHeadPosition;
newHeadPosition.x = player.headLocation.x + player.speed.x;
newHeadPosition.y = player.headLocation.y + player.speed.y;
return newHeadPosition;
}
sf::Vector2f GameEngine::GetGameBoundaries(void)
{
return graphics.gameBoundaries;
}
void GameEngine::PlaceNewSnakePart(sf::Vector2f location) {
void GameEngine::PlaceNewSnakePart(sf::Vector2f location)
{
if (!player.speed.x && !player.speed.y) { return; }
try {
GameSpace* locationState = &gameBoard.at(location.y).at(location.x);
if (locationState->m_bSnake && (player.body.size() > 1)) {
state.m_bIsGameOver = true; // Game should end (Snake touching snake)
}
locationState->m_bSnake = true;
try
{
char* locationState;
locationState = &gameBoard.at(location.y).at(location.x);
if (*locationState == 'O' && (player.body.size() > 1))
isGameOver = true; // Game should end (Snake touching snake)
*locationState = 'O';
player.body.push(locationState);
player.headLocation = location;
if (playerFood.location != location)
player.Pop();
else {
locationState->m_bFood = false;
if (state.m_bIsBotControlled)
bot.ResetPath();
}
} catch (const std::out_of_range& error) {
state.m_bIsGameOver = true; // Snake ran into edge
isGameOver = true; // Snake ran into edge
}
return;
}
// Generates new food until not colliding with player
void GameEngine::RegenerateFood()
void GameEngine::RegenerateFood(void)
{
// Generate a new food location if the current one is occupied
while (gameBoard.at(playerFood.location.y).at(playerFood.location.x).m_bSnake) {
sf::Vector2f newLocation = playerFood.location;
bool isUpdated = false;
while (gameBoard.at(newLocation.y).at(newLocation.x) == 'O')
{
isUpdated = true;
playerFood.GenerateNewFood(GetGameBoundaries());
newLocation = playerFood.location;
}
// Update the game board with the new food location
gameBoard.at(playerFood.location.y).at(playerFood.location.x).m_bFood = 1;
if (isUpdated) {
gameBoard.at(newLocation.y).at(newLocation.x) = 'X';
}
return;
}
void GameEngine::PrepareGameBoard(void)
{
gameBoard.clear();
sf::Vector2f boardDimensions = GetGameBoundaries();
gameBoard.resize(boardDimensions.y, std::vector<GameSpace>(boardDimensions.x));
gameBoard.resize(boardDimensions.y, std::vector<char> (boardDimensions.x, ' '));
// Snake setup
player.headLocation.x = GenerateRandomNumber(boardDimensions.x);
player.headLocation.y = GenerateRandomNumber(boardDimensions.y);
{
GameSpace* locationState = &gameBoard.at(player.headLocation.y).at(player.headLocation.x);
char* locationState = &gameBoard.at(player.headLocation.y).at(player.headLocation.x);
player.body.push(locationState);
locationState->m_bSnake = true;
*locationState = 'O';
}
// Food setup
playerFood.GenerateNewFood(boardDimensions);
gameBoard.at(playerFood.location.y).at(playerFood.location.x).m_bFood = true;
gameBoard.at(playerFood.location.y).at(playerFood.location.x) = 'X';
return;
}
void GameEngine::UpdatePlayerSpeed(void)
{
PlayerDirection controller;
if (state.m_bIsBotControlled) {
if (isBotControlled) {
if (bot.path.empty()) {
bot.GetNewPath(player.headLocation);
bot.GetNewPath(gameBoard, player.headLocation, GetGameBoundaries(), player.body.size());
}
controller = bot.GetInput(&player.headLocation);
}
@ -158,3 +152,4 @@ void GameEngine::UpdatePlayerSpeed(void)
}
return;
}
}

View File

@ -3,11 +3,12 @@
#define GAMESTATE_HPP
#include <SFML/Graphics.hpp>
#include <memory>
#include "botinterface.hpp"
#include "snake.hpp"
#include "playerinterface.hpp"
namespace snakeplusplus
{
const int kUnitSpeed = 1;
class GameEngine
@ -17,22 +18,15 @@ public:
void Start(void);
void Reset(void);
sf::Vector2f GetGameBoundaries(void);
struct GameState {
unsigned char m_bIsGameOver : 1 = 0;
unsigned char m_bIsBotControlled : 1 = 0;
unsigned char m_bNoDisplay : 1 = 0;
unsigned char _3 : 1 = 0;
unsigned char _4 : 1 = 0;
unsigned char _5 : 1 = 0;
unsigned char _6 : 1 = 0;
unsigned char _7 : 1 = 0;
} state;
std::vector< std::vector<GameSpace> > gameBoard;
private:
std::vector< std::vector<char> > gameBoard;
PlayerOutput graphics;
Snake player;
Food playerFood;
AISnake bot;
bool isGameOver = 0;
bool isBotControlled = 1;
void DisplayEndScreen(void);
void Loop(void);
sf::Vector2f MovePlayer(void);
void PlaceNewSnakePart(sf::Vector2f location);
@ -40,7 +34,6 @@ private:
void PrepareGameBoard(void);
void UpdatePlayerSpeed();
};
inline std::unique_ptr<GameEngine> g_pEngine;
}
#endif

View File

@ -1,42 +1,8 @@
#include "gamestate.hpp"
#include <memory>
#include <string>
#include <vector>
#include <iostream>
void Help(void) {
std::cout << "Usage: snakeplusplus [OPTIONS]" << std::endl;
std::cout << "Options:" << std::endl;
std::cout << "\t--server\tRun snake in server mode (also sets --bot)" << std::endl;
std::cout << "\t--auto\t\tControl snake using a bot or AI" << std::endl;
std::cout << "\t-h, --help\tPrint this help message and exit" << std::endl;
std::cout << std::endl;
std::cout << "Autoplay options (requires --auto):" << std::endl;
std::cout << "\t--dumb\t\tPlays using basic search algorithms BFS and DFS" << std::endl;
std::cout << "\t--smart\t\tTrains an algorithm using unsupervised learning" << std::endl;
std::cout << std::endl;
}
int main(int argc, char* argv[]) {
std::vector<std::string> args(argv, argv + argc);
g_pEngine = std::make_unique<GameEngine>();
for (int i = 1; i < args.size(); ++i) {
if (args[i].compare("--server") == 0) {
g_pEngine->state.m_bNoDisplay = true;
g_pEngine->state.m_bIsBotControlled = true;
std::cout << "[LOG - Main] Disabling display" << std::endl;
} else if (args[i].compare("--auto") == 0) {
g_pEngine->state.m_bIsBotControlled = true;
std::cout << "[LOG - Main] Bot control enabled" << std::endl;
} else if (args[i].compare("-h") == 0 || args[i].compare("--help") == 0) {
Help();
return 0;
} else {
std::cout << "[LOG - Main] Argument `" << args[i] << "` unrecognized, printing help and exiting..."<< std::endl;
Help();
return 1;
}
}
g_pEngine->Start();
int main(void)
{
snakeplusplus::GameEngine game;
game.Start();
return 0;
}

View File

@ -1,8 +1,9 @@
#include "playerinterface.hpp"
#include <SFML/System/Vector2.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <iostream>
namespace snakeplusplus
{
PlayerDirection GetPlayerInput(void)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)
@ -22,7 +23,7 @@ PlayerDirection GetPlayerInput(void)
bool PlayerOutput::IsOpen(void)
{
return isWindowAlive;
return gameWindow.isOpen();
}
PlayerOutput::PlayerOutput(void)
@ -37,8 +38,9 @@ PlayerOutput::PlayerOutput(void)
return;
}
void PlayerOutput::CheckContinue()
void PlayerOutput::CheckContinue(bool isBotControlled)
{
if (isBotControlled) { return; }
DisplayEndScreen();
while (true)
{
@ -47,7 +49,6 @@ void PlayerOutput::CheckContinue()
|| (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)))
{
gameWindow.close();
isWindowAlive = false;
return;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Enter)) { return; }
@ -70,37 +71,29 @@ void PlayerOutput::DisplayEndScreen(void)
return;
}
void PlayerOutput::DisplayScore(int score) {
sf::Vector2f textPosition(gameBoundaries);
textPosition.x = textPosition.x / 2;
textPosition.y = textPosition.y / 2;
sf::Font font;
font.loadFromFile("Arial.ttf");
std::string text = "Score: " + std::to_string(score);
sf::Text ScoreText(text, font);
ScoreText.setPosition(textPosition);
gameWindow.draw(ScoreText);
}
void PlayerOutput::DisplayGameState(std::vector< std::vector<GameSpace> >& gameBoard, int score)
void PlayerOutput::DisplayGameState(std::vector< std::vector<char> >& gameBoard)
{
CheckWindowEvents();
if (delay == sf::milliseconds(0)) { return; }
char* letterOnBoard;
for (float y = 0; y < gameBoundaries.y; y++)
{
for (float x = 0; x < gameBoundaries.x; x++)
{
if (gameBoard.at(y).at(x).m_bSnake)
letterOnBoard = &gameBoard.at(y).at(x);
switch (*letterOnBoard)
{
case 'O':
DrawSnake(sf::Vector2f(x, y));
else if (gameBoard.at(y).at(x).m_bFood)
break;
case 'X':
DrawFood(sf::Vector2f(x,y));
else
break;
default:
DrawEmpty(sf::Vector2f(x,y));
break;
}
}
}
DisplayScore(score);
gameWindow.display();
sf::sleep(delay);
return;
@ -113,29 +106,13 @@ void PlayerOutput::StartGameWindow(void)
return;
}
void PlayerOutput::SetShowGame(bool isShowing) {
if (isShowing) { delay = sf::milliseconds(5); }
else { delay = sf::milliseconds(0); }
return;
}
void PlayerOutput::CheckWindowEvents(void)
{
while (gameWindow.pollEvent(event))
{
if ((event.type == sf::Event::Closed)
|| (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))) {
|| (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)))
gameWindow.close();
isWindowAlive = false;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Equal)) {
if (delay > sf::milliseconds(16)) { continue; }
delay += sf::milliseconds(1);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Hyphen)) {
if (delay == sf::milliseconds(0)) { continue; }
delay -= sf::milliseconds(1);
}
}
}
@ -165,3 +142,4 @@ void PlayerOutput::DrawSnake(sf::Vector2f location)
gameWindow.draw(drawObject);
return;
}
}

View File

@ -6,6 +6,8 @@
const int kGridSize = 25;
namespace snakeplusplus
{
PlayerDirection GetPlayerInput(void);
class PlayerOutput
@ -14,11 +16,9 @@ public:
sf::Vector2f gameBoundaries;
PlayerOutput(void);
bool IsOpen(void);
void CheckContinue();
void DisplayGameState(std::vector< std::vector<GameSpace> >& gameBoard, int score);
void DisplayScore(int score);
void CheckContinue(bool isBotControlled);
void DisplayGameState(std::vector< std::vector<char> >& gameBoard);
void StartGameWindow(void);
void SetShowGame(bool isShowing);
private:
void CheckWindowEvents(void);
void DisplayEndScreen(void);
@ -29,8 +29,9 @@ private:
sf::VideoMode gameVideoSettings;
sf::RectangleShape drawObject;
sf::Event event;
bool isWindowAlive = false;
sf::Time delay = sf::milliseconds(12);
bool isWindowAlive;
sf::Time delay = sf::milliseconds(15);
};
}
#endif

View File

@ -4,9 +4,11 @@
#include "common.hpp"
#include "snake.hpp"
namespace snakeplusplus
{
void Snake::Pop(void)
{
body.front()->m_bSnake = false;
*(body.front()) = ' ';
body.pop();
return;
}
@ -26,3 +28,4 @@ void Food::GenerateNewFood(sf::Vector2f boundaries)
location.y = GenerateRandomNumber(boundaries.y);
return;
}
}

View File

@ -4,14 +4,15 @@
#include <SFML/System/Vector2.hpp>
#include <queue>
#include "common.hpp"
namespace snakeplusplus
{
struct Snake
{
public:
sf::Vector2f headLocation;
sf::Vector2f speed;
std::queue<GameSpace*> body;
std::queue<char*> body;
void Pop(void);
void Reset(void);
};
@ -20,7 +21,9 @@ struct Food
{
public:
sf::Vector2f location;
char* food;
void GenerateNewFood(sf::Vector2f boundaries);
};
}
#endif