1

I deal with that for a week now.

I have constructed a tree of gameboards and I evaluated them - > every node hasint evaluation field on what basis I choose the right move? I cannot get it from any tutorials, youtube video. The algorithm seems to be useless.

In my evaluation method: 1 cross/circle inline 10^0 points, 2 crosses/circles in line 10^1 points,... n crosses/circles in line 10^(n-1) points. Is it + or - 10^(n-1) points depends on a level of tree.

I put cross on a top left field my stupid algorithm thinks this is the best possible track for computer player(CIRCLE):

[O| | |
------
 | | |
------
 | | |

Evaluation -1
TotalEvaluation -1
Dobry  X
, O|X| |
------
 | | |
------
 | | |

Evaluation 0
TotalEvaluation -1
Dobry  X
, O|X| |
------
O| | |
------
 | | |

Evaluation -11
TotalEvaluation -12
Dobry  X
, O|X| |
------
O| | |
------
X| | |

Evaluation 10
TotalEvaluation -2
Dobry  X
, O|X| |
------
O|O| |
------
X| | |

Evaluation -31
TotalEvaluation -33
Dobry  X
, O|X| |
------
O|O| |
------
X| |X|

Evaluation 30
TotalEvaluation -3
Dobry  X
]

If you understand any of it please try to explain.

This is my Node:

package tictactoe;

import java.util.ArrayList;

import static tictactoe.Field.*;

public class Node {
    static int KEY = 0;
    int level, key, evaluation;
    int[][] board;
    ArrayList<Node> children;
    int stamp;
    int totalEval;
    Node parent;

    Node(int[][] board, int level, int stamp, Node parent) {
        this.board = board;
        this.level = level;
        this.stamp = stamp;
        this.parent = parent;
        totalEval = 0;
        children = new ArrayList<Node>();
        key = KEY++;
        setEvaluation();
        if(parent != null)
            totalEval = parent.totalEval + evaluation;
        else
            totalEval = evaluation;

    }

    public void setEvaluation() {
        int evaluation = 0;
        int howManyInLine = board.length;
        int opponentSign = CIRCLE;
        if(stamp == CIRCLE)
            opponentSign = CROSS;
        for(; howManyInLine > 0; howManyInLine--) {
            if(level % 2 == 0) {
                evaluation += countInlines(stamp, howManyInLine);
                evaluation -= countInlines(opponentSign, howManyInLine);
            } else {
                evaluation -= countInlines(stamp, howManyInLine);
                evaluation += countInlines(opponentSign, howManyInLine);
            }
        }
        this.evaluation = evaluation;
    }

    public int countInlines(int sign, int howManyInLine) {
        int points = (int) Math.pow(10, howManyInLine - 1);
        int postiveCounter = 0;
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board[i].length; j++) {
                //czy od tego miejsca jest cos po przekatnej w prawo w dol, w lewo w dol, w dol, w prawo
                if(toRigth(i, j, sign, howManyInLine))
                    postiveCounter++;
                if(howManyInLine > 1) {
                    if(toDown(i, j, sign, howManyInLine))
                        postiveCounter++;
                    if(toRightDiagonal(i, j, sign, howManyInLine))
                        postiveCounter++;
                    if(toLeftDiagonal(i, j, sign, howManyInLine))
                        postiveCounter++;
                }
            }
        }
        return points * postiveCounter;
    }

    public boolean toRigth(int i, int j, int sign, int howManyInLine) {
        for(int start = j; j < start + howManyInLine; j++)
            if(j >= board.length || board[i][j] != sign)
                return false;
        return true;
    }

    public boolean toDown(int i, int j, int sign, int howManyInLine) {
        for(int start = i; i < start + howManyInLine; i++)
            if(i >= board.length || board[i][j] != sign)
                return false;
        return true;
    }

    public boolean toRightDiagonal(int i, int j, int sign, int howManyInLine) {
        int startJ = j;
        for(int start = i; i < start + howManyInLine; i++, j++)
            if(i >= board.length || j >= board.length || board[i][j] != sign)
                return false;
        return true;
    }

    public boolean toLeftDiagonal(int i, int j, int sign, int howManyInLine) {
        int startJ = j;
        for(int start = i; i < start + howManyInLine; i++, j--)
            if(i >= board.length || j < 0 || board[i][j] != sign)
                return false;
        return true;
    }

    public boolean gameOver() {
        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board.length; j++) {
                if(toRigth(i, j, CROSS, board.length))
                    return true;
                if(toDown(i, j, CROSS, board.length))
                    return true;
                if(toRightDiagonal(i, j, CROSS, board.length))
                    return true;
                if(toLeftDiagonal(i, j, CROSS, board.length))
                    return true;
                if(toRigth(i, j, CIRCLE, board.length))
                    return true;
                if(toDown(i, j, CIRCLE, board.length))
                    return true;
                if(toRightDiagonal(i, j, CIRCLE, board.length))
                    return true;
                if(toLeftDiagonal(i, j, CIRCLE, board.length))
                    return true;
            }
        }

        return false;
    }

    @Override
    public String toString() {
        String s = "";

        for(int i = 0; i < board.length; i++) {
            for(int j = 0; j < board[i].length; j++) {
                if(board[i][j] == CROSS)
                    s += "X";
                if(board[i][j] == CIRCLE)
                    s += "O";
                if(board[i][j] == EMPTY)
                    s += " ";
                s += "|";
            }
            s += "\n";
            if(i < board.length - 1) {
                for(int k = 0; k < board.length * 2; k++)
                    s += "-";
            }
            s += "\n";
        }
        s += "Evaluation " + evaluation + "\n";
        s += "TotalEvaluation " + totalEval + "\n";
        s += "Dobry ";
        if(stamp == CROSS)
            s += " X\n";
        else
            s += " O\n";

        return s;
    }

}

And I construct a tree from that nodes:

package tictactoe;

import static tictactoe.Field.*;

import java.awt.GridLayout;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

public class Tree {
    Node root;
    GameBoard gameBoard;
    Player player;

    public Tree(GameBoard gameBoard, Player player) {
        this.gameBoard = gameBoard;
        this.player = player;
        addNodes();
    }

    public void addNodes() {
        if(root == null)
            root = new Node(toIntArray(gameBoard.getFields()), 0, player.getStamp(), null);
        addChildren(root);
    }

    public void addChildren(Node parent) {
        if(parent.level < 5 && !parent.gameOver()) {
            for(int i = 0; i < parent.board.length; i++) {
                for(int j = 0; j < parent.board.length; j++) {
                    if(parent.board[i][j] == EMPTY) {
                        int[][] copy = hardCopy(parent.board);
                        int stamp = 0;
                        if(parent.level % 2 == 0)
                            stamp = CROSS;
                        else
                            stamp = CIRCLE;

                        copy[i][j] = stamp;
                        Node child = new Node(copy, parent.level + 1, player.getStamp(), parent);
                        System.out.println(stamp);
                        parent.children.add(child);
                        addChildren(child);
                    }
                }
            }
        }
    }

    public int[][] getBestMove() {
        System.out.println("----------");
        ArrayList<Node> childrenList = getBestNode(root, new ArrayList<Node>());
        ArrayList<Node> track = new ArrayList<Node>();
        System.out.println("ROZMIAR: " + childrenList.size());
        Node bestChild = null;
        int max = Integer.MIN_VALUE;
        for(Node node : childrenList)
            if(node.evaluation > max) {
                max = node.evaluation;
                bestChild = node;
            }
        System.out.println("NAJLEPSZY");
        System.out.println(bestChild);

        //znajdowanie przodka
        Node moveToDo = bestChild;
        while (moveToDo.parent.parent != null) {
            track.add(0, moveToDo);
            moveToDo = moveToDo.parent;
        }
        track.add(0, moveToDo);
        track.add(0, moveToDo.parent);
        System.out.println(moveToDo);
        ///
        JFrame jf = new JFrame();
        jf.setLayout(new GridLayout());
        JTextArea jta = new JTextArea(track.toString());
        JScrollPane jsp = new JScrollPane(jta, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        jf.add(jsp);
        jf.setVisible(true);
        jf.setLocation(600, 0);
        jf.pack();

        ////

        return moveToDo.board;
    }

    public ArrayList<Node> getBestNode(Node node, ArrayList<Node> childrenList) {
        for(Node n : node.children) {
            getBestNode(n, childrenList);
            if(n.children.size() == 0)
                childrenList.add(n);
        }
        return childrenList;
    }

    public void print(Node node) {
        System.out.println(node);
        for(Node n : node.children)
            print(n);
    }

    public static int[][] hardCopy(int[][] t) {
        int[][] copy = new int[t.length][t.length];
        for(int i = 0; i < t.length; i++) {
            for(int j = 0; j < t.length; j++) {
                copy[i][j] = t[i][j];
            }
        }
        return copy;
    }
}
H H
  • 263,252
  • 30
  • 330
  • 514
Yoda
  • 17,363
  • 67
  • 204
  • 344
  • 1
    The search space is pretty small - I believe you can just explore all the moves to the end of the game (+1 for win, -1 for loss, 0 for draw). – Bernhard Barker May 31 '14 at 20:47
  • @Dukeling Ok but how select the MOVE? We got leafes -1,0,1 and how to go back in the tree and select the move? – Yoda May 31 '14 at 21:18
  • 1
    Iterate the values up the tree. Assume players move optimally and make each node's value the same as the best choice for that player among its children. So if there's a 1 as a child and it's MAX's move, make that node a 1 as well (since the best move for MAX is the 1 and it leads to a guaranteed win). – Bernhard Barker May 31 '14 at 21:45
  • In practice, you never use MinMax but NegaMax instead. – H H Jun 01 '14 at 08:30
  • @DukelingI did that for 3x3 and it works problem is 4x4 and higher so I did evaluation method. Do you know better evaluation rules(to give points for a situation onboard) please have a look: http://stackoverflow.com/questions/23982531/pretty-good-heuristic-evaluation-rules-for-big-tictactoe-5x5-board – Yoda Jun 01 '14 at 17:38

1 Answers1

1

As described here, the best move is chosen by either maximizing or minimizing the recursively calculated valued of the turn, depending on whether it is the turn of player one or player two. This means that in each evaluation, the algorithm alternatively takes the perspective of the two players and selecting an optimal move. This detail could be implemented by always maximizing the value, multiplying with a factor of 1 or -1 depending on the player, as only values of 1, 0 and -1 can occur for the moves.

Furthermore, pruning can be used; this means that evaluation can be stopped as soon as a move is found which is optimal for the current player, i.e. of value 1 or -1 is found, respectively.

Codor
  • 17,447
  • 9
  • 29
  • 56
  • I did that for 3x3 and it works problem is 4x4 and higher so I did evaluation method. Do you know better evaluation rules(to give points for a situation onboard) please have a look: http://stackoverflow.com/questions/23982531/pretty-good-heuristic-evaluation-rules-for-big-tictactoe-5x5-board – Yoda Jun 01 '14 at 17:38
  • If the evaluation is correct but unacceptably slow, some heuristics could be used. One is pruning via knowing the optimum values, another one is the fact that a winning chain can be completed only by the last piece placed, which means that in the leaves not the entire board has to be evaluated. Was this already taken into account? – Codor Jun 01 '14 at 18:28