diff --git a/Makefile b/Makefile index cff45ec..8eda9c0 100755 --- a/Makefile +++ b/Makefile @@ -8,9 +8,7 @@ fresh: dirs compile link compile: g++ $(INC) $(STD) -c -o build/main.o src/main.cpp - g++ $(INC) $(STD) -c -o build/common.o src/common.cpp g++ $(INC) $(STD) -c -o build/display.o src/display.cpp - g++ $(INC) $(STD) -c -o build/game.o src/game.cpp g++ $(INC) $(STD) -c -o build/gamestate.o src/gamestate.cpp g++ $(INC) $(STD) -c -o build/snake.o src/snake.cpp g++ $(INC) $(STD) -c -o build/snakefood.o src/snakefood.cpp @@ -19,7 +17,7 @@ dirs: mkdir bin build link: - g++ build/*.o -o bin/SnakePlusPlus $(SFML) + g++ build/*.o -o bin/SnakePlusPlus.out $(SFML) clean: - rm bin/*.o build/*.out \ No newline at end of file + rm bin/*.out build/*.o diff --git a/include/common.h b/include/common.h index 62b9dbb..0ac55ec 100755 --- a/include/common.h +++ b/include/common.h @@ -1,8 +1,6 @@ #ifndef COMMON_H #define COMMON_H -#include - enum PlayerDirection { kLeft = 1, @@ -11,6 +9,4 @@ enum PlayerDirection kRight = 4 }; -bool GlobalCollision(sf::Vector2f object1Position, sf::Vector2f object2Position); - #endif diff --git a/include/display.h b/include/display.h index c90ff04..845af8e 100755 --- a/include/display.h +++ b/include/display.h @@ -3,6 +3,8 @@ #include +const int kGridSize = 25; + class DisplayInterface { public: @@ -10,10 +12,10 @@ public: DisplayInterface(void); bool IsOpen(void); protected: - bool isGameStillRunning; - virtual void DisplayGameState(void) = 0; + bool isWindowAlive; + virtual void DisplayGameState(std::vector< std::vector >* gameBoard) = 0; virtual void DisplayEndScreen(void) = 0; - virtual void StartGame(void) = 0; + virtual void StartGameWindow(void) = 0; private: ; }; @@ -22,29 +24,34 @@ class CommandLine : public DisplayInterface { public: CommandLine(void); - void DisplayGameState(void); + void DisplayGameState(std::vector< std::vector >* gameBoard); void DisplayEndScreen(void); - void StartGame(void); + void StartGameWindow(void); protected: ; private: - const int kGridSize = 25; + ; }; class SFML : public DisplayInterface { public: SFML(void); - void DisplayGameState(void); + void DisplayGameState(std::vector< std::vector >* gameBoard); void DisplayEndScreen(void); - void StartGame(void); + void StartGameWindow(void); void UpdateResolution(sf::Vector2i newResolution); protected: ; private: + void CheckWindowEvents(void); + void DrawEmpty(sf::Vector2f location); + void DrawFood(sf::Vector2f location); + void DrawSnake(sf::Vector2f location); sf::Time delay; sf::RenderWindow gameWindow; sf::VideoMode gameVideoSettings; + sf::RectangleShape drawObject; }; #endif diff --git a/include/game.h b/include/game.h deleted file mode 100755 index fd17d79..0000000 --- a/include/game.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef GAME_H -#define GAME_H - -class Game -{ -public: - ; -protected: - ; -private: - ; -}; - -#endif \ No newline at end of file diff --git a/include/gamestate.h b/include/gamestate.h index 7551bda..adff1f2 100755 --- a/include/gamestate.h +++ b/include/gamestate.h @@ -5,22 +5,25 @@ #include #include #include "snake.h" +#include "snakefood.h" #include "display.h" class GameState { public: - std::vector< std::vector > gameBoard; - bool useSFML = 1; GameState(); + void SetGameSettings(int argc, char* argv[]); void StartGame(void); sf::Vector2f GetGameBoundaries(void); protected: ; private: + std::vector< std::vector > gameBoard; std::unique_ptr graphics; - SnakeFood playerFood; Snake player; + SnakeFood playerFood; + bool useSFML = 1; + void ApplySettings(void); void DisplayEndScreen(void); void GetKeyboardInput(void); bool PlayerWantsToContinue(void); diff --git a/include/snake.h b/include/snake.h index b42a913..85941b8 100755 --- a/include/snake.h +++ b/include/snake.h @@ -2,33 +2,24 @@ #ifndef SNAKE_H #define SNAKE_H -#include +#include #include -#include "snakefood.h" class Snake { public: - bool gameFinished = false; Snake(void); - void DisplaySnake(sf::RenderWindow& window); - sf::RectangleShape GetSnakeHead(void); - sf::Vector2f GetSnakeHeadPosition(void); - bool IsTouchingObject(sf::RectangleShape object); - void MoveSnake(SnakeFood* playerFood); + sf::Vector2f MoveSnake(void); + sf::Vector2f Pop(void); void UpdateDirection(int newDirection); protected: ; private: - std::deque snakeBody; - sf::Vector2f bodyPartSize; + std::queue snakeBody; int snakeDirection = 0; - void AddBodyPart(sf::RectangleShape newBodyPart); - sf::Vector2f CalculateNewPosition(sf::Vector2f position); - bool CheckBoundaries(void); - void CreateHead(void); - bool IsSelfCollision(sf::RectangleShape testRectangle); + sf::Vector2f CalculateNewHead(); + void CreateNewHead(sf::Vector2f); }; diff --git a/include/snakefood.h b/include/snakefood.h index adf7dc3..9e3d4f3 100755 --- a/include/snakefood.h +++ b/include/snakefood.h @@ -9,14 +9,11 @@ class SnakeFood { public: SnakeFood(); - SnakeFood(sf::Vector2f snakeFoodSize); - void GenerateNewFood(sf::Vector2f windowSize); - sf::RectangleShape GetFoodObject(void); - sf::Vector2f GetFoodObjectPosition(void); + sf::Vector2f GenerateNewFood(sf::Vector2f boundaries); protected: ; private: - sf::RectangleShape snakeFoodObject; + sf::Vector2f location; std::default_random_engine generator; int GenerateRandomNumber(int generationLimit); }; diff --git a/src/common.cpp b/src/common.cpp deleted file mode 100755 index f163cbc..0000000 --- a/src/common.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "common.h" - -// Test for collision between two object positions -bool GlobalCollision(sf::Vector2f object1Position, sf::Vector2f object2Position) -{ - if (object1Position.x != object2Position.x) - return false; - if (object1Position.y != object2Position.y) - return false; - return true; -} \ No newline at end of file diff --git a/src/display.cpp b/src/display.cpp index d613eb7..25da728 100755 --- a/src/display.cpp +++ b/src/display.cpp @@ -1,5 +1,5 @@ -#include "common.h" #include "display.h" +#include //#include DisplayInterface::DisplayInterface(void) @@ -9,7 +9,7 @@ DisplayInterface::DisplayInterface(void) bool DisplayInterface::IsOpen(void) { - return isGameStillRunning; + return isWindowAlive; } CommandLine::CommandLine(void) @@ -19,28 +19,34 @@ CommandLine::CommandLine(void) return; } -// TODO: Use cout for printing game to screen -void CommandLine::DisplayGameState(void) +void CommandLine::DisplayEndScreen(void) { - ; -} - -void CommandLine::StartGame(void) -{ - isGameStillRunning = true; + std::cout << "Game Over!" << std::endl; + return; +} + +// TODO: Use cout for printing game to screen +void CommandLine::DisplayGameState(std::vector< std::vector >* gameBoard) +{ + for (int i = 0; i < gameBoundaries.y; i++) + { + for (int j = 0; j < gameBoundaries.x; j++) + std::cout << gameBoard->at(i).at(j); + std::cout << std::endl; + } +} + +void CommandLine::StartGameWindow(void) +{ + isWindowAlive = true; return; } -// TODO: Setup making window SFML::SFML(void) { delay = sf::milliseconds(75); gameVideoSettings = sf::VideoMode(1025, 725); - return; -} - -void SFML::DisplayGameState(void) -{ + drawObject.setSize(sf::Vector2f(kGridSize, kGridSize)); return; } @@ -64,10 +70,32 @@ void SFML::DisplayEndScreen(void) return; } -void SFML::StartGame(void) +void SFML::DisplayGameState(std::vector< std::vector >* gameBoard) +{ + CheckWindowEvents(); + sf::Vector2i location(0,0); + char letterOnBoard; + for (; location.y < gameBoundaries.y; location.y++) + { + for (; location.x < gameBoundaries.x; location.x++) + { + letterOnBoard = gameBoard->at(location.y).at(location.y); + if (letterOnBoard == 'o') + DrawSnake(static_cast(location)); + else if (letterOnBoard == 'x') + DrawFood(static_cast(location)); + else + DrawEmpty(static_cast(location)); + } + } + gameWindow.display(); + return; +} + +void SFML::StartGameWindow(void) { gameWindow.create(gameVideoSettings, "SnakePlusPlus"); - isGameStillRunning = true; + isWindowAlive = true; return; } @@ -80,3 +108,41 @@ void SFML::UpdateResolution(sf::Vector2i newResolution) gameWindow.create(gameVideoSettings, "SnakePlusPlus"); return; } + +void SFML::CheckWindowEvents(void) +{ + sf::Event event; + while (gameWindow.pollEvent(event)) + { + if ((event.type == sf::Event::Closed) || + (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))) + gameWindow.close(); + } +} + +void SFML::DrawEmpty(sf::Vector2f location) +{ + location *= static_cast(kGridSize); + drawObject.setPosition(location); + drawObject.setFillColor(sf::Color::Black); + gameWindow.draw(drawObject); + return; +} + +void SFML::DrawFood(sf::Vector2f location) +{ + location *= static_cast(kGridSize); + drawObject.setPosition(location); + drawObject.setFillColor(sf::Color::Red); + gameWindow.draw(drawObject); + return; +} + +void SFML::DrawSnake(sf::Vector2f location) +{ + location *= static_cast(kGridSize); + drawObject.setPosition(location); + drawObject.setFillColor(sf::Color::Green); + gameWindow.draw(drawObject); + return; +} diff --git a/src/game.cpp b/src/game.cpp deleted file mode 100755 index 7bb61ba..0000000 --- a/src/game.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "game.h" diff --git a/src/gamestate.cpp b/src/gamestate.cpp index e818d6f..30d50b6 100755 --- a/src/gamestate.cpp +++ b/src/gamestate.cpp @@ -1,4 +1,5 @@ // GameState.cpp +#include #include #include "common.h" #include "display.h" @@ -6,20 +7,36 @@ GameState::GameState() { - if (useSFML) - graphics.reset(new SFML()); - else - graphics.reset(new CommandLine()); return; } +void GameState::SetGameSettings(int argc, char* argv[]) +{ + std::string convertedString; + for (int i = 0; i < argc; i++) + { + convertedString = argv[i]; + if (convertedString == "--no-sfml") + useSFML = false; + } +} + void GameState::StartGame() { + ApplySettings(); ResetGameBoard(); RunGameLoop(); return; } +void GameState::ApplySettings(void) +{ + if (useSFML) + graphics.reset(new SFML()); + else + graphics.reset(new CommandLine()); +} + // TODO: Reimplement for DisplayInterface void GameState::DisplayEndScreen(void) { @@ -32,24 +49,16 @@ sf::Vector2f GameState::GetGameBoundaries(void) return graphics->gameBoundaries; } -// TODO: Reimplement for DisplayInterface void GameState::GetKeyboardInput(void) { - // sf::Event event; - // while (gameWindow.pollEvent(event)) - // { - // if ((event.type == SFML:Event::Closed) || - // (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))) - // gameWindow.close(); - // } - // if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) - // player.UpdateDirection(kLeft); - // if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) - // player.UpdateDirection(kUp); - // if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) - // player.UpdateDirection(kDown); - // if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) - // player.UpdateDirection(kRight); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) + player.UpdateDirection(kLeft); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) + player.UpdateDirection(kUp); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) + player.UpdateDirection(kDown); + if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) + player.UpdateDirection(kRight); return; } @@ -75,9 +84,12 @@ bool GameState::PlayerWantsToContinue(void) // Generates new food until not colliding with player void GameState::RegenerateFood(void) { + sf::Vector2f newLocation; + playerFood.GenerateNewFood(GetGameBoundaries()); // Keep making new food until generating a valid spot - while (player.IsTouchingObject(playerFood.GetFoodObject())) + while (gameBoard.at(newLocation.y).at(newLocation.x) == 'o') playerFood.GenerateNewFood(GetGameBoundaries()); + gameBoard.at(newLocation.y).at(newLocation.x) = 'x'; return; } @@ -97,7 +109,7 @@ void GameState::RunGameLoop(void) while (graphics->IsOpen()) { GetKeyboardInput(); - player.MoveSnake(&playerFood); + player.MoveSnake(); RegenerateFood(); RenderWindow(); } diff --git a/src/main.cpp b/src/main.cpp index 354ebd1..d1b266b 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,8 @@ #include "gamestate.h" -int main() +int main(int argc, char *argv[]) { GameState game; + game.SetGameSettings(argc, argv); game.StartGame(); } diff --git a/src/snake.cpp b/src/snake.cpp index a65e343..c10bfa1 100755 --- a/src/snake.cpp +++ b/src/snake.cpp @@ -1,82 +1,32 @@ // Snake.cpp -#include #include #include #include "common.h" #include "snake.h" -#include "snakefood.h" // General constructor for snake class Snake::Snake(void) { - bodyPartSize = sf::Vector2f(kGridSize, kGridSize); - CreateHead(); + CreateNewHead(sf::Vector2f(4,5)); return; } -// Iterate through snake deque and draw to window -void Snake::DisplaySnake(sf::RenderWindow& window) -{ - for (auto snakeBodyPart = snakeBody.cbegin(); snakeBodyPart != snakeBody.cend(); ++snakeBodyPart) - window.draw(*snakeBodyPart); - return; -} - -// Return the RectangleShape head of snake -sf::RectangleShape Snake::GetSnakeHead(void) -{ - sf::RectangleShape head; - head = snakeBody.front(); - return head; -} - -// Return the Vector2f head of snake -sf::Vector2f Snake::GetSnakeHeadPosition(void) -{ - sf::Vector2f position; - position = snakeBody.front().getPosition(); - return position; -} - -// Checks if snake position matches object position -bool Snake::IsTouchingObject(sf::RectangleShape object) -{ - for (auto snakeBodyPart = snakeBody.cbegin(); snakeBodyPart != snakeBody.cend(); ++snakeBodyPart) - { - if ((*snakeBodyPart).getPosition().x != object.getPosition().x) - continue; - if ((*snakeBodyPart).getPosition().y != object.getPosition().y) - continue; - return true; - } - return false; -} - // Move snake based on direction and check for collision -void Snake::MoveSnake(SnakeFood* snakeFood) +sf::Vector2f Snake::MoveSnake(void) { - // TODO: Add losing on wall collision - if (CheckBoundaries()) // Wall collision - { - gameFinished = true; - return; - } sf::Vector2f newHeadPosition; - newHeadPosition = CalculateNewPosition(GetSnakeHeadPosition()); - sf::RectangleShape newBodyPart(sf::Vector2f(kGridSize, kGridSize)); - newBodyPart.setPosition(newHeadPosition); - // TODO: Add losing on self collision - if (IsSelfCollision(newBodyPart) && (snakeBody.size() > 1)) // Snake collision - { - gameFinished = true; - return; - } - if (IsSelfCollision(newBodyPart)) - return; - AddBodyPart(newBodyPart); - if (!GlobalCollision(GetSnakeHeadPosition(), snakeFood->GetFoodObjectPosition())) - snakeBody.pop_back(); - return; + newHeadPosition = CalculateNewHead(); + CreateNewHead(newHeadPosition); + return newHeadPosition; +} + +// Removes tail of snake +// Returns the location of the tail +sf::Vector2f Snake::Pop(void) +{ + sf::Vector2f tailLocation = snakeBody.front(); + snakeBody.pop(); + return tailLocation; } void Snake::UpdateDirection(int newDirection) @@ -85,55 +35,22 @@ void Snake::UpdateDirection(int newDirection) return; } -void Snake::AddBodyPart(sf::RectangleShape newBodyPart) -{ - newBodyPart.setFillColor(sf::Color::Green); - snakeBody.push_front(newBodyPart); -} - // Get a new coordinate position based on snake direction -sf::Vector2f Snake::CalculateNewPosition(sf::Vector2f position) +sf::Vector2f Snake::CalculateNewHead(void) { - if (snakeDirection == 0) - return position; + sf::Vector2f position = snakeBody.back(); if (snakeDirection == kLeft) - position.x -= kGridSize; + position.x -= 1; if (snakeDirection == kUp) - position.y -= kGridSize; + position.y -= 1; if (snakeDirection == kDown) - position.y += kGridSize; + position.y += 1; if (snakeDirection == kRight) - position.x += kGridSize; - return position; + position.x += 1; } -// Check snake head for running into boundaries -bool Snake::CheckBoundaries(void) +void Snake::CreateNewHead(sf::Vector2f headLocation) { - if (snakeBody.front().getPosition().x == 0 && snakeDirection == kLeft) - return true; - if (snakeBody.front().getPosition().y == 0 && snakeDirection == kUp) - return true; - // TODO: Change boundaries to not be hard-coded - if (snakeBody.front().getPosition().y > 675 && snakeDirection == kDown) - return true; - if (snakeBody.front().getPosition().x > 975 && snakeDirection == kRight) - return true; - return false; -} - -void Snake::CreateHead(void) -{ - sf::RectangleShape newBodyPart(bodyPartSize); - newBodyPart.setFillColor(sf::Color::Green); - snakeBody.push_front(newBodyPart); -} - -// Test for snake self collision -bool Snake::IsSelfCollision(sf::RectangleShape testRectangle) -{ - for (auto snakeBodyPart = snakeBody.cbegin(); snakeBodyPart != snakeBody.cend(); ++snakeBodyPart) - if (GlobalCollision(testRectangle.getPosition(), (*snakeBodyPart).getPosition())) - return true; - return false; + snakeBody.push(headLocation); + return; } diff --git a/src/snakefood.cpp b/src/snakefood.cpp index 55ecbbf..b21be6b 100755 --- a/src/snakefood.cpp +++ b/src/snakefood.cpp @@ -1,42 +1,18 @@ // SnakeFood.cpp -#include -#include -#include "common.h" #include "snakefood.h" SnakeFood::SnakeFood() { - snakeFoodObject.setSize(sf::Vector2f(kGridSize, kGridSize)); - snakeFoodObject.setFillColor(sf::Color::Red); - return; -} - -SnakeFood::SnakeFood(sf::Vector2f snakeFoodSize) -{ - snakeFoodObject.setSize(snakeFoodSize); - snakeFoodObject.setFillColor(sf::Color::Red); return; } // Returns a new food object for the snakeFood -void SnakeFood::GenerateNewFood(sf::Vector2f windowSize) +sf::Vector2f SnakeFood::GenerateNewFood(sf::Vector2f boundaries) { - sf::Vector2f newPosition; - newPosition.x = GenerateRandomNumber(windowSize.x); - newPosition.y = GenerateRandomNumber(windowSize.y); - snakeFoodObject.setPosition(newPosition); - return; -} - -sf::RectangleShape SnakeFood::GetFoodObject(void) -{ - return snakeFoodObject; -} - -sf::Vector2f SnakeFood::GetFoodObjectPosition(void) -{ - return snakeFoodObject.getPosition(); + location.x = GenerateRandomNumber(boundaries.x); + location.y = GenerateRandomNumber(boundaries.y); + return location; } // Returns a newly generated number @@ -45,6 +21,5 @@ int SnakeFood::GenerateRandomNumber(int generationLimit) int generatedNumber; std::uniform_int_distribution<> distribution(0, generationLimit); generatedNumber = distribution(generator); - generatedNumber -= (generatedNumber % kGridSize); return generatedNumber; }