我使用minmax算法阅读了许多井字游戏示例,但没有找到一个能真正说明正在发生什么的好的示例。我写了一个可行的方法,但需要帮助您了解递归过程的一小部分。
如果加载下面的代码,请输入名称O(非零)和0(零),您将得到我正在查看的结果。在计算机移动后,停止程序并查看console.log。
如果您查看控制台输出,则会看到在return语句之前的行best 0 7 [[0, 7]]
。下一行来自0 simulateMove = 6 scoreMove[1] = 7 depth = 2
另一个问题是为什么在第423行上我需要scoreMoveAvailable[i][1] = simulateMove;
而不是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;
}
}
答案 0 :(得分:0)
请注意,下图假定您未执行“ depth ++”,而是在递归调用中仅使用“ depth + 1”。前者是代码中的良性错误,导致打印出误导性深度。
当findBestMove
用于查找计算机移动时,首先调用 bestScoreMove = findBestMove...
。基于播种板和人类取“ 0”,现在可用的移动为4、6和7。
best 0 7 [[0, 7]]
的地方。0 simulateMove = 6..
的地方。您可以使用调试器来显示有关堆栈的更多详细信息,但是这里有一些额外的建议(主要是使函数更短),以便于调试:
Thread.sleep(2000);
之后有很多println
。您可以将这两行包装在函数中&& availableMoves.size() < 9)
。这是不一致的,并且此逻辑可能会造成混淆。为了清楚起见,您可以创建一个函数“ noMovesMade()”,该函数根据可用变量进行计算。if(!humanGoes && availableMoves.size() < 9){
可以直接进入“ else”块(因为这已经意味着轮到计算机了)if((x - o) == 1) humanGoes = true;
假设其他代码未假定人类正在玩“ O”。String[][] currentBoard = new String[boardRowDim][boardColDim]; currentBoard = copyBoard(board);
是冗余的,与bestScoreMove
相同。 Arrays.copyOf
可用于复制数组(例如,在copyBoard
的内部循环中)ArrayList.contains
可以代替迭代checkMove
中的数组使用humanGoes = humanGoes ? false:true;
可以缩写为humanGoes = !humanGoes