ai: added basic decision making based on sensors, needs weights and weight adjustment
This commit is contained in:
parent
f055ab4078
commit
a5902171ec
@ -16,6 +16,8 @@ AISnake::AISnake() {
|
|||||||
|
|
||||||
PlayerDirection AISnake::GetInput(const sf::Vector2f* source)
|
PlayerDirection AISnake::GetInput(const sf::Vector2f* source)
|
||||||
{
|
{
|
||||||
|
if (g_pEngine->state.m_bSmart)
|
||||||
|
return CurrentBestDecision();
|
||||||
sf::Vector2f directionDelta;
|
sf::Vector2f directionDelta;
|
||||||
if (!source)
|
if (!source)
|
||||||
return kUp;
|
return kUp;
|
||||||
@ -222,3 +224,129 @@ void AISnake::EmptyPath(void) {
|
|||||||
while (!botPathUnsanitized.empty())
|
while (!botPathUnsanitized.empty())
|
||||||
botPathUnsanitized.pop();
|
botPathUnsanitized.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Main method for using AI snake
|
||||||
|
PlayerDirection AISnake::CurrentBestDecision(void) {
|
||||||
|
// Reset probabilities
|
||||||
|
probabilityUp = 0.25;
|
||||||
|
probabilityDown = 0.25;
|
||||||
|
probabilityLeft = 0.25;
|
||||||
|
probabilityRight = 0.25;
|
||||||
|
|
||||||
|
// Calculate options
|
||||||
|
CheckLocalFreedom();
|
||||||
|
CheckFoodDirection();
|
||||||
|
|
||||||
|
// Deny impossible movement
|
||||||
|
RemoveImpossibleChoice();
|
||||||
|
|
||||||
|
// Make decision
|
||||||
|
if (probabilityUp > probabilityDown) {
|
||||||
|
if (probabilityUp < probabilityLeft)
|
||||||
|
return kLeft;
|
||||||
|
if (probabilityUp < probabilityRight)
|
||||||
|
return kRight;
|
||||||
|
return kUp;
|
||||||
|
} else {
|
||||||
|
if (probabilityDown < probabilityLeft)
|
||||||
|
return kLeft;
|
||||||
|
if (probabilityDown < probabilityRight)
|
||||||
|
return kRight;
|
||||||
|
return kDown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Improves probability based on amount of local open spaces
|
||||||
|
// TODO: Add weights
|
||||||
|
void AISnake::CheckLocalFreedom(void) {
|
||||||
|
std::array<sf::Vector2f, 4> choices;
|
||||||
|
std::array<double, 4> chances;
|
||||||
|
chances.fill(0);
|
||||||
|
choices.fill(g_pEngine->GetHeadLocation());
|
||||||
|
choices[0].y += 1;
|
||||||
|
choices[1].x += 1;
|
||||||
|
choices[2].y -= 1;
|
||||||
|
choices[3].x -= 1;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
try {
|
||||||
|
if (g_pEngine->gameBoard.at(choices[i].y).at(choices[i].x).m_bSnake) {
|
||||||
|
chances[i] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (g_pEngine->gameBoard.at(choices[i].y).at(choices[i].x).m_bFood) {
|
||||||
|
chances[0] = 0;
|
||||||
|
chances[1] = 0;
|
||||||
|
chances[2] = 0;
|
||||||
|
chances[3] = 0;
|
||||||
|
chances[i] = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (const std::out_of_range& error) {
|
||||||
|
continue; // Out of bounds
|
||||||
|
}
|
||||||
|
double openSpaces = 0;
|
||||||
|
bool foodNearby = false;
|
||||||
|
for (int j = -1; j < 2; ++j) {
|
||||||
|
for (int k = -1; k < 2; ++k) {
|
||||||
|
try {
|
||||||
|
if (!g_pEngine->gameBoard.at(choices[i].y + j).at(choices[i].x + k).m_bSnake)
|
||||||
|
++openSpaces;
|
||||||
|
if (g_pEngine->gameBoard.at(choices[i].y + j).at(choices[i].x + k).m_bFood)
|
||||||
|
foodNearby = true;
|
||||||
|
} catch (const std::out_of_range& error) {
|
||||||
|
continue; // Out of bounds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chances[i] = openSpaces * 0.11111111111;
|
||||||
|
if (foodNearby)
|
||||||
|
chances[i] = 1; // Ignore chance, be greedy because food
|
||||||
|
}
|
||||||
|
probabilityDown *= chances[0];
|
||||||
|
probabilityRight *= chances[1];
|
||||||
|
probabilityUp *= chances[2];
|
||||||
|
probabilityLeft *= chances[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Improves probability that direction of food is best option
|
||||||
|
// TODO: Add weights
|
||||||
|
void AISnake::CheckFoodDirection(void) {
|
||||||
|
sf::Vector2f delta = g_pEngine->GetHeadLocation() - g_pEngine->GetFoodLocation();
|
||||||
|
if (delta.x > 0)
|
||||||
|
probabilityLeft *= 2;
|
||||||
|
if (delta.x < 0)
|
||||||
|
probabilityRight *= 2;
|
||||||
|
if (delta.y > 0)
|
||||||
|
probabilityUp *= 2;
|
||||||
|
if (delta.y < 0)
|
||||||
|
probabilityDown *= 2;
|
||||||
|
std::array<sf::Vector2f, 4> choices;
|
||||||
|
choices.fill(g_pEngine->GetHeadLocation());
|
||||||
|
choices[0].y += 1;
|
||||||
|
choices[1].x += 1;
|
||||||
|
choices[2].y -= 1;
|
||||||
|
choices[3].x -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AISnake::RemoveImpossibleChoice(void) {
|
||||||
|
if (g_pEngine->GetPlayerSize() == 1)
|
||||||
|
return; // Player can go any direction
|
||||||
|
PlayerDirection currentDirection = g_pEngine->GetCurrentDirection();
|
||||||
|
switch (currentDirection) {
|
||||||
|
case (kUp):
|
||||||
|
probabilityDown = 0;
|
||||||
|
break;
|
||||||
|
case (kDown):
|
||||||
|
probabilityUp = 0;
|
||||||
|
break;
|
||||||
|
case (kLeft):
|
||||||
|
probabilityRight = 0;
|
||||||
|
break;
|
||||||
|
case (kRight):
|
||||||
|
probabilityLeft = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
std::cout << "[ERR - AI] Impossibility defaulted somehow??" << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,6 +21,8 @@ private:
|
|||||||
double average = 0;
|
double average = 0;
|
||||||
double probabilityBFS = 0.800;
|
double probabilityBFS = 0.800;
|
||||||
bool pathFailed = false;
|
bool pathFailed = false;
|
||||||
|
|
||||||
|
// Generic search algorithms
|
||||||
std::stack<sf::Vector2f> botPathUnsanitized;
|
std::stack<sf::Vector2f> botPathUnsanitized;
|
||||||
void BFS(const sf::Vector2f& source);
|
void BFS(const sf::Vector2f& source);
|
||||||
void DFS(const sf::Vector2f& source);
|
void DFS(const sf::Vector2f& source);
|
||||||
@ -29,6 +31,18 @@ private:
|
|||||||
void UpdateAverage(const int size);
|
void UpdateAverage(const int size);
|
||||||
void TrimPath(void);
|
void TrimPath(void);
|
||||||
void EmptyPath(void);
|
void EmptyPath(void);
|
||||||
|
|
||||||
|
// Unsupervised learning
|
||||||
|
// Make decisions about current state of board
|
||||||
|
double probabilityUp = 0.25;
|
||||||
|
double probabilityDown = 0.25;
|
||||||
|
double probabilityLeft = 0.25;
|
||||||
|
double probabilityRight = 0.25;
|
||||||
|
PlayerDirection CurrentBestDecision(void);
|
||||||
|
void CheckLocalFreedom(void);
|
||||||
|
void CheckFoodDirection(void);
|
||||||
|
void RemoveImpossibleChoice(void);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -31,11 +31,15 @@ void GameEngine::Reset()
|
|||||||
PrepareGameBoard();
|
PrepareGameBoard();
|
||||||
state.m_bIsGameOver = false;
|
state.m_bIsGameOver = false;
|
||||||
if (state.m_bIsBotControlled) {
|
if (state.m_bIsBotControlled) {
|
||||||
while (!bot.path.empty())
|
if (!state.m_bSmart) {
|
||||||
bot.path.pop();
|
while (!bot.path.empty())
|
||||||
|
bot.path.pop();
|
||||||
|
}
|
||||||
if (state.m_bNoDisplay)
|
if (state.m_bNoDisplay)
|
||||||
graphics.SetShowGame(false);
|
graphics.SetShowGame(false);
|
||||||
graphics.SetShowGame((bot.amountPlayed + 1) % 50 == 0);
|
if (state.m_bSkipIterations)
|
||||||
|
graphics.SetShowGame((bot.amountPlayed + 1) % 50 == 0);
|
||||||
|
graphics.SetShowGame(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,6 +70,32 @@ sf::Vector2f GameEngine::GetGameBoundaries(void)
|
|||||||
return graphics.gameBoundaries;
|
return graphics.gameBoundaries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlayerDirection GameEngine::GetCurrentDirection(void) {
|
||||||
|
if (player.speed.x) {
|
||||||
|
if (player.speed.x > 0)
|
||||||
|
return kRight;
|
||||||
|
else
|
||||||
|
return kLeft;
|
||||||
|
} else {
|
||||||
|
if (player.speed.y > 0)
|
||||||
|
return kDown;
|
||||||
|
else
|
||||||
|
return kUp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int GameEngine::GetPlayerSize(void) {
|
||||||
|
return player.body.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
sf::Vector2f GameEngine::GetHeadLocation(void) {
|
||||||
|
return player.headLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
sf::Vector2f GameEngine::GetFoodLocation(void) {
|
||||||
|
return playerFood.location;
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <SFML/Graphics.hpp>
|
#include <SFML/Graphics.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "botinterface.hpp"
|
#include "botinterface.hpp"
|
||||||
|
#include "common.hpp"
|
||||||
#include "snake.hpp"
|
#include "snake.hpp"
|
||||||
#include "playerinterface.hpp"
|
#include "playerinterface.hpp"
|
||||||
|
|
||||||
@ -21,13 +22,17 @@ public:
|
|||||||
unsigned char m_bIsGameOver : 1 = 0;
|
unsigned char m_bIsGameOver : 1 = 0;
|
||||||
unsigned char m_bIsBotControlled : 1 = 0;
|
unsigned char m_bIsBotControlled : 1 = 0;
|
||||||
unsigned char m_bNoDisplay : 1 = 0;
|
unsigned char m_bNoDisplay : 1 = 0;
|
||||||
unsigned char _3 : 1 = 0;
|
unsigned char m_bSmart : 1 = 0;
|
||||||
unsigned char _4 : 1 = 0;
|
unsigned char m_bSkipIterations : 1 = 0;
|
||||||
unsigned char _5 : 1 = 0;
|
unsigned char _5 : 1 = 0;
|
||||||
unsigned char _6 : 1 = 0;
|
unsigned char _6 : 1 = 0;
|
||||||
unsigned char _7 : 1 = 0;
|
unsigned char _7 : 1 = 0;
|
||||||
} state;
|
} state;
|
||||||
std::vector< std::vector<GameSpace> > gameBoard;
|
std::vector< std::vector<GameSpace> > gameBoard;
|
||||||
|
PlayerDirection GetCurrentDirection(void);
|
||||||
|
int GetPlayerSize(void);
|
||||||
|
sf::Vector2f GetHeadLocation(void);
|
||||||
|
sf::Vector2f GetFoodLocation(void);
|
||||||
private:
|
private:
|
||||||
PlayerOutput graphics;
|
PlayerOutput graphics;
|
||||||
Snake player;
|
Snake player;
|
||||||
|
@ -28,6 +28,12 @@ int main(int argc, char* argv[]) {
|
|||||||
} else if (args[i].compare("--auto") == 0) {
|
} else if (args[i].compare("--auto") == 0) {
|
||||||
g_pEngine->state.m_bIsBotControlled = true;
|
g_pEngine->state.m_bIsBotControlled = true;
|
||||||
std::cout << "[LOG - Main] Bot control enabled" << std::endl;
|
std::cout << "[LOG - Main] Bot control enabled" << std::endl;
|
||||||
|
} else if (args[i].compare("--smart") == 0) {
|
||||||
|
g_pEngine->state.m_bSmart = true;
|
||||||
|
std::cout << "[LOG - Main] Using AI" << std::endl;
|
||||||
|
} else if (args[i].compare("--skip") == 0) {
|
||||||
|
g_pEngine->state.m_bSkipIterations = true;
|
||||||
|
std::cout << "[LOG - Main] Only showing every 50 epochs" << std::endl;
|
||||||
} else if (args[i].compare("-h") == 0 || args[i].compare("--help") == 0) {
|
} else if (args[i].compare("-h") == 0 || args[i].compare("--help") == 0) {
|
||||||
Help();
|
Help();
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user