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) {
 | 
				
			||||||
 | 
					        if (!state.m_bSmart) {
 | 
				
			||||||
            while (!bot.path.empty())
 | 
					            while (!bot.path.empty())
 | 
				
			||||||
                bot.path.pop();
 | 
					                bot.path.pop();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if (state.m_bNoDisplay)
 | 
					        if (state.m_bNoDisplay)
 | 
				
			||||||
            graphics.SetShowGame(false);
 | 
					            graphics.SetShowGame(false);
 | 
				
			||||||
 | 
					        if (state.m_bSkipIterations)
 | 
				
			||||||
            graphics.SetShowGame((bot.amountPlayed + 1) % 50 == 0);
 | 
					            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