我想测试一个通用的极小极大算法,它应该为一个场景返回最佳可能的位置,算法的代码如下:
import java.util.ArrayList;public class AI<E> { public int go = 0; protected int minimax(ArrayList<E> position, int depth, int alpha, int beta, boolean maximizingPlayer) { if (depth == 0 || isGameOver(position)) { return evaluatePosition(position); } if (maximizingPlayer) { int maxEval = Integer.MIN_VALUE; for (ArrayList<E> child : getChildren(position, maximizingPlayer)) { int eval = minimax(child, depth - 1, alpha, beta, false); maxEval = Math.max(maxEval, eval); alpha = Math.max(alpha, eval); if (beta <= alpha) { break; } } return maxEval; } else { int minEval = Integer.MAX_VALUE; for (ArrayList<E> child : getChildren(position, maximizingPlayer)) { int eval = minimax(child, depth - 1, alpha, beta, true); minEval = Math.min(minEval, eval); beta = Math.min(beta, eval); if (beta <= alpha) { break; } } return minEval; }} protected boolean isGameOver(ArrayList<E> position) { return false; } protected int evaluatePosition(ArrayList<E> position) { return 0; } protected ArrayList<ArrayList<E>> getChildren(ArrayList<E> position, boolean player) { return new ArrayList<>(); } public static <T> void copyArrayList(ArrayList<T> source, ArrayList<T> destination) { //destination.clear(); destination.addAll(source); }}
井字游戏代码如下:
import java.util.ArrayList;import java.util.List;import java.util.Random;import java.util.Scanner;public class TicTacToe extends AI<Character> { private final char EMPTY = ' '; private final char PLAYER_X = 'X'; private final char PLAYER_O = 'O'; private final int SIZE = 3; private char currentPlayer = PLAYER_X; private ArrayList<Character> board = new ArrayList<>(SIZE * SIZE); private Scanner scanner = new Scanner(System.in); Random random = new Random(); public TicTacToe() { super(); initializeBoard(); while (true) { printBoard(); if (currentPlayer == PLAYER_X) { playerMove(currentPlayer); } else { botMove(currentPlayer); } if (checkWinner(board, currentPlayer)) { printBoard(); System.out.println("玩家 " + currentPlayer + " 获胜!"); break; } if (checkDraw(board)) { printBoard(); System.out.println("游戏平局!"); break; } currentPlayer = (currentPlayer == PLAYER_X) ? PLAYER_O : PLAYER_X; } } public static void main(String[] args) { new TicTacToe(); } private void initializeBoard() { for (int i = 0; i < SIZE * SIZE; i++) { board.add(EMPTY); } } private void printBoard() { for (int i = 0; i < SIZE; i++) { for (int j = 0; j < SIZE; j++) { System.out.print(board.get(i * SIZE + j)); if (j < SIZE - 1) System.out.print("|"); } System.out.println(); if (i < SIZE - 1) System.out.println("-----"); } } private void playerMove(char player) { while (true) { System.out.println("请为玩家 " + player + " 输入行(0, 1, 2)和列(0, 1, 2):"); int row = scanner.nextInt(); int col = scanner.nextInt(); int index = row * SIZE + col; if (row >= 0 && row < SIZE && col >= 0 && col < SIZE && board.get(index) == EMPTY) { board.set(index, player); break; } else { System.out.println("这个位置已经被占用或超出范围,请重新尝试。"); } } } private void botMove(char player) { int max = Integer.MIN_VALUE; int pos = -1; ArrayList<ArrayList<Character>> children = getChildren(board,false); //andern so das true/ false andert welches zeichen gesetzt wird for (int i = 0; i < children.size(); i++) { int depth = 10; int alpha = Integer.MIN_VALUE; int beta = Integer.MAX_VALUE; boolean maximizingPlayer = true; int result = minimax(children.get(i), depth, alpha, beta, maximizingPlayer); if (result > max) { max = result; pos = i; } } if (pos != -1) { board = children.get(pos); } else { // 如果极小极大算法未找到有效移动,则回退到随机移动 System.out.println("随机"); while (true) { int row = random.nextInt(SIZE); int col = random.nextInt(SIZE); int index = row * SIZE + col; if (board.get(index) == EMPTY) { board.set(index, player); break; } } } } private boolean checkWinner(ArrayList<Character> board, char player) { // 检查水平和垂直行 for (int i = 0; i < SIZE; i++) { // 水平检查 if (board.get(i * SIZE) == player && board.get(i * SIZE + 1) == player && board.get(i * SIZE + 2) == player) { return true; } // 垂直检查 if (board.get(i) == player && board.get(SIZE + i) == player && board.get(2 * SIZE + i) == player) { return true; } } // 检查对角线(从左上到右下) if (board.get(0) == player && board.get(4) == player && board.get(8) == player) { return true; } // 检查对角线(从右上到左下) if (board.get(2) == player && board.get(4) == player && board.get(6) == player) { return true; } return false;} private boolean checkDraw(ArrayList<Character> pos) { return pos.stream().noneMatch(spot -> spot == EMPTY); } @Override protected boolean isGameOver(ArrayList<Character> position) { return checkDraw(position) || checkWinner(position, PLAYER_X) || checkWinner(position, PLAYER_O); } @Override protected int evaluatePosition(ArrayList<Character> position) { char otherPlayer = currentPlayer == PLAYER_X ? PLAYER_O : PLAYER_X; if (checkWinner(position, currentPlayer)) { // 这里有问题,不要忘记!!! return 100000; } else if (checkWinner(position, otherPlayer)) { return -100000; //math.min } else { return 0; } } @Overrideprotected ArrayList<ArrayList<Character>> getChildren(ArrayList<Character> position, boolean player) { ArrayList<ArrayList<Character>> children = new ArrayList<>(); char currentPlayer = player ? PLAYER_X : PLAYER_O; char otherPlayer = currentPlayer == PLAYER_X ? PLAYER_O : PLAYER_X; for (int i = 0; i < SIZE * SIZE; i++) { if (position.get(i) == EMPTY) { ArrayList<Character> child = new ArrayList<>(position); child.set(i, currentPlayer); children.add(child); } } return children;}}
我很确定检查游戏是否结束的代码是正常工作的,而且极小极大算法与许多其他程序中的实现方式相同,所以我猜测在某个地方存在逻辑问题。一个测试它的失败案例:
X|-|- -|-|--|-|-AI:X|-|O -|-|--|-|-
这将导致AI在3步内无论如何都会输。
另外:当我减少算法的深度时,代码甚至无法完成简单任务,如在一步内阻止对方获胜,这可能有助于查找错误。谢谢
回答:
我重写了我的整个代码,现在它可以工作了,我的新极小极大算法:
protected int minimax(ArrayList<E> position, int depth, int alpha, int beta, boolean maximizingPlayer) { if (depth == 0 || isGameOver(position)) { return evaluatePosition(position); } if (maximizingPlayer) { int maxEval = Integer.MIN_VALUE; for (ArrayList<E> child : getChildren(position)) { int eval = minimax(child, depth - 1, alpha, beta, false); maxEval = Math.max(maxEval, eval); alpha = Math.max(alpha, eval); if (beta <= alpha) { break; } } return maxEval; } else { int minEval = Integer.MAX_VALUE; for (ArrayList<E> child : getChildren(position)) { int eval = minimax(child, depth - 1, alpha, beta, true); minEval = Math.min(minEval, eval); beta = Math.min(beta, eval); if (beta <= alpha) { break; } } return minEval; }}
希望这对尝试编写极小极大算法的人有所帮助,我还有一个使用二维数组的版本,如果你需要的话请告诉我