【问题标题】:Tic-tac-toe A.I. in Java using minimax doesn't work井字游戏 A.I.在 Java 中使用 minimax 不起作用
【发布时间】:2015-06-28 12:15:26
【问题描述】:

我正在尝试构建人工智能。井字游戏使用 minimax 算法,但它返回奇怪的动作 - 有时它只返回我认为第一个可用的动作(左上、中上、右上......),但其他时候它只是返回看起来像“随机”移动。但据我所知,这些动作并不是随机的,而且 A.I.在相同的条件下播放总是相同的。

当有 2 名人类玩家时,游戏才有效。整个游戏很大,所以我创建了AITest 类,它应该显示行为的重要部分。它让两个 A.I.玩家玩并打印棋盘。请注意,这是一个单独的类,不是根据最佳实践编写的,并且被大大简化(不检查是否获胜......)。但很明显,A.I.很傻。

感谢您的建议。

package tic_tac_toe;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class AITest {

    public enum Tile {
        EMPTY, O, X;
    }

    // [row][column]
    private static Tile[][] board;
    private static int moveCount = 0;

    private Tile mySeed;
    private Tile opponentSeed;

    public static void main(String[] args) {
        board = new Tile[3][3];
        for (int y = 0; y < board.length; y++) {
            for (int x = 0; x < board.length; x++) {
                board[y][x] = Tile.EMPTY;
            }
        }

        AITest ai = new AITest(Tile.X);
        AITest opponent = new AITest(Tile.O);
        // simulate some moves
        for (int i = 0; i < 15; i++) {
            checkReset();
            int[] move = ai.getNextMove(board);
            board[move[0]][move[1]] = Tile.X;
            printBoard();

            checkReset();
            int[] opponentMove = opponent.getNextMove(board);
            board[opponentMove[0]][opponentMove[1]] = Tile.O;
            printBoard();
        }
    }

    private static void checkReset() {
        moveCount++;
        if (moveCount == 9) {
            for (int y = 0; y < board.length; y++) {
                for (int x = 0; x < board.length; x++) {
                    board[y][x] = Tile.EMPTY;
                }
            }
            moveCount = 0;
        }

    }

    private static void printBoard() {
        StringBuilder sb = new StringBuilder();
        sb.append("-----------------------\n");
        Map<Tile, String> map = new HashMap<>();
        map.put(Tile.EMPTY, "e");
        map.put(Tile.O, "O");
        map.put(Tile.X, "X");
        for (Tile[] row : board) {
            for (Tile t : row) {

                sb.append(map.get(t) + "|");
            }
            sb.append("\n");
        }
        System.out.println(sb.toString());
    }

    public AITest(Tile seed) {
        mySeed = seed;
        opponentSeed = (mySeed == Tile.X) ? Tile.O : Tile.X;
    }

    public int[] getNextMove(Tile[][] board) {
        int[] result = minimax(board, mySeed);
        // row, column
        return new int[] { result[1], result[2] };
    }

    private int[] minimax(Tile[][] board, Tile player) {
        // mySeed is maximizing, opponentSeed is minimizing
        int bestScore = (player == mySeed) ? Integer.MIN_VALUE
                : Integer.MAX_VALUE;
        int currentScore;
        int bestRow = -1;
        int bestCol = -1;

        List<Point> possibleMoves = getPossibleMoves(Arrays.copyOf(board,
                board.length));
        if (possibleMoves.isEmpty()) {
            bestScore = getScore(board, player);
        } else {
            for (Point move : possibleMoves) {
                // try the move
                board[move.y][move.x] = player;
                if (player == mySeed) {
                    currentScore = minimax(board, opponentSeed)[0];
                    if (currentScore > bestScore) {
                        bestScore = currentScore;
                        bestRow = move.y;
                        bestCol = move.x;
                    }
                } else {
                    currentScore = minimax(board, mySeed)[0];
                    if (currentScore < bestScore) {
                        bestScore = currentScore;
                        bestRow = move.y;
                        bestCol = move.x;
                    }
                }
                // undo the move
                board[move.y][move.x] = Tile.EMPTY;
            }
        }
        return new int[] { bestScore, bestRow, bestCol };
    }

    private List<Point> getPossibleMoves(Tile[][] board) {
        List<Point> possibleMoves = new ArrayList<>();
        for (int y = 0; y < board.length; y++) {
            for (int x = 0; x < board.length; x++) {
                if (board[y][x] == Tile.EMPTY) {
                    possibleMoves.add(new Point(x, y));
                }
            }
        }
        return possibleMoves;
    }

    private int getScore(Tile[][] board, Tile player) {
        if (isWinner(board, mySeed)) {
            return 1;
        }
        if (isWinner(board, opponentSeed)) {
            return -1;
        }
        return 0;
    }

    private boolean isWinner(Tile[][] board, Tile player) {
        // rows
        for (int i = 0; i < board.length; i++) {
            if (checkLine(player, board[i])) {
                return true;
            }
        }
        // columns
        for (int i = 0; i < board.length; i++) {
            if (checkLine(player, board[0][i], board[1][i], board[2][i])) {
                return true;
            }
        }
        // diagonals
        return checkLine(player, board[0][0], board[1][1], board[2][2])
                || checkLine(player, board[0][2], board[1][1], board[2][0]);
    }

    private boolean checkLine(Tile player, Tile... line) {
        for (Tile tile : line) {
            if (tile != player) {
                return false;
            }
        }
        return true;
    }
}

【问题讨论】:

  • 请问你能比“返回奇怪的动作”更具体吗?
  • @AndyTurner 已编辑 - 见第一段。
  • 你能举一些“随机”动作的例子吗?您期望什么,它会做什么?
  • @AndyTurner 我从(中、中)人工智能开始。进入(顶部,左侧) - 到目前为止可能还可以。然后我移动(中,左)和 A.I.移动(顶部,右侧),而它应该阻止 - i。 e.走(中,右)。
  • 你曾经战胜过人工智能吗?你实施了吗?

标签: java tic-tac-toe minimax


【解决方案1】:

MinMax 非常依赖于有一个好的评估函数(这个函数给一个棋盘“打分”,告诉你它有多好)。你的评价函数应该如下:

  1. 如果比赛获胜,获胜者将获得很多积分。赢得比赛后,一切都不重要了。
  2. 否则,将部分功劳归于占据可用于更多获胜动作的图块。例如,中心牌可以用于4个获胜位置,3个角,2个边。因此,如果您有选择,而对手无法单赢,则中心牌更好。

你当前的评估函数被破坏了,因为它只实现了#1,并且只有在板子满时。要看到很大的改进,请在minimax() 中检查任何一方的胜利在检查可用动作之前,并且如果任何一方获胜,返回@987654322 的值@。

【讨论】:

  • 我曾经忘记在国际象棋 AI 中添加类似的“世界末日”检查。然而,国王的身价仍然很高。结果是一盘人工智能认为“抓住你的国王是可以的,只要你也能抓住他们的国王”:-P
  • 我知道已经有一段时间了,但是您的回答帮助我更多地了解了 minimax 算法以及评估部分的实际重要性,所以谢谢 :D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多