refactor: merge final cleanups before working on AI #6

Merged
Trianta merged 12 commits from refactor into master 2024-08-10 15:05:56 -05:00
3 changed files with 76 additions and 45 deletions
Showing only changes of commit ff70b0dbd4 - Show all commits

View File

@ -16,7 +16,6 @@ AISnake::AISnake() {
PlayerDirection AISnake::GetInput(const sf::Vector2f* source) PlayerDirection AISnake::GetInput(const sf::Vector2f* source)
{ {
// TODO: Figure out why bot is suddenly going rogue
sf::Vector2f directionDelta; sf::Vector2f directionDelta;
if (!source) { if (!source) {
std::cout << "[ERROR - AI] Source was borked, bailing" << std::endl; std::cout << "[ERROR - AI] Source was borked, bailing" << std::endl;
@ -67,7 +66,6 @@ void AISnake::AddIteration(const int size)
} }
std::cout << "[Info - AI] Current average: " << average << std::endl; std::cout << "[Info - AI] Current average: " << average << std::endl;
std::cout << "[Info - AI] Previous iteration size: " << size << std::endl; std::cout << "[Info - AI] Previous iteration size: " << size << std::endl;
} }
void AISnake::ResetPath(void) { void AISnake::ResetPath(void) {
@ -76,29 +74,34 @@ void AISnake::ResetPath(void) {
// 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 sf::Vector2f& source, const sf::Vector2f& boundaries, const int snakeSize) void AISnake::GetNewPath(const sf::Vector2f& source)
{ {
// Search for food // Search for food
// Probability-based approach for fun // Probability-based approach for fun
double roll = ((double) GenerateRandomNumber(RAND_MAX)) / ((double) RAND_MAX); double roll = ((double) GenerateRandomNumber(RAND_MAX)) / ((double) RAND_MAX);
if (roll <= probabilityBFS) { BFS(source, boundaries); } if (roll <= probabilityBFS) { BFS(source); }
else { DFS(source, boundaries); } else { DFS(source); }
UnvisitBoard(); UnvisitBoard();
// Create path for food if (pathFailed) {
path.push(botPathUnsanitized.top()); pathFailed = false;
botPathUnsanitized.pop(); EmptyPath();
path.push(GetAnyOpenPath(source));
} else {
TrimPath(); TrimPath();
if (path.empty())
path.push(GetAnyOpenPath(source));
}
} }
void AISnake::BFS(const sf::Vector2f& source, const sf::Vector2f& boundaries) { void AISnake::BFS(const sf::Vector2f& source) {
std::cout << "[Info - AI] Rolled BFS" << std::endl;
std::queue<sf::Vector2f> search; std::queue<sf::Vector2f> search;
bool foodFound = false;
search.push(source); search.push(source);
while (!search.empty()) { while (!search.empty()) {
sf::Vector2f currentLocation = search.front(); sf::Vector2f currentLocation = search.front();
search.pop(); search.pop();
if (foodFound) { break; } if (g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited)
if (g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited) { continue; } continue;
botPathUnsanitized.push(currentLocation); botPathUnsanitized.push(currentLocation);
std::array<sf::Vector2f, 4> localLocations; std::array<sf::Vector2f, 4> localLocations;
localLocations.fill(currentLocation); localLocations.fill(currentLocation);
@ -108,14 +111,21 @@ void AISnake::BFS(const sf::Vector2f& source, const sf::Vector2f& boundaries) {
localLocations[3].x -= 1; localLocations[3].x -= 1;
for (sf::Vector2f nearby : localLocations) { for (sf::Vector2f nearby : localLocations) {
try { try {
if (g_pEngine->gameBoard.at(nearby.y).at(nearby.x).m_bFood) { GameSpace* space = &g_pEngine->gameBoard.at(nearby.y).at(nearby.x);
if (space->m_bFood) {
botPathUnsanitized.push(nearby); botPathUnsanitized.push(nearby);
foodFound = true; std::cout << "[TRACE - BFS] Path successfully found food" << std::endl;
break; return;
} }
if (g_pEngine->gameBoard.at(nearby.y).at(nearby.x).m_bVisited) if (nearby.x < 1 || nearby.y < 1
|| nearby.x > g_pEngine->GetGameBoundaries().x - 1
|| nearby.y > g_pEngine->GetGameBoundaries().y - 1) {
continue; continue;
if (g_pEngine->gameBoard.at(nearby.y).at(nearby.x).m_bSnake)
}
if (space->m_bVisited)
continue;
if (space->m_bSnake)
continue; continue;
search.push(nearby); search.push(nearby);
} catch (const std::out_of_range& error) { } catch (const std::out_of_range& error) {
@ -124,18 +134,19 @@ void AISnake::BFS(const sf::Vector2f& source, const sf::Vector2f& boundaries) {
} }
g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited = true; g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited = true;
} }
std::cout << "[ERROR - BFS] Failed to get a path to food" << std::endl;
pathFailed = true;
} }
void AISnake::DFS(const sf::Vector2f& source, const sf::Vector2f& boundaries) { void AISnake::DFS(const sf::Vector2f& source) {
std::cout << "[Info - AI] Rolled DFS" << std::endl;
std::stack<sf::Vector2f> search; std::stack<sf::Vector2f> search;
bool foodFound = false;
search.push(source); search.push(source);
while (!search.empty()) { while (!search.empty()) {
sf::Vector2f currentLocation = search.top(); sf::Vector2f currentLocation = search.top();
search.pop(); search.pop();
if (foodFound) { break; } if (g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited)
if (g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited) { continue; } continue;
if (g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bFood) { foodFound = true; }
botPathUnsanitized.push(currentLocation); botPathUnsanitized.push(currentLocation);
std::array<sf::Vector2f, 4> localLocations; std::array<sf::Vector2f, 4> localLocations;
localLocations.fill(currentLocation); localLocations.fill(currentLocation);
@ -145,15 +156,21 @@ void AISnake::DFS(const sf::Vector2f& source, const sf::Vector2f& boundaries) {
localLocations.at(3).x -= 1; localLocations.at(3).x -= 1;
for (sf::Vector2f nearby : localLocations) { for (sf::Vector2f nearby : localLocations) {
try { try {
if (g_pEngine->gameBoard.at(nearby.y).at(nearby.x).m_bFood) { GameSpace* space = &g_pEngine->gameBoard.at(nearby.y).at(nearby.x);
if (space->m_bFood) {
botPathUnsanitized.push(nearby); botPathUnsanitized.push(nearby);
foodFound = true; std::cout << "[TRACE - DFS] Path successfully found food" << std::endl;
std::cout << "[TRACE - AI] Found food, breaking..." << std::endl; return;
break;
} }
if (g_pEngine->gameBoard.at(nearby.y).at(nearby.x).m_bVisited) if (nearby.x < 1 || nearby.y < 1
|| nearby.x > g_pEngine->GetGameBoundaries().x - 1
|| nearby.y > g_pEngine->GetGameBoundaries().y - 1) {
continue; continue;
if (g_pEngine->gameBoard.at(nearby.y).at(nearby.x).m_bSnake)
}
if (space->m_bVisited)
continue;
if (space->m_bSnake)
continue; continue;
search.push(nearby); search.push(nearby);
} catch (const std::out_of_range& error) { } catch (const std::out_of_range& error) {
@ -162,18 +179,17 @@ void AISnake::DFS(const sf::Vector2f& source, const sf::Vector2f& boundaries) {
} }
g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited = true; g_pEngine->gameBoard.at(currentLocation.y).at(currentLocation.x).m_bVisited = true;
} }
std::cout << "[ERROR - DFS] Failed to get a path to food" << std::endl;
pathFailed = true;
} }
sf::Vector2f AISnake::GetAnyOpenPath(const sf::Vector2f& source) { sf::Vector2f AISnake::GetAnyOpenPath(const sf::Vector2f& source) {
sf::Vector2f bail; sf::Vector2f bail;
sf::Vector2f paths[4]; std::array<sf::Vector2f, 4> paths;
paths[0] = source; paths.fill(source);
paths[0].x -= 1; paths[0].x -= 1;
paths[1] = source;
paths[1].x += 1; paths[1].x += 1;
paths[2] = source;
paths[2].y -= 1; paths[2].y -= 1;
paths[3] = source;
paths[3].y += 1; paths[3].y += 1;
for (auto path : paths) { for (auto path : paths) {
@ -192,9 +208,10 @@ sf::Vector2f AISnake::GetAnyOpenPath(const sf::Vector2f& source) {
} }
void AISnake::UnvisitBoard(void) { void AISnake::UnvisitBoard(void) {
for (auto i : g_pEngine->gameBoard) for (std::vector<GameSpace>& i : g_pEngine->gameBoard)
for (auto j : i) for (GameSpace& j : i)
j.m_bVisited = false; j.m_bVisited = false;
std::cout << "[TRACE - AI] Unvisited board" << std::endl;
} }
void AISnake::UpdateAverage(const int size) { void AISnake::UpdateAverage(const int size) {
@ -204,12 +221,24 @@ void AISnake::UpdateAverage(const int size) {
} }
void AISnake::TrimPath(void) { void AISnake::TrimPath(void) {
bool reachedSnake = false;
path.push(botPathUnsanitized.top()); // Push food location
while (!botPathUnsanitized.empty()) { while (!botPathUnsanitized.empty()) {
sf::Vector2f deltaVector = botPathUnsanitized.top() - path.top(); 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); int delta = abs(deltaVector.x) + abs(deltaVector.y);
if (delta == 1) { if (delta == 1)
path.push(botPathUnsanitized.top()); path.push(location);
} }
botPathUnsanitized.pop(); botPathUnsanitized.pop();
} }
std::cout << "[TRACE - AI] Trimmed path" << std::endl;
}
void AISnake::EmptyPath(void) {
while (!botPathUnsanitized.empty())
botPathUnsanitized.pop();
} }

View File

@ -9,7 +9,7 @@ class AISnake {
public: public:
std::stack<sf::Vector2f> path; std::stack<sf::Vector2f> path;
AISnake(); AISnake();
void GetNewPath(const sf::Vector2f& source, const sf::Vector2f& boundaries, const int snakeSize); void GetNewPath(const sf::Vector2f& source);
PlayerDirection GetInput(const sf::Vector2f* source); PlayerDirection GetInput(const sf::Vector2f* source);
void UpdateProbability(int snakeSize); void UpdateProbability(int snakeSize);
void AdjustProbability(double amount); void AdjustProbability(double amount);
@ -19,14 +19,16 @@ public:
private: private:
int totalLength = 0; int totalLength = 0;
double average = 0; double average = 0;
double probabilityBFS = 0.500; double probabilityBFS = 0.800;
bool pathFailed = false;
std::stack<sf::Vector2f> botPathUnsanitized; std::stack<sf::Vector2f> botPathUnsanitized;
void BFS(const sf::Vector2f& source, const sf::Vector2f& boundaries); void BFS(const sf::Vector2f& source);
void DFS(const sf::Vector2f& source, const sf::Vector2f& boundaries); void DFS(const sf::Vector2f& source);
sf::Vector2f GetAnyOpenPath(const sf::Vector2f& source); sf::Vector2f GetAnyOpenPath(const sf::Vector2f& source);
void UnvisitBoard(void); void UnvisitBoard(void);
void UpdateAverage(const int size); void UpdateAverage(const int size);
void TrimPath(void); void TrimPath(void);
void EmptyPath(void);
}; };
#endif #endif

View File

@ -126,7 +126,7 @@ void GameEngine::UpdatePlayerSpeed(void)
PlayerDirection controller; PlayerDirection controller;
if (state.m_bIsBotControlled) { if (state.m_bIsBotControlled) {
if (bot.path.empty()) { if (bot.path.empty()) {
bot.GetNewPath(player.headLocation, GetGameBoundaries(), player.body.size()); bot.GetNewPath(player.headLocation);
} }
controller = bot.GetInput(&player.headLocation); controller = bot.GetInput(&player.headLocation);
} }