我正在努力建立A.I.对于使用minimax算法的Tic-tac-toe但它返回奇怪的动作 - 有时它只返回我认为的第一个可用的移动(左上,上中,右上......),但有时它只返回看起来像“随机“移动。但据我所知,这些动作并非随机而且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;
}
}
答案 0 :(得分:3)
MinMax非常依赖于具有良好的评估功能(“评分”电路板的功能,告诉您它有多好)。您的评估功能应如下:
您当前的评估功能已被破坏,因为它仅实现#1,而仅在电路板已满时实现。要查看一个很好的改进,请在检查minimax()
中的可用移动之前检查双方的胜利,如果任何一方获胜,请检查,返回值为getScore()
。