Compare commits

..

14 Commits

Author SHA1 Message Date
eefc2bf60b Merge pull request 'add misc features to game' (#5) from iterative into master
Reviewed-on: Trianta/snakeplusplus#5
2024-08-02 19:36:14 -05:00
Trianta
03e2bf2748 git: update gitignore for cmake stuff 2024-08-02 19:33:45 -05:00
Trianta
22da054dab controls: added adjusting game speed with - and + 2024-05-20 01:20:05 -05:00
Trianta
b18ce89970 bot: added ability to skip display delay between specific generations 2024-05-20 01:05:59 -05:00
05f78d67fd Add iterative approach to deciding DFS and BFS 2024-02-01 12:50:10 -06:00
30ffcc4bb3 Merge pull request 'Minor code style fix' (#4) from dev into master
Reviewed-on: Trianta/snakeplusplus#4
2024-02-01 11:25:33 -06:00
a6c60ac3c4 Merge branch 'dev' 2024-02-01 11:25:43 -06:00
1498110048 Merge branch 'master' into dev 2024-02-01 11:23:52 -06:00
Trianta
e2d0c5dac7 Mini style adjustment 2023-11-05 19:04:44 -06:00
4f7cacfc44 Merge pull request 'Added scoring and chance-based algorithm picking' (#3) from dev into master
Reviewed-on: Trianta/snakeplusplus#3
2023-10-24 00:55:30 -05:00
Trianta
5dce9b677d Added scoring and chance-based algorithm picking 2023-10-24 00:54:30 -05:00
Trianta
da6ceebb55 Added displaying score 2023-10-23 21:33:47 -05:00
014aaffc0a Merge pull request 'Update dev from master' (#2) from master into dev
Reviewed-on: Trianta/snakeplusplus#2
2023-10-18 09:42:26 -05:00
9bb4b8ebca Increased survivability with DFS when no path 2023-10-16 12:35:35 -05:00
7 changed files with 131 additions and 35 deletions
Vendored
+14 -2
View File
@@ -33,7 +33,19 @@
*.json *.json
*.ps1 *.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 # Extras
.vs* .vs*
build
bin
+32 -3
View File
@@ -2,6 +2,7 @@
#include "common.hpp" #include "common.hpp"
#include <array> #include <array>
#include <cstdlib> #include <cstdlib>
#include <iostream>
#include <queue> #include <queue>
#include <stdexcept> #include <stdexcept>
#include <SFML/System/Vector2.hpp> #include <SFML/System/Vector2.hpp>
@@ -36,16 +37,38 @@ namespace snakeplusplus
return lastKnownDirection; 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; }
std::cout << "[Info - AISnake] New BFS probability: " << probabilityBFS << std::endl;
return;
}
// Gets a new path for the bot to follow // Gets a new path for the bot to follow
// Uses DFS algorithm // Uses DFS algorithm
void AISnake::GetNewPath(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries, const int snakeSize) void AISnake::GetNewPath(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries, const int snakeSize)
{ {
// Search for food // Search for food
if (snakeSize < 135) { /*
BFS(gameBoard, source, boundaries); BFS(gameBoard, source, boundaries);
} else { if (gameBoard[botPathUnsanitized.top().y][botPathUnsanitized.top().x] != 'X') {
while (!botPathUnsanitized.empty()) { botPathUnsanitized.pop(); }
DFS(gameBoard, source, boundaries); DFS(gameBoard, source, boundaries);
while (botPathUnsanitized.size() > 15) { botPathUnsanitized.pop(); }
} }
*/
// Probability-based approach for fun
double roll = ((double) GenerateRandomNumber(RAND_MAX)) / ((double) RAND_MAX);
if (roll <= probabilityBFS) { BFS(gameBoard, source, boundaries); }
else { DFS(gameBoard, source, boundaries); }
// Create path for food // Create path for food
path.push(botPathUnsanitized.top()); path.push(botPathUnsanitized.top());
botPathUnsanitized.pop(); botPathUnsanitized.pop();
@@ -135,6 +158,12 @@ namespace snakeplusplus
} }
for (sf::Vector2f newLocation : localLocations) { for (sf::Vector2f newLocation : localLocations) {
try { try {
if (newLocation.x < 1 || newLocation.y < 1
|| newLocation.x > boundaries.x - 2
|| newLocation.y > boundaries.y - 2) {
continue;
}
if ((!visited.at(newLocation.y).at(newLocation.x)) if ((!visited.at(newLocation.y).at(newLocation.x))
&& (gameBoard.at(newLocation.y).at(newLocation.x) == ' ')) { && (gameBoard.at(newLocation.y).at(newLocation.x) == ' ')) {
search.push(newLocation); search.push(newLocation);
+3
View File
@@ -14,7 +14,10 @@ namespace snakeplusplus
AISnake(); AISnake();
void GetNewPath(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries, const int snakeSize); 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); PlayerDirection GetInput(const sf::Vector2f* source);
void UpdateProbability(int snakeSize);
void AdjustProbability(double amount);
private: private:
double probabilityBFS = 0.500;
std::stack<sf::Vector2f> botPathUnsanitized; std::stack<sf::Vector2f> botPathUnsanitized;
void BFS(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries); 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); void DFS(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries);
+43 -27
View File
@@ -1,4 +1,5 @@
// GameState.cpp // GameState.cpp
#include <iostream>
#include <stdexcept> #include <stdexcept>
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
#include "botinterface.hpp" #include "botinterface.hpp"
@@ -24,51 +25,64 @@ namespace snakeplusplus
void GameEngine::Reset() void GameEngine::Reset()
{ {
graphics.CheckContinue(isBotControlled); AddIteration();
player.Reset(); player.Reset();
if (isBotControlled) { if (isBotControlled) { while (!bot.path.empty()) { bot.path.pop(); } }
while (!bot.path.empty()) { bot.path.pop(); }
}
PrepareGameBoard(); PrepareGameBoard();
isGameOver = false; isGameOver = false;
graphics.SetShowGame((amountPlayed + 1) % 50 == 0);
return; return;
} }
void GameEngine::AddIteration(void)
{
graphics.CheckContinue(isBotControlled);
if (player.body.size() > 40)
{
UpdateAverage();
double adjustmentAmount = 0.002;
if (average > player.body.size()) { bot.AdjustProbability(adjustmentAmount); }
else { bot.AdjustProbability(-adjustmentAmount); }
}
std::cout << "[Info - GameEngine] Current average: " << average << std::endl;
std::cout << "[Info - GameEngine] Previous iteration size: " << player.body.size() << std::endl;
}
void GameEngine::Loop(void) void GameEngine::Loop(void)
{ {
int currentScore = 0;
while (graphics.IsOpen()) while (graphics.IsOpen())
{ {
if (isGameOver) { Reset(); } if (isGameOver) { Reset(); }
UpdatePlayerSpeed(); UpdatePlayerSpeed();
PlaceNewSnakePart(MovePlayer()); PlaceNewSnakePart(MovePlayer());
RegenerateFood(); RegenerateFood();
graphics.DisplayGameState(gameBoard); currentScore = player.body.size() * 100;
//bot.UpdateProbability(player.body.size());
graphics.DisplayGameState(gameBoard, currentScore);
} }
return; return;
} }
sf::Vector2f GameEngine::MovePlayer(void) sf::Vector2f GameEngine::MovePlayer(void)
{ {
sf::Vector2f newHeadPosition; return sf::Vector2f(player.headLocation.x + player.speed.x, player.headLocation.y + player.speed.y);
newHeadPosition.x = player.headLocation.x + player.speed.x;
newHeadPosition.y = player.headLocation.y + player.speed.y;
return newHeadPosition;
} }
sf::Vector2f GameEngine::GetGameBoundaries(void) sf::Vector2f GameEngine::GetGameBoundaries(void)
{ {
return graphics.gameBoundaries; return graphics.gameBoundaries;
} }
void GameEngine::PlaceNewSnakePart(sf::Vector2f location) void GameEngine::PlaceNewSnakePart(sf::Vector2f location) {
{
if (!player.speed.x && !player.speed.y) { return; } if (!player.speed.x && !player.speed.y) { return; }
try try {
{ char* locationState = &gameBoard.at(location.y).at(location.x);
char* locationState; if (*locationState == 'O' && (player.body.size() > 1)) {
locationState = &gameBoard.at(location.y).at(location.x);
if (*locationState == 'O' && (player.body.size() > 1))
isGameOver = true; // Game should end (Snake touching snake) isGameOver = true; // Game should end (Snake touching snake)
}
*locationState = 'O'; *locationState = 'O';
player.body.push(locationState); player.body.push(locationState);
player.headLocation = location; player.headLocation = location;
@@ -80,23 +94,20 @@ namespace snakeplusplus
return; return;
} }
// Generates new food until not colliding with player // Generates new food until not colliding with player
void GameEngine::RegenerateFood(void) void GameEngine::RegenerateFood()
{ {
sf::Vector2f newLocation = playerFood.location; // Generate a new food location if the current one is occupied
bool isUpdated = false; while (gameBoard.at(playerFood.location.y).at(playerFood.location.x) == 'O') {
while (gameBoard.at(newLocation.y).at(newLocation.x) == 'O')
{
isUpdated = true;
playerFood.GenerateNewFood(GetGameBoundaries()); playerFood.GenerateNewFood(GetGameBoundaries());
newLocation = playerFood.location;
} }
if (isUpdated) {
gameBoard.at(newLocation.y).at(newLocation.x) = 'X'; // Update the game board with the new food location
} gameBoard.at(playerFood.location.y).at(playerFood.location.x) = 'X';
return;
} }
void GameEngine::PrepareGameBoard(void) void GameEngine::PrepareGameBoard(void)
{ {
gameBoard.clear(); gameBoard.clear();
@@ -152,4 +163,9 @@ namespace snakeplusplus
} }
return; return;
} }
void GameEngine::UpdateAverage() {
totalLength += player.body.size();
amountPlayed += 1;
average = (double)totalLength / amountPlayed;
}
} }
+5
View File
@@ -17,6 +17,7 @@ namespace snakeplusplus
GameEngine(); GameEngine();
void Start(void); void Start(void);
void Reset(void); void Reset(void);
void AddIteration(void);
sf::Vector2f GetGameBoundaries(void); sf::Vector2f GetGameBoundaries(void);
private: private:
std::vector< std::vector<char> > gameBoard; std::vector< std::vector<char> > gameBoard;
@@ -33,6 +34,10 @@ namespace snakeplusplus
void RegenerateFood(void); void RegenerateFood(void);
void PrepareGameBoard(void); void PrepareGameBoard(void);
void UpdatePlayerSpeed(); void UpdatePlayerSpeed();
void UpdateAverage();
int totalLength = 0;
int amountPlayed = 0;
double average = 0;
}; };
} }
+30 -1
View File
@@ -71,9 +71,23 @@ namespace snakeplusplus
return; return;
} }
void PlayerOutput::DisplayGameState(std::vector< std::vector<char> >& gameBoard) 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<char> >& gameBoard, int score)
{ {
CheckWindowEvents(); CheckWindowEvents();
if (delay == sf::milliseconds(0)) { return; }
char* letterOnBoard; char* letterOnBoard;
for (float y = 0; y < gameBoundaries.y; y++) for (float y = 0; y < gameBoundaries.y; y++)
{ {
@@ -94,6 +108,7 @@ namespace snakeplusplus
} }
} }
} }
DisplayScore(score);
gameWindow.display(); gameWindow.display();
sf::sleep(delay); sf::sleep(delay);
return; return;
@@ -106,6 +121,12 @@ namespace snakeplusplus
return; return;
} }
void PlayerOutput::SetShowGame(bool isShowing) {
if (isShowing) { delay = sf::milliseconds(2); }
else { delay = sf::milliseconds(0); }
return;
}
void PlayerOutput::CheckWindowEvents(void) void PlayerOutput::CheckWindowEvents(void)
{ {
while (gameWindow.pollEvent(event)) while (gameWindow.pollEvent(event))
@@ -113,6 +134,14 @@ namespace snakeplusplus
if ((event.type == sf::Event::Closed) if ((event.type == sf::Event::Closed)
|| (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))) || (sf::Keyboard::isKeyPressed(sf::Keyboard::Escape)))
gameWindow.close(); gameWindow.close();
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);
}
} }
} }
+4 -2
View File
@@ -17,8 +17,10 @@ namespace snakeplusplus
PlayerOutput(void); PlayerOutput(void);
bool IsOpen(void); bool IsOpen(void);
void CheckContinue(bool isBotControlled); void CheckContinue(bool isBotControlled);
void DisplayGameState(std::vector< std::vector<char> >& gameBoard); void DisplayGameState(std::vector< std::vector<char> >& gameBoard, int score);
void DisplayScore(int score);
void StartGameWindow(void); void StartGameWindow(void);
void SetShowGame(bool isShowing);
private: private:
void CheckWindowEvents(void); void CheckWindowEvents(void);
void DisplayEndScreen(void); void DisplayEndScreen(void);
@@ -30,7 +32,7 @@ namespace snakeplusplus
sf::RectangleShape drawObject; sf::RectangleShape drawObject;
sf::Event event; sf::Event event;
bool isWindowAlive; bool isWindowAlive;
sf::Time delay = sf::milliseconds(15); sf::Time delay = sf::milliseconds(1);
}; };
} }