-2

I'm making an AI controlled alpha-beta algorithm for a school project, but my algorithm is very inconsistent. Sometimes it blocks all my moves successfully, and sometimes it just ignores my 3 in a row as seen here. How could this happen and how can I resolve the issue?

int alphaBeta(const State board, int alpha, int beta, const Player player, int depth)
{
    //Max player = Player::O
    //Min player = Player::X

    std::vector<Move> possibleMoves = getMoves(board);

    if(eval(board)==Player::X){return 9999-depth;}      //Player X wins
    else if(eval(board)==Player::O){return -9999+depth;}    //Player O wins
    else if(possibleMoves.size()==0){return 0;}     //Tie
    else{   //Zoek verder
        depth++;
        State nextBoard = board;
        int result;

        if(player==Player::O){
            for (Move move: possibleMoves) {
                nextBoard = doMove(nextBoard, move);
                result = alphaBeta(nextBoard, alpha, beta, Player::X, depth);
                if (result > alpha){    
                    alpha = result; 
                    if (depth == 1){
                                    choice = move; //The actual move he will do
                    }
                }
                else if (alpha >= beta){ 
                    return alpha; 
                }
            }
            return alpha;
        }

        else{
            for (Move move: possibleMoves) {
                nextBoard = doMove(nextBoard, move);
                result = alphaBeta(nextBoard, alpha, beta, Player::O, depth);
                if (result < beta){ 
                    beta = result;
                    if (depth == 1){
                                    choice = move;
                    }
                }
                else if (beta <= alpha){ 
                    return beta;
                }
            }
            return beta;
        }
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
  • 1
    May I recommend you familiarise yourself with [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) ;) – Jesper Juhl Mar 27 '18 at 12:06
  • Welcome to StackOverflow. Please read and follow the posting guidelines in the help documentation, as suggested when you created this account. [Minimal, complete, verifiable example](http://stackoverflow.com/help/mcve) applies here. We cannot effectively help you until you post your MCVE code and accurately describe the problem. We should be able to paste your posted code into a text file and reproduce the problem you described. – Prune Mar 27 '18 at 16:27
  • Your posted code merely defines a function and exits without executing. There is no attempt to trace the logic and data flow. See this lovely [debug](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) blog for help. – Prune Mar 27 '18 at 16:27
  • Wow excuse me. I'll fix it soon. –  Mar 27 '18 at 17:22

2 Answers2

2

You're repeatedly modifying nextBoard, adding (possibly illegal) moves to it:

nextBoard = doMove(nextBoard, move);

but you should try each move in turn on the original board:

State nextBoard = doMove(board, move);

(Disclaimer: there may be other issues, as well.)

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
  • Hmm but now I need to undo the moves on the original board everytime I use this, right? Edit: The bot now keeps placing them all on row 0 and once it has 3 circles he places 3 on row 1 etc... –  Mar 27 '18 at 13:12
  • @KevinSnijder Assuming that `doMove` doesn't modify its board argument (otherwise, why return a board?), you don't need to undo anything. If it does modify it, make it stop. Recursion and mutation is an unpleasant combination. – molbdnilo Mar 27 '18 at 13:23
  • Hmm Thanks for your help by the way, doMove copies the board it recieves and returns the result from the move. But still. With your solution my bot keeps placing them in row 0 for some reason. `State doMove(const State &state, const Move &m) { State result = state; for (int r=0; r<6; r++) { if (r == 5 || result[r+1][m] != Player::None) { result[r][m] = getCurrentPlayer(state); return result; } } return result; // Invalid move }` –  Mar 27 '18 at 13:25
  • @KevinSnijder It looks like your move search has been "split" between `possibleMoves` and `doMove`. Leave the searching to `possibleMoves` and do only the execution in `doMove`. – molbdnilo Mar 27 '18 at 13:36
  • I am not quite sure what you mean by "split", I got the doMove function from my teacher, am I supposed to change something about it? –  Mar 27 '18 at 13:44
  • @KevinSnijder It's a mystery to me why `doMove` *searches* for a valid move that `possibleMoves` should have already found. I would expect it to only receive a valid move and update the board. But it's possible that you're supposed to approach the problem in a very specific way that your teacher has determined (in which case you should ignore my ramblings and talk to your teacher and/or your fellow students). – molbdnilo Mar 27 '18 at 13:53
0

1) Don't evaluate each and every nodes within the recursive call, that would be too much time expensive. Evaluate only the leaf nodes.

2) Use boundary condition in the minimax recursive call to terminate if the depth is more than certain value; every branches does not lead to a winning move, and the search will be too big, and the program may hang.

3) Consider using multi-thread on the top level branches to speed up the search.

int alphaBeta(const State board, int alpha, int beta, const Player player, int depth)
{
    std::vector<Move> possibleMoves = getMoves(board);

    if(CheckForWinX(board))
    {
        return 9999;
    }      
    else 
        if(CheckForWinO(board))
    {
        return -9999;
    }   
    else 
        if(possibleMoves.size()==0)
    {
        return 0;
    }     
    else 
        if( depth >= 5)   // without this boundary condition, the search tree will too big 
    { 
        return eval(board);    // evaluate ( which is more time expensive than CheckForWin() ) only the leaf node, not every nodes 
    }
    else{   
        depth++;
        State nextBoard = board;
        int result;

        if(player==Player::O){
              /**/
            return alpha;
        }
        else{
             /**/
            return beta;
        }
    }
}
seccpur
  • 4,996
  • 2
  • 13
  • 21