snakeplusplus/src/botinterface.cpp

169 lines
6.6 KiB
C++
Raw Normal View History

#include "botinterface.hpp"
#include "common.hpp"
2023-10-13 19:07:08 -05:00
#include <array>
#include <cstdlib>
#include <queue>
#include <stdexcept>
#include <SFML/System/Vector2.hpp>
namespace snakeplusplus
{
PlayerDirection lastKnownDirection = kNone;
2023-10-13 19:07:08 -05:00
AISnake::AISnake() {
;
}
PlayerDirection AISnake::GetInput(const sf::Vector2f* source)
{
sf::Vector2f directionDelta;
2023-10-13 19:07:08 -05:00
if (*source == path.top()) { path.pop(); }
if (path.empty()) { return kUp; } // Snake is trapped
directionDelta = *source - path.top();
path.pop();
if ((directionDelta.y == 1)
&& (lastKnownDirection != kDown))
{ lastKnownDirection = kUp; }
2023-10-13 19:07:08 -05:00
else if ((directionDelta.y == -1)
&& (lastKnownDirection != kUp))
{ lastKnownDirection = kDown; }
2023-10-13 19:07:08 -05:00
else if ((directionDelta.x == 1)
&& (lastKnownDirection != kRight))
{ lastKnownDirection = kLeft; }
2023-10-13 19:07:08 -05:00
else if ((directionDelta.x == -1)
&& (lastKnownDirection != kLeft))
{ lastKnownDirection = kRight; }
return lastKnownDirection;
}
2023-10-13 19:07:08 -05:00
void AISnake::UpdateProbability(int snakeSize)
{
probabilityBFS = 1 - ((double) snakeSize) / 1000;
return;
}
2023-10-13 19:07:08 -05:00
// Gets a new path for the bot to follow
// Uses DFS algorithm
2023-10-13 19:22:11 -05:00
void AISnake::GetNewPath(const std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries, const int snakeSize)
2023-10-13 19:07:08 -05:00
{
2023-10-13 19:22:11 -05:00
// Search for food
/*
BFS(gameBoard, source, boundaries);
if (gameBoard[botPathUnsanitized.top().y][botPathUnsanitized.top().x] != 'X') {
while (!botPathUnsanitized.empty()) { botPathUnsanitized.pop(); }
2023-10-13 19:22:11 -05:00
DFS(gameBoard, source, boundaries);
while (botPathUnsanitized.size() > 15) { botPathUnsanitized.pop(); }
2023-10-13 19:22:11 -05:00
}
*/
// Probability-based approach for fun
double roll = ((double) GenerateRandomNumber(RAND_MAX)) / ((double) RAND_MAX);
2023-11-05 19:04:44 -06:00
if (roll <= probabilityBFS) { BFS(gameBoard, source, boundaries); }
else { DFS(gameBoard, source, boundaries); }
2023-10-13 19:22:11 -05:00
// 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 std::vector< std::vector<char> >& gameBoard, const sf::Vector2f& source, const sf::Vector2f& boundaries) {
2023-10-13 19:07:08 -05:00
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()) {
2023-10-13 19:22:11 -05:00
sf::Vector2f currentLocation = search.front();
2023-10-13 19:07:08 -05:00
search.pop();
if (foodFound) { break; }
if (visited.at(currentLocation.y).at(currentLocation.x)) { continue; }
if (gameBoard.at(currentLocation.y).at(currentLocation.x) == 'X') {
foodFound = true;
}
2023-10-13 19:22:11 -05:00
botPathUnsanitized.push(currentLocation);
std::array<sf::Vector2f, 4> localLocations;
2023-10-13 19:07:08 -05:00
localLocations.fill(currentLocation);
localLocations[0].y += 1;
localLocations[1].x += 1;
localLocations[2].y -= 1;
localLocations[3].x -= 1;
for (auto i : localLocations) {
try {
if (gameBoard.at(i.y).at(i.x) == 'X') {
botPathUnsanitized.push(i);
foodFound = true;
}
} catch (const std::out_of_range& error) {
continue; // Out of bounds
}
}
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;
}
2023-10-13 19:22:11 -05:00
}
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 (foodFound) { break; }
if (visited.at(currentLocation.y).at(currentLocation.x)) { continue; }
if (gameBoard.at(currentLocation.y).at(currentLocation.x) == 'X') {
foodFound = true;
2023-10-13 19:07:08 -05:00
}
2023-10-13 19:22:11 -05:00
botPathUnsanitized.push(currentLocation);
std::array<sf::Vector2f, 4> localLocations;
localLocations.fill(currentLocation);
localLocations[0].y += 1;
localLocations[1].x += 1;
localLocations[2].y -= 1;
localLocations[3].x -= 1;
for (auto i : localLocations) {
try {
if (gameBoard.at(i.y).at(i.x) == 'X') {
botPathUnsanitized.push(i);
foodFound = true;
}
} catch (const std::out_of_range& error) {
continue; // Out of bounds
}
}
for (sf::Vector2f newLocation : localLocations) {
try {
if (newLocation.x < 1 || newLocation.y < 1
2023-10-23 21:33:47 -05:00
|| newLocation.x > boundaries.x - 2
|| newLocation.y > boundaries.y - 2) {
continue;
}
2023-10-13 19:22:11 -05:00
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;
2023-10-13 19:07:08 -05:00
}
}
}