-2

I'm trying to make a simple AI for playing tictactoe, but it is not working; I get an 'std::out_of_range' when the board fills up (brute forcing till the first final nodes of the first branch), and I quite didn't find a solution.

If I play X at 0,0 it crashes on the return of the best move return jogadas.at(melhorMov); // return best move after the 10th iteration.

(full code for Windows MingW here if you wish )

Image of the error

(the shown games above it's the bruteforcing steps, not the actual game)

AI algorithm:

  1. Moving/launching AI

    void IA::movimenta(){ //lauches the AI move action
        std::cout << "**Pensando**" << std::endl; //little message
        pensamentos = 0;                          //counter of iterations
        cmp = campo.getcampo();                   //gets the pointer** to the tictactoe field
        AiMove jogada = MelhorJogada(AIjogador);  //gets the best move for the AIplayer
        campo.joga(AIjogador,jogada.x,jogada.y);  //puts the best move on the field
    }
    
  2. Minimax Code

    #include "ia.h"
    #include <vector>
    #include <iostream>
    
    using namespace std;
    
    IA::IA()
    {
    
    }
    
    
    void IA::inicia(field &campo, field::jogador IAplayer){
        this->campo = campo;
    
        this->AIjogador = IAplayer;
        if(IAplayer == field::x){
            this->HUjogador = field::o;
        }else{
            this->HUjogador = field::x;
        }
    
    }
    
    
    void printacamp(field::campo **campo){
        cout << "  0 1 2" << endl;
        for (int var = 0; var < 3; ++var) {
            cout << var;
            cout << "|";
            for (int var2 = 0; var2 < 3; ++var2) {
    
                switch (campo[var][var2]) {
                case field::NADA:
                    cout << " ";
                    break;
                case field::x_c:
                    cout << "X";
                    break;
                case field::o_c:
                    cout << "O";
                    break;
                default:
                    break;
                }
    
                cout << "|";
            }
            cout << endl;
        }
    }
    
    void IA::movimenta(){ //lauches the AI move action
        std::cout << "**Pensando**" << std::endl; //little message
        pensamentos = 0;                          //couter of iterations
        cmp = campo.getcampo();                   //gets the pointer** to the tictactoe field
        AiMove jogada = MelhorJogada(AIjogador);  //gets the best move for the AIplayer
        campo.joga(AIjogador,jogada.x,jogada.y);  //puts the best move on the field
    }
    
    AiMove IA::MelhorJogada(field::jogador jogador){ //bruteforce through the possible moves and searches for the best one
        field::jogador vic = campo.checaJ(); // checks if someone wins the game (final node)
        if(vic == AIjogador){                //if AIplayer wins
            return AiMove(15);             //add 15 points
        } else if(vic == jogador){           //if human wins
            return AiMove(-15);            //add -15 points
        } else if(vic == field::NINGJ){      //if its a tie (full board no winners)
            return AiMove(0);               //score 0
        }                                  //if none of those, the game is still in progress
        std::vector<AiMove> jogadas;        //vector for moves, something tells me that it should be static but it gives alot of bugs and nosense moves (or no moves at all)
        AiMove jogada;                      //move for this iteration
        pensamentos++;                      //add counter
        cout << pensamentos << endl;        //print counter
        for (int y = 0; y < 3; ++y) {       //for each of the board
            for (int x = 0; x < 3; ++x) {
                if(cmp[x][y] == field::NADA){  // if its an empty space
                    jogada.x = x;              //gets the x,y of the empty space
                    jogada.y = y;
                    campo.jogaJ(jogador,jogada.x,jogada.y); //makes a move
    
                    printacamp(cmp);           //prints it on screen
    
                    if(jogador == AIjogador){  //if its max(AI) next is human
                        jogada.pontos = MelhorJogada(HUjogador).pontos;
                    }else{                     //if its min(Human) next is AI
                        jogada.pontos = MelhorJogada(AIjogador).pontos;
                    }
                    jogadas.push_back(jogada); //store the score of the final node
                    campo.jogaJ(field::NADAJ,jogada.x,jogada.y); // delete the move made for testings
                }
            }
        }
        int melhorMov = 0;                  // bestMove is 0
        if(jogador == AIjogador){           // if AIturn(max)
            int melhorPon = -1000000;       // best pontuation = -∞
            for (int var = 0; var < jogadas.size(); ++var) { //best move/points of the subbranches
                if(jogadas.at(var).pontos > melhorPon){
                    melhorMov = var;
                    melhorPon = jogadas.at(var).pontos;
                }
            }
        } else {                            //if human(min)
            int melhorPon = 1000000;        //best pontution = +∞
            for (int var = 0; var < jogadas.size(); ++var) { //best move/points of the subbranches
                if(jogadas.at(var).pontos < melhorPon){
                    melhorMov = var;
                    melhorPon = jogadas.at(var).pontos;
                }
            }
        }
        return jogadas.at(melhorMov); // return best move
    
    }
    
  3. AI.h

    #pragma once
    #ifndef IA_H
    #define IA_H
    #include "field.h"
    #include <vector>
    struct AiMove {
        AiMove() {};
        AiMove(int Pontos) : pontos(Pontos) {}
        int x;
        int y;
        int pontos;
    };
    
    
    class IA
    {
    
    public:
            IA();
            void inicia(field &camp, field::jogador IAplayer);
            void movimenta();
    private:
            field campo;
            AiMove MelhorJogada(field::jogador HUjogador);
            field::campo **cmp;
            field::jogador HUjogador;
            field::jogador AIjogador;
            int pensamentos = 0;
    
    };
    #endif // IA_H
    
Ollegn
  • 2,294
  • 2
  • 16
  • 22

1 Answers1

1

Your vector is empty (as it should be, there are no remaining legal moves).

Calling at(0) on an empty vector throws an exception, because there is no element 0.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • actually there is positions at 0 but you are right, i set the initial condition to -1 and made a verification on the return and other modifications and it worked, thanks for your help – Ollegn Dec 14 '15 at 16:21
  • @Ollegn: I'm not talking about position (0, 0) on the board, but index 0 in the vector. When the vector is empty, 0 is not a legal index. – Ben Voigt Dec 14 '15 at 16:58
  • Im talking about the vectors, in c++ vectors starts at 0 and goes forth `Notice that the first element has a position of 0 (not 1).` [Font](http://www.cplusplus.com/reference/vector/vector/at/) – Ollegn Dec 14 '15 at 18:54
  • 1
    @Ollegn: You missed my point. Valid indexes have `i < size()`. When `size() == 0`, then `0` is not a valid index. – Ben Voigt Dec 14 '15 at 19:19
  • oh, got it, it's true, the size counting starts at 1, if no data is stored in the vector the size is 0. – Ollegn Dec 14 '15 at 19:26
  • 1
    @Ollegn: Right. `0` isn't a problem because of being too low (you are correct that indexing starts at `0` not `1`). It's a problem because it is too high, when the vector is empty. – Ben Voigt Dec 14 '15 at 19:27