我有以下java mtdf A.I.的代码。这是行不通的。这是一个tic tac toe计划。没有错误,但AI正在选择错误的位置。 职位如下。
00 01 02
10 11 12
20 21 22
如果我选择22
作为我的选项,AI应选择11
作为最佳解决方案。但它选择了错误的价值。代码在Board.test()
方法中。
有人可以告诉我是否做错了什么?这是我的代码:
package com.tictactoe;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
class Point {
int x, y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "[" + x + ", " + y + "]";
}
}
class PointsAndScores {
int score;
Point point;
PointsAndScores(int score, Point point) {
this.score = score;
this.point = point;
}
public PointsAndScores() {
if(point==null)point=new Point(0,0);
}
@Override
public String toString() {
return "[" + point.x + ", " + point.y + "],Score:"+score;
}
}
class Board {
int lowestDepth = 0;
Integer hashValue = new Integer(0);
PointsAndScores bestMove = new PointsAndScores();
List<Point> availablePoints;
Scanner scan = new Scanner(System.in);
int[][] board = new int[3][3];
int[][] tempBoard = new int[3][3];
public boolean isGameOver() {
//Game is over is someone has won, or board is full (draw)
return (hasXWon() || hasOWon() || this.getAvailablePoints(board).isEmpty());
}
public boolean hasXWon() {
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 1) || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 1)) {
//System.out.println("X Diagonal Win");
return true;
}
for (int i = 0; i < 3; ++i) {
if (((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 1)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 1))) {
// System.out.println("X Row or Column win");
return true;
}
}
return false;
}
public boolean hasOWon() {
if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[0][0] == 2) || (board[0][2] == board[1][1] && board[0][2] == board[2][0] && board[0][2] == 2)) {
// System.out.println("O Diagonal Win");
return true;
}
for (int i = 0; i < 3; ++i) {
if ((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] == 2)
|| (board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] == 2)) {
// System.out.println("O Row or Column win");
return true;
}
}
return false;
}
public List<Point> getAvailablePoints(int[][] array) {
availablePoints = new ArrayList<>();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (array[i][j] == 0) {
availablePoints.add(new Point(i, j));
}
}
}
return availablePoints;
}
public int[][] placeAMove(Point point, int player) {
board[point.x][point.y] = player; //player = 1 for X, 2 for O
return board;
}
void takeHumanInput() {
System.out.println("Your move: ");
int x = scan.nextInt();
int y = scan.nextInt();
Point point = new Point(x, y);
placeAMove(point, 2);
}
public void displayBoard() {
System.out.println();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
System.out.print(board[i][j] + " ");
}
System.out.println();
}
}
public void resetBoard() {
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
board[i][j] = 0;
}
}
}
public int evaluateBoard() {
int score = 0;
//Check all rows
for (int i = 0; i < 3; ++i) {
int blank = 0;
int X = 0;
int O = 0;
for (int j = 0; j < 3; ++j) {
if (board[i][j] == 0) {
blank++;
} else if (board[i][j] == 1) {
X++;
} else {
O++;
}
}
score+=changeInScore(X, O);
}
//Check all columns
for (int j = 0; j < 3; ++j) {
int blank = 0;
int X = 0;
int O = 0;
for (int i = 0; i < 3; ++i) {
if (board[i][j] == 0) {
blank++;
} else if (board[i][j] == 1) {
X++;
} else {
O++;
}
}
score+=changeInScore(X, O);
}
int blank = 0;
int X = 0;
int O = 0;
//Check diagonal (first)
for (int i = 0, j = 0; i < 3; ++i, ++j) {
if (board[i][j] == 1) {
X++;
} else if (board[i][j] == 2) {
O++;
} else {
blank++;
}
}
score+=changeInScore(X, O);
blank = 0;
X = 0;
O = 0;
//Check Diagonal (Second)
for (int i = 2, j = 0; i > -1; --i, ++j) {
if (board[i][j] == 1) {
X++;
} else if (board[i][j] == 2) {
O++;
} else {
blank++;
}
}
score+=changeInScore(X, O);
return score;
}
private int changeInScore(int X, int O){
int change;
if (X == 3) {
change = 100;
} else if (X == 2 && O == 0) {
change = 10;
} else if (X == 1 && O == 0) {
change = 1;
} else if (O == 3) {
change = -100;
} else if (O == 2 && X == 0) {
change = -10;
} else if (O == 1 && X == 0) {
change = -1;
} else {
change = 0;
}
return change;
}
private String getHashCode(int[][] board){
String strHash="";
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
strHash = strHash+board[i][j];
}
}
return strHash;
}
private TableEntry test(int[][] board,int maxDepth,int currentDepth,int gamma){
MTDTicTacToe.LOGGER.log(Level.INFO, "test() started.maxDepth:"+maxDepth+",currentDepth:"+currentDepth+",gamma:"+gamma);
int currentScore = 0;
if(currentDepth>lowestDepth){
lowestDepth=currentDepth;
}
TableEntry entry = MTDTicTacToe.mapTranspositionTable.get(getHashCode(board));
if(entry==null){
entry = new TableEntry();
entry.bestMove = new PointsAndScores();
}
if(entry!=null && entry.depth>maxDepth-currentDepth){
// Early outs for stored positions
if(entry.minScore>gamma){
entry.bestMove.score=entry.minScore;
return entry;
}else if(entry.maxScore<gamma){
entry.bestMove.score=entry.maxScore;
return entry;
}
}else{//we need to create the entry
entry.hashValue = getHashCode(board);
entry.depth = maxDepth - currentDepth;
entry.minScore = Integer.MIN_VALUE;
entry.maxScore = Integer.MAX_VALUE;
// Now we have the entry, we can get on with the test
}
MTDTicTacToe.mapTranspositionTable.put(getHashCode(board),entry);
//Check if we’re done recursing
if(this.isGameOver() || currentDepth == maxDepth){
entry.minScore = entry.maxScore = this.evaluateBoard();
entry.bestMove.score=entry.minScore;
this.bestMove.point = entry.bestMove.point;
return entry;
}
// Now go into bubbling up mode
bestMove = new PointsAndScores();
bestMove.score=Integer.MIN_VALUE;
int[][] newBoard=new int[3][3];
MTDTicTacToe.copyArrayUtil(board,newBoard);
TableEntry entry2;
for(Point point:this.getAvailablePoints(newBoard)){
newBoard[point.x][point.y]=1;
entry2 = test(newBoard,maxDepth,currentDepth+1,-gamma);
currentScore = - entry2.bestMove.score;
//Update the best score
if(currentScore > bestMove.score){// Track the current best move
entry.bestMove.point = point ;
entry.bestMove.score = currentScore;
bestMove.score = currentScore;
bestMove.point = point;
}
//MTDTicTacToe.LOGGER.log(Level.INFO,"ENTRY####2:"+entry2);
}
//If we pruned, then we have a min score, otherwise we have a max score.
if(bestMove.score< gamma){
entry.maxScore = bestMove.score;
} else{
entry.minScore = bestMove.score;
}
//MTDTicTacToe.LOGGER.log(Level.INFO,"tableEntries:"+MTDTicTacToe.mapTranspositionTable);
MTDTicTacToe.LOGGER.log(Level.INFO, "test() Ended.maxDepth:"+maxDepth+",currentDepth:"+currentDepth+",gamma:"+gamma+",entry:"+entry);
//Store the entry and return the best score and move.
return entry;
}
private TableEntry mtd(int maxDepth,int guess){
int MAX_ITERATIONS = 1;//10;
TableEntry entry=null;
for(int i=0;i<MAX_ITERATIONS;i++){
int gamma = guess;
entry = test(board,maxDepth,0,gamma-1);
guess = entry.bestMove.score;
if(gamma==guess){break;}
}
return entry;
}
public TableEntry mtdf(int maxDepth){
maxDepth=3;
TableEntry entry=null;
int guess=0;
for(int depth=2;depth<maxDepth;depth++){
entry = mtd(depth,guess);
//TODO if(timeout)break;
}
return entry;
}
public void placeAMove(Point point) {
board[point.x][point.y] = 1; //player = 1 for X, 2 for O
}
}
class TableEntry{
String hashValue;
int minScore;
int maxScore;
PointsAndScores bestMove;
int depth;
@Override
public String toString() {
return "hashValue:" + hashValue +",minscore:"+minScore+",maxscore:"+maxScore+ ",bestMove:" + bestMove + ",depth:"+depth+"\n";
}
}
public class MTDTicTacToe {
public static Map<String,TableEntry> mapTranspositionTable = new HashMap<String,TableEntry>();
static final Logger LOGGER = Logger.getLogger(MTDTicTacToe.class.getName());
public static void main(String[] args) throws Exception{
Handler fileHandler = new FileHandler("./MTDTicTacToe.log");
LOGGER.addHandler(fileHandler);
fileHandler.setLevel(Level.ALL);
LOGGER.setLevel(Level.ALL);
Board b = new Board();
Random rand = new Random();
b.displayBoard();
System.out.println("Who's gonna move first? (1)Computer (2)User: ");
int choice = b.scan.nextInt();
if (choice == 1) {
Point p = new Point(rand.nextInt(3), rand.nextInt(3));
b.placeAMove(p, 1);
b.displayBoard();
}
int maxDepth=10;
TableEntry entry=null;
while (!b.isGameOver()) {
System.out.println("Your move: ");
Point userMove = new Point(b.scan.nextInt(), b.scan.nextInt());
b.placeAMove(userMove, 2); //2 for O and O is the user
b.displayBoard();
if (b.isGameOver()) break;
copyArrayUtil(b.board,b.tempBoard);
entry=b.mtdf(maxDepth);
copyArrayUtil(b.tempBoard,b.board);
b.placeAMove(entry.bestMove.point);//move player 1 - computer
b.displayBoard();
}
if (b.hasXWon()) {
System.out.println("Unfortunately, you lost!");
} else if (b.hasOWon()) {
System.out.println("You win!");
} else {
System.out.println("It's a draw!");
}
LOGGER.log(Level.INFO, "Tic tac toe game Ended.");
}
public static void copyArrayUtil(int[][] t1,int[][] t2){
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
t2[i][j] = t1[i][j];
}
}
}
}