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 )
(the shown games above it's the bruteforcing steps, not the actual game)
AI algorithm:
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
}
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
}
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
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.