tic tac toe minimax递归引起混乱

问题描述 投票:0回答:1

我使用minmax算法阅读了许多tic tac toe示例,但没有找到一个真正解释正在发生的事情的好例子。我写了一个有效的但需要帮助理解递归过程的一小部分。

如果你加载下面的代码,输入你的名字,O(不是零)和0(零)你将得到我正在看的结果。计算机移动后停止程序并查看console.log。

如果你查看控制台输出,你会看到在return语句之前发生的行best 0 7 [[0, 7]]。下一行来自0 simulateMove = 6 scoreMove[1] = 7 depth = 2

另一个问题是为什么在线423我需要qazxsw poi而不是qazxsw poi

scoreMoveAvailable[i][1] =  simulateMove;
java recursion tic-tac-toe
1个回答
0
投票

请注意,下图假设您没有执行“depth ++”,而只是在递归调用中使用“depth + 1”。前者是代码中的良性错误,导致打印误导深度。当scoreMoveAvailable[i][1] = scoreMove[1];用于查找计算机的移动时,首先调用import java.util.Scanner; import java.util.ArrayList; import java.util.Random; import java.util.Arrays; public class TicTacToeV2{ private static final int boardRowDim = 3; private static final int boardColDim = 3; private String[][] board; private String playerName; private String playerMark; private String computerMark; private boolean humanGoes; private boolean winner; private boolean draw private int gameTargetScore; private boolean output = true; private boolean toSeed = true; private ArrayList<Integer> availableMoves; public TicTacToeV2(String name, boolean whoGoesFirst){ availableMoves = new ArrayList<Integer>(); board = new String[boardRowDim][boardColDim]; for (int i = 0; i < board.length; i++){ for(int j = 0; j < board[0].length; j++){ board[i][j] = ((Integer)(double2single(i,j))).toString(); availableMoves.add(double2single(i,j)); } } playerName = name; humanGoes = whoGoesFirst; playerMark = "X"; computerMark = "O"; gameTargetScore = 15; if(!humanGoes){ playerMark = "O"; computerMark = "X"; gameTargetScore = - 15; } winner = false; draw = false; } public static void main(String[] args)throws Exception{ System.out.println("\u000C"); Scanner kboard = new Scanner(System.in); printHeader(); System.out.print(" Please enter your name ; "); String name = kboard.next(); name = capitalize(name); System.out.print("\n\n X's go first. " + name + ", please enter your mark ('X' or 'O')"); String mark = kboard.next().toUpperCase(); boolean whoPlaysFirst = (mark.equals("X")) ? true : false; TicTacToeV2 myGame = new TicTacToeV2(name,whoPlaysFirst); myGame.playGame(kboard); } public void playGame(Scanner kboard)throws Exception{ Integer move = null; boolean goodMove; String kboardInput = null; Scanner input; int[] cell2D = new int[2]; Random random = new Random(); int nextComputerMove; if(toSeed){ board = seedBoard(); availableMoves = seedAvailable(board); int x = 0; int o = 0; for(int i = 0; i < 3;i++){ for(int j = 0;j < 3;j++){ if(board[i][j].equals("X"))x++; else if(board[i][j].equals("O"))o++; } } if((x - o) == 1) humanGoes = true; else if((x - o) == 0) humanGoes = false; else{ System.out.println("Fatal Error: seed bad"); System.exit(0); } System.out.println("humangoes = " + humanGoes + x + o); } while(!winner && !draw){ printHeader(); goodMove = false; drawBoard(board); if(!humanGoes && availableMoves.size() < 9){ System.out.println("That's a great move, I'll have to think about this"); Thread.sleep(2000); } if(humanGoes){ while(!goodMove){ System.out.print("\n\n Please enter a number for your move : "); kboardInput = kboard.next(); input = new Scanner(kboardInput); if(input.hasNextInt()){ move = input.nextInt(); if(move == 99){ System.out.println("You found the secret exit code"); Thread.sleep(2000); printHeader(); System.out.println("bye"); System.exit(0); } goodMove = checkMove(move); if(!goodMove)System.out.println(" WARNING: Incorrect input, try again"); }else{ System.out.println(" WARNING: Incorrect input, try again"); } } cell2D = single2Double(move); board[cell2D[0]][cell2D[1]] = playerMark; }else{ //nextComputerMove = random.nextInt(availableMoves.size()); //move = availableMoves.get(nextComputerMove); String[][] currentBoard = new String[boardRowDim][boardColDim]; currentBoard = copyBoard(board); ArrayList<Integer> currentAvailableMoves= new ArrayList<Integer>(); currentAvailableMoves = copyAvailableMoves(availableMoves); //System.out.println(System.identityHashCode(currentAvailableMoves)); int[] bestScoreMove = new int[2]; bestScoreMove = findBestMove(currentBoard,currentAvailableMoves,true,0,kboard); move = availableMoves.get(availableMoves.indexOf(bestScoreMove[1])); cell2D = single2Double(move); board[cell2D[0]][cell2D[1]] = computerMark; } humanGoes = humanGoes ? false:true; availableMoves = updateAvailableMoves(move,availableMoves); if (Math.abs(score(board)) == 15) winner = true; if (availableMoves.size() == 0) draw = true; if(winner || draw){ printHeader(); drawBoard(board); } if(score(board) == gameTargetScore)System.out.println(playerName + " you are too good for me. \n" + "Congratulations you won!!\n\n"); else if(score(board) == -gameTargetScore)System.out.println("IWONIWONIWONohboyIWONIWONIWON"); else if(draw)System.out.println("Good game. It's a draw!"); } } public void drawBoard(String[][] someBoard){ String mark = " "; Integer row,col; String type; for( int i = 0;i < 15; i++){ System.out.print(" "); for (int j = 0; j < 27; j++){ mark = " "; if(i==5 || i == 10)mark = "-"; if(j==8 || j == 17)mark = "|"; row = i/5; col = j/9; type = someBoard[row][col]; if(type == "X"){ if( ((i%5 == 1 || i%5 == 3) && (j%9 == 3 || j%9 == 5)) || (i%5 == 2 && j%9 == 4))mark = "X"; }else if(type == "O"){ if( ((i%5 == 1 || i%5 == 3) && (j%9 == 3 || j%9 == 4 || j%9 == 5)) || ((i%5 == 2) && (j%9 == 3 || j%9 == 5))) mark = "O"; }else{ if( i%5 == 2 && j%9 == 4){ mark = ((Integer)(row * 3 + col)).toString(); } } System.out.print(mark); } System.out.println(); } System.out.println("\n\n\n"); } public boolean checkMove(Integer move){ /* * to sanitize user input we have to check if what * they entered is an available square */ boolean goodMove = false; for(Integer available : availableMoves){ if (available == move) goodMove = true; } return goodMove; } public int score(String[][] newBoard){ int row; int newCol; int score = 0; for (int strategy = 0; strategy < 8; strategy++){ score = 0; for (int col = 0; col < 3; col++){ if(strategy < 3){ //rows row = strategy ; newCol = col; }else if (strategy < 6){ //cols row = col; newCol = strategy - 3; }else{//diag int diag = strategy - 6; row = col - 2 * diag * (col - 1); newCol = col; } if(newBoard[row][newCol].equals("X")){ score+=5; }else if(newBoard[row][newCol].equals("O")){ score+=-5; } } score = (Math.abs(score)== 15) ? score : 0; if(Math.abs(score) == 15) break; } return score; } public String[][] copyBoard(String[][] originalBoard){ String[][] duplicateBoard = new String[boardRowDim][boardColDim]; for (int i = 0;i < boardRowDim; i++){ for(int j = 0; j < boardColDim; j++){ duplicateBoard[i][j] = originalBoard[i][j]; } } return duplicateBoard; } public String[][] updateBoard(Integer move, String mark, String[][]oldBoard){ String[][] currentBoard = new String[boardRowDim][boardColDim]; int[] cell2D = new int[2]; currentBoard = copyBoard(oldBoard); cell2D = single2Double(move); currentBoard[cell2D[0]][cell2D[1]] = mark; return currentBoard; } public ArrayList<Integer> copyAvailableMoves(ArrayList<Integer> originalAvailableMoves){ ArrayList<Integer> duplicateAvailableMoves = new ArrayList<Integer>(); for(int i = 0; i < originalAvailableMoves.size();i++){ duplicateAvailableMoves.add(originalAvailableMoves.get(i)); } return duplicateAvailableMoves; } public ArrayList<Integer> updateAvailableMoves(Integer move, ArrayList<Integer> oldAvailableMoves){ ArrayList<Integer> currentAvailableMoves = new ArrayList<Integer>(); currentAvailableMoves = copyAvailableMoves(oldAvailableMoves); currentAvailableMoves.remove(move); return currentAvailableMoves; } public String[][] seedBoard(){ String[][] sampleBoard ={{"0","O","X"},{"X","4","O"},{"6","7","X"}}; //String[][] sampleBoard ={{"X","O","O"},{"3","4","X"},{"6","7","8"}}; return sampleBoard; } public ArrayList<Integer> seedAvailable(String[][] seedBoard){ ArrayList seedMoves = new ArrayList<Integer>(); int index = -1; for(int i = 0; i < 3;i++){ for (int j = 0; j < 3; j++){ if(!seedBoard[i][j].equals("X") && !seedBoard[i][j].equals("O")){ index = i*3 + j; seedMoves.add(index); } } } //System.out.println(seedMoves); return seedMoves; } public int[] findBestMove(String[][] currentBoard, ArrayList<Integer> currentAvailableMoves,boolean currentComputerMoves,int depth,Scanner kboard){ ArrayList<Integer> simulateAvailableMoves = new ArrayList<Integer>(); String[][] simulateBoard = new String[boardRowDim][boardColDim]; //System.out.println(System.identityHashCode(currentAvailableMoves)); int[] scoreMove = new int[2]; //return array with score and associated move int[] cell2D = new int[2]; //array holding i and j of board to place Mark (X or O) int computerTargetScore = (computerMark.equals("X")) ? 15:-15; /* * scoreMoveAvailable is an array that holds scores for each available move * inside loop. The bestScoreMove will be the min or max of this array * depending on whether it's X's or O's turn to move */ int[][] scoreMoveAvailable = new int[currentAvailableMoves.size()][2]; Integer simulateMove = null; //current move inside loop Boolean simulateComputerMoves = null; for(int i = 0; i < currentAvailableMoves.size(); i++){ scoreMoveAvailable[i][0] = 0; //score scoreMoveAvailable[i][1] = -1; // square 0 - 8 } if(output)System.out.println("on enter available moves " + currentAvailableMoves); for (int i = 0; i < currentAvailableMoves.size() ;i++){ simulateAvailableMoves = copyAvailableMoves(currentAvailableMoves); simulateBoard = copyBoard(currentBoard); simulateComputerMoves = currentComputerMoves; if(output)System.out.println("in loop available moves " + i + " " + simulateAvailableMoves); simulateMove = simulateAvailableMoves.get(i); simulateAvailableMoves = updateAvailableMoves(simulateMove,simulateAvailableMoves); cell2D = single2Double(simulateMove); if(simulateComputerMoves){ if(output)System.out.println("computer moves " + simulateMove); simulateBoard[cell2D[0]][cell2D[1]] = computerMark; simulateComputerMoves = false; if(score(simulateBoard) == computerTargetScore || simulateAvailableMoves.size() == 0){ scoreMove[0] = score(simulateBoard); scoreMove[1] = simulateMove; if(output)System.out.println("score computer" + Arrays.toString(scoreMove) +" computer moves = " + simulateMove + " i = " + i); if(output)drawBoard(simulateBoard); }else{ depth++; if(output)System.out.println("computer calling findbest " +simulateAvailableMoves); if(output)drawBoard(simulateBoard); scoreMove = findBestMove(simulateBoard,simulateAvailableMoves,simulateComputerMoves,depth,kboard); } }else{ if(output)System.out.println("human moves" + simulateMove); simulateBoard[cell2D[0]][cell2D[1]] = playerMark; simulateComputerMoves = true; if(score(simulateBoard) == (-computerTargetScore) || simulateAvailableMoves.size() == 0){ scoreMove[0] = score(simulateBoard); scoreMove[1] = simulateMove; if(output)System.out.println("score human "+ Arrays.toString(scoreMove) +" human moves " + simulateMove + " i = " + i); if(output)drawBoard(simulateBoard); }else{ depth++; if(output)System.out.println("human calling findbest " + simulateAvailableMoves); if(output)drawBoard(simulateBoard); scoreMove = findBestMove(simulateBoard,simulateAvailableMoves,simulateComputerMoves,depth,kboard); } } if(output)System.out.println(i + " simulateMove = " + simulateMove + " scoreMove[1] = " + scoreMove[1] + " depth = " + depth); // drawBoard(simulateBoard); scoreMoveAvailable[i][0] = scoreMove[0] ; scoreMoveAvailable[i][1] = simulateMove; if(output)System.out.println("score array = " + i + " " + Arrays.deepToString(scoreMoveAvailable)); } int[] bestScoreMove = new int[2]; bestScoreMove[0] = scoreMoveAvailable[0][0]; //set bestScoreMove to first element in arraylist bestScoreMove[1] = scoreMoveAvailable[0][1]; if(output)System.out.println("****************************************"); if( (currentComputerMoves && computerMark.equals("X") ) || (!currentComputerMoves && computerMark.equals("O") ) ) { for (int i = 0; i < scoreMoveAvailable.length;i++){ if(scoreMoveAvailable[i][0] > bestScoreMove[0]){ bestScoreMove[0] = scoreMoveAvailable[i][0] ; bestScoreMove[1] = scoreMoveAvailable[i][1]; } if(output)System.out.printf("MAX X scores and moves = %d %d %d %s\n",i,scoreMoveAvailable[i][0],scoreMoveAvailable[i][1],"XXX"); } if(output)System.out.println("\n"); }else{ for (int i = 0; i < scoreMoveAvailable.length;i++){ if(scoreMoveAvailable[i][0] < bestScoreMove[0]){ bestScoreMove[0] = scoreMoveAvailable[i][0] ; bestScoreMove[1] = scoreMoveAvailable[i][1]; } if(output)System.out.printf("MIN O scores and moves =%d %d %d %s\n",i,scoreMoveAvailable[i][0],scoreMoveAvailable[i][1],"OOO"); } if(output)System.out.println("\n"); } if(output)System.out.println("best " + bestScoreMove[0] + " " + bestScoreMove[1] + " " + Arrays.deepToString(scoreMoveAvailable)); return bestScoreMove; } /* * just some static methods to help make things easy */ public static void printHeader(){ System.out.println("u000C Welcome to TicTacToe\n" + " where you can match wits\n" + " against the computer\n" + "(the real challenge is making it a draw)\n"); } public static int double2single(int row, int col){ int singleCell = 0; singleCell = boardRowDim * row + col; return singleCell; } public static int[] single2Double(int cell){ int[] cell2D = new int[2]; cell2D[0] = cell / boardColDim; cell2D[1] = cell % boardColDim; return cell2D; } public static String capitalize(String word){ word = word.substring(0,1).toUpperCase() + word.substring(1); return word; } } enter image description here。根据种子板和人类取“0”,现在可用的动作是4,6和7。

  1. 首先,计算机模拟移动4
  2. 这会创建一个递归调用,向函数堆栈添加一个帧。人类现在有6个和7个可能的动作。由于6是第一个,人类首先“尝试”6。现在在这个模拟中,它是计算机/ 4 - >人/ 6
  3. 同样,一个帧被添加到函数堆栈中,用于来自人类“运行模拟”的递归调用。现在,还有一个动作:计算机需要7个。这就是打印findBestMove的地方。
  4. 在最后一次递归调用返回之后,弹出函数堆栈,我们再次在人类移动的位置6,这就是打印bestScoreMove = findBestMove...的地方。

您可以使用调试器来显示有关堆栈的更多详细信息,但这里有一些额外的建议(主要是为了使函数更短)以便于调试:

  • best 0 7 [[0, 7]]之后有很多0 simulateMove = 6..。您可以将这两行包装在一个函数中
  • 在许多但不是所有地方,逻辑假设电路板尺寸为3×3,例如Thread.sleep(2000);。这是不一致的,这种逻辑可能令人困惑。为了清楚起见,您可以创建一个函数“noMovesMade()”,根据可用变量计算它。
  • 此外,println可以简单地进入“其他”块(因为这已经暗示它是计算机的转向)
  • && availableMoves.size() < 9)假设当其余代码没有假设时,人类正在播放“O”。
  • 有很多情况下变量被初始化,分配,然后重新分配而不被使用。例如,if(!humanGoes && availableMoves.size() < 9){是多余的,与if((x - o) == 1) humanGoes = true;相同。
  • String[][] currentBoard = new String[boardRowDim][boardColDim]; currentBoard = copyBoard(board);可用于复制数组(例如,在bestScoreMove的内环中)
  • 可以使用Arrays.copyOf而不是在copyBoard中遍历数组
  • ArrayList.contains可缩短为checkMove
© www.soinside.com 2019 - 2024. All rights reserved.