1

The game I'm talking about is something similar to Gomoku or a "large", simplified version of Tic-Tac-Toe. Basically, you have an 8x8 board and the winner is the one that chains 4 in a row or column (no diagonal).

I've set up a minimax with alpha-beta pruning, the problem I have is that I'm not sure how returned values can let you know which move to make. Or like how to connect values to a move.

Currently, I've considered returning a GameStateNode instead. With the GameStateNode having fields: char[][] (the current state of the board), evaluationVal(the value of the current state when it is not a terminal node).

But I still can't think of a way to use the returned node to decide on the best move.

    // Alpha-Beta Pruning Search
    private static Node alphaBeta(Node initial, int depth) {
        Node bestMove = max(initial, depth, NEGATIVE_INFINITY, POSITIVE_INFINITY);
        return bestMove;
    }

    private static Node max(Node n, int depth, int alpha, int beta) {
        int value = NEGATIVE_INFINITY;
        Node currentBestMove = null;
        Node temp = null;

        // Terminal state
        if(n.fourInALine() != 0) {
            return n;
        }
        // Depth limit reached
        if(depth == 0) {
            return n;
        }

        ArrayList<Node> successors = n.generateSuccessors('X');
        // Iterate through all the successors, starting with best evaluationValues
        for(Node s : successors) {
            temp = min(s, depth - 1, alpha, beta);
            if(temp.evaluationVal > value) {
                value = temp.evaluationVal;
                currentBestMove = temp;
            }
            alpha = Math.max(alpha, value);
            if(alpha >= beta) {
                break;
            }
        }
        return currentBestMove;
    }

    // I have similar min method just with the correct comparison
Amai
  • 141
  • 6
  • You haven't asked any question. And your problem is very broad.Please see: [Why is “Can someone help me?” not an actual question?](http://meta.stackoverflow.com/q/284236). Also check [ask] on how to improve the quality of your question and [edit] your question with additional information, preferable with a [mcve]. – Progman Aug 04 '19 at 09:21
  • @Progman I'm confused on how to narrow it down, when I'm not exactly asking a problem where I need people to help with my coding. I'm more of asking how to make a move with a returned value from the minimax, but I guess I will try to edit the problem to make it emphasize that more. – Amai Aug 04 '19 at 16:26
  • Start smaller. You've shown code, but nothing that explains whether that code already works or not, so a [mcve] is probably a good next move in terms of making this question better. Also, it's unclear _why_ you're showing that code: if the question is "how do I use a Node to make a move" the most obvious counter-question would be "you tell us? _you_ wrote the code, so _you_ know _your_ requirements and presumably wrote it accordingly?". If that is not the case, then please explain why you wrote it. – Mike 'Pomax' Kamermans Aug 04 '19 at 16:36
  • To disagree with @Mike'Pomax'Kamermans and others, I think the question *is* extremely specific, and the code excerpt is sufficiently minimal and reproducible for us to analyze. This question doesn't require editing or improvement. And don't give him the "new user lecture"; he's been here for a while and knows how the site works. – MultiplyByZer0 Aug 04 '19 at 16:48
  • Amai has 66 rep, an amount that in no way suggests familiarity with SO in the slightest. SO is not about questions that are written such that there might be one person who understands it, good SO questions are written in a form that anyone with knowledge of the tagged subjects understands it. That is not the case right now. It is unclear what people are actually asked to help with. If you think that is not the case, then please _help everyone else by editing the question_ because you clearly understand it, and can probably rephrase it so that more people do, too. – Mike 'Pomax' Kamermans Aug 04 '19 at 17:04
  • @Mike'Pomax'Kamermans First, reputation means nothing. And I can't understand why you think what OP is asking for is unclear: it's literally in the question title. OP is trying to implement [minimax search](https://en.wikipedia.org/wiki/Minimax#Minimax_algorithm_with_alternate_moves), a common algorithm used to play board games such as chess or, in this case, [gomoku](https://en.wikipedia.org/wiki/Gomoku). Minimax works by generating all possible moves for player one (that's what `max()` does through `node.generateSuccessors()`), and then all possible moves in response for player two... – MultiplyByZer0 Aug 04 '19 at 19:32
  • ...(that's `min()` that is omitted here, because it's practically identical to `max` except that it returns the move with the lowest score instead of the highest; most implementations combine the two functions into one), and all possible moves in response to *that* for player one, and so on. This essentially build a search tree of all possible moves up to a certain depth. To prevent the algorithm from running forever, at the stopping depth (a count of max-min-max-min-etc cycles), we stop recursing and just ask a [evaluation function](https://en.wikipedia.org/wiki/Evaluation_function)... – MultiplyByZer0 Aug 04 '19 at 19:33
  • ...to look at the board and give a guess (a score) about how good the position is. That's `node.evaluationVal`. The scores are then propagated back up the tree, and each layer of the tree is either a maximizing layer (`max()` runs), representing player one (us, the maximizing player) trying to find the node with the best score; or a minimizing layer (`min()` runs), representing the player two (the opponent, the minimizing player) trying to find the node with the worst score (the worst move for us is the best move for them). Eventually, the recursion finishes and the "best move"... – MultiplyByZer0 Aug 04 '19 at 19:33
  • ...is returned from the `alphaBeta()` function. That's minimax, and if it is unclear, [here](https://stackoverflow.com/a/3956356) is a better explanation. [Alpha-beta search](https://web.archive.org/web/20071030084528im_/http://www.brucemo.com/compchess/programming/alphabeta.htm) is an improvement on minimax by pruning useless chunks off the search tree. OP's question is that, after this whole process has occurred and the "best move" has been found and returned from `alphaBeta()`, he doesn't know how to turn the resulting `Node` into a move he can make on the board. That's the question. – MultiplyByZer0 Aug 04 '19 at 19:33

1 Answers1

0

You cannot get the move information out of the returned bestMove because that Node represents the position of the board after depth moves. If you diff bestMove's position and initial's position, you will find multiple differences, and you won't be able to tell what order the moves were played in.

To get the move to play out of your search code:

  1. Add a boolean isRoot parameter to max() to tell the method whether it is called directly from alphaBeta() and n is the search tree's root node.
  2. In max(), if isRoot is true, instead of keeping track of the best temp (the Node returned from min()) for currentBestMove, keep track of the best s (the Node from n.generateSuccessors()).
  3. In alphaBeta(), take bestMove (the Node returned from max()) and diff its state array against initial. Find the coordinates of the slot where bestMove has an 'X' and initial doesn't.
  4. That is the move to play.

Code:

private static int[] alphaBeta(Node initial, int depth) {
    Node bestMove = max(initial, depth, NEGATIVE_INFINITY, POSITIVE_INFINITY, true);
    for(int i = 0; i < bestMove.state.length; i++) {
        for(int j = 0; j < bestMove.state[i].length; j++) {
            if(bestMove.state[i][j] != initial.state[i][j]) {
                return new int[] { i, j };
            }
        }
     }
}

private static Node max(Node n, int depth, int alpha, int beta, boolean isRoot) {
    int value = NEGATIVE_INFINITY;
    Node currentBestMove = null;
    Node temp = null;

    // Terminal state
    if(n.fourInALine() != 0) {
        return n;
    }

    // Depth limit reached
    if(depth == 0) {
        return n;
    }

    ArrayList<Node> successors = n.generateSuccessors('X');
    // Iterate through all the successors, starting with best evaluationValues
    for(Node s : successors) {
        temp = min(s, depth - 1, alpha, beta);
        if(temp.evaluationVal > value) {
            value = temp.evaluationVal;
            currentBestMove = isRoot ? s : temp;
        }
        alpha = Math.max(alpha, value);
        if(alpha >= beta) {
            break;
        }
    }
    return currentBestMove;
}

// I have a similar min() method with the opposite comparison,
// and without an isRoot argument.

Note that none of this is tested, at all.

MultiplyByZer0
  • 6,302
  • 3
  • 32
  • 48
  • Thank you a bunches! I will look into this more in depth after I try to finish up an alternate method I'm testing. – Amai Aug 04 '19 at 21:59