我正在研究一种极大极小算法,用于无可匹敌的Tic-Tac-Toe。我需要它既适用于电脑的第一步,也适用于玩家的第一步。在目前的版本中,电脑在第一次使用时永远不会丢失。然而,如果玩家先走,Minimax似乎永远找不到最好的移动(总是返回-1作为分数)。
如果玩家采取第一步,是什么导致计算机的最小最大分数返回为-1?
例子:
board.addMark( 1, Mark2.PLAYER ); // add a 'PLAYER' mark to an arbitrary location
Minimax.minimax( board, Mark2.COMPUTER ); // will always return -1
以下是“极大极小”类:
public class Minimax {
public static int minimax( Board board, Mark2 currentMark ) {
int score = (currentMark == Mark2.COMPUTER) ? -1 : 1;
int[] availableSpaces = board.getAvailableSpaces();
if ( board.hasWinningSolution() )
score = (board.getWinningMark() == Mark2.COMPUTER) ? 1 : -1;
else if ( availableSpaces.length != 0 ) {
Mark2 nextMark = (currentMark == Mark2.COMPUTER) ? Mark2.PLAYER : Mark2.COMPUTER;
for ( int availableIndex = 0; availableIndex < availableSpaces.length; availableIndex++ ) {
board.addMark( availableSpaces[availableIndex], currentMark );
int nextScore = minimax( board, nextMark );
board.eraseMark( availableSpaces[availableIndex] );
if ( currentMark == Mark2.COMPUTER && nextScore > score
|| currentMark == Mark2.PLAYER && nextScore < score )
score = nextScore;
}
}
return score;
}
}
以下是“董事会”课程:
public class Board {
private Mark2 gameBoard[];
private int blankSpaces;
private boolean solutionFound;
private Mark2 winningMark;
public final static int winSets[][] = {
{ 0, 1, 2 }, { 3, 4, 5 },
{ 6, 7, 8 }, { 0, 3, 6 },
{ 1, 4, 7 }, { 2, 5, 8 },
{ 0, 4, 8 }, { 2, 4, 6 }
};
public Board() {
gameBoard = new Mark2[9];
blankSpaces = 9;
for ( int boardIndex = 0; boardIndex < gameBoard.length; boardIndex++ ) {
gameBoard[boardIndex] = Mark2.BLANK;
}
}
public void addMark( int spaceIndex, Mark2 mark ) {
if ( gameBoard[spaceIndex] != mark ) {
gameBoard[spaceIndex] = mark;
if ( mark == Mark2.BLANK ) {
blankSpaces++;
} else {
blankSpaces--;
}
}
}
public void eraseMark( int spaceIndex ) {
if ( gameBoard[spaceIndex] != Mark2.BLANK ) {
gameBoard[spaceIndex] = Mark2.BLANK;
blankSpaces++;
}
}
public int[] getAvailableSpaces() {
int spaces[] = new int[blankSpaces];
int spacesIndex = 0;
for ( int boardIndex = 0; boardIndex < gameBoard.length; boardIndex++ )
if ( gameBoard[boardIndex] == Mark2.BLANK )
spaces[spacesIndex++] = boardIndex;
return spaces;
}
public Mark2 getMarkAtIndex( int spaceIndex ) {
return gameBoard[spaceIndex];
}
public boolean hasWinningSolution() {
this.solutionFound = false;
this.winningMark = Mark2.BLANK;
for ( int setIndex = 0; setIndex < winSets.length && !solutionFound; setIndex++ )
checkSpacesForWinningSolution( winSets[setIndex][0], winSets[setIndex][1], winSets[setIndex][2] );
return solutionFound;
}
public Mark2 getWinningMark() {
return this.winningMark;
}
private void checkSpacesForWinningSolution( int first, int second, int third ) {
if ( gameBoard[first] == gameBoard[second] && gameBoard[third] == gameBoard[first]
&& gameBoard[first] != Mark2.BLANK ) {
solutionFound = true;
winningMark = gameBoard[first];
}
}
public void printBoard() {
System.out.printf( " %c | %c | %c\n", getMarkCharacter( gameBoard[0] ), getMarkCharacter( gameBoard[1] ),
getMarkCharacter( gameBoard[2] ) );
System.out.println( "------------" );
System.out.printf( " %c | %c | %c\n", getMarkCharacter( gameBoard[3] ), getMarkCharacter( gameBoard[4] ),
getMarkCharacter( gameBoard[5] ) );
System.out.println( "------------" );
System.out.printf( " %c | %c | %c\n", getMarkCharacter( gameBoard[6] ), getMarkCharacter( gameBoard[7] ),
getMarkCharacter( gameBoard[8] ) );
}
public char getMarkCharacter( Mark2 mark ) {
char result = (mark == Mark2.PLAYER) ? 'O' : ' ';
result = (mark == Mark2.COMPUTER) ? 'X' : result;
return result;
}
}
如果有任何混淆,这里是“Mark2”类:
public enum Mark2 {
BLANK,
PLAYER,
COMPUTER
}
在Tic-Tac-Toe游戏中,有3种可能性,而不仅仅是2种:玩家1赢,玩家2赢,无人赢。
您应该像这样替换行:
int score = (currentMark == Mark2.COMPUTER) ? -1 : 1;
通过这样的事情:
int score = (currentMark == Mark2.COMPUTER) ? -1 : ((currentMark == Mark2.PLAYER) ? 1 : 0);
让我们来看一些更简单的东西——棋盘是1x1,第一个在棋盘上打上标记的玩家获胜。
现在计算机启动,分数=-1。没有获胜的解决方案(选中了获胜的一组,它不是一行中的一个),并且有可用的空间。因此,我们继续回溯,尝试一个可用的位置。
现在Mark=PLAYER,棋盘有一个获胜的解决方案。所以计算机获胜,得分=-1。
返回到第一个调用,行"int nextScore=极小值(board, nextMark);"再次返回-1,最终得分为-1。
更大的问题也会发生同样的事情。
在长时间Rest后,在@GuyAdini的回复的帮助下,我顿悟了。我写了一个测试来计算极小值()返回的三个可能分数的发生情况。结果0没有得到任何结果,这让我意识到我需要0才能被算法视为一个可能的分数。
我最初将“分数”的初始化更改为最低/最高可能的结果(-1/1)并与之进行比较。然而,这禁止结果从返回的分数集中获得最低/最高值,而是还包括初始化值。这破坏了结果。
我将以下比较添加到“分数”的条件变化中:
|| availableIndex == 0
这迫使所有剩余的比较都必须与属于返回分数集的值进行比较。
public class Minimax {
public static int minimax( Board board, Mark2 currentMark ) {
int score = 0;
int[] availableSpaces = board.getAvailableSpaces();
if ( board.hasWinningSolution() )
score = (board.getWinningMark() == Mark2.COMPUTER) ? 1 : -1;
else if ( availableSpaces.length != 0 ) {
Mark2 nextMark = (currentMark == Mark2.COMPUTER) ? Mark2.PLAYER : Mark2.COMPUTER;
for ( int availableIndex = 0; availableIndex < availableSpaces.length; availableIndex++ ) {
board.addMark( availableSpaces[availableIndex], currentMark );
int nextScore = minimax( board, nextMark );
board.eraseMark( availableSpaces[availableIndex] );
if ( currentMark == Mark2.COMPUTER && nextScore > score
|| currentMark == Mark2.PLAYER && nextScore < score
|| availableIndex == 0 )
score = nextScore;
}
}
return score;
}
}
对角线从右到左 在这里,支票中奖者代码结束。
我正在尝试用alpha-beta剪枝实现一个用于tic-tac-toe的minimax算法。现在我有程序在运行,但它似乎不起作用。每当我运行它时,它似乎会在所有的方块中输入垃圾。我已经实现了它,所以我的minimax函数接受一个棋盘状态,并修改该状态,以便当它完成时,棋盘状态包含下一个最佳移动。然后,我将“this”设置为与修改后的电路板相等。以下是我的minimax算法函数: 如果有人想尝试运行
所以我在过去的三周里一直在做这个项目。我很早就设法让minimax函数在3x3板上工作,但是当我尝试在4x4板上使用它时,问题开始出现,即Java堆空间错误。从那时起,在Alpha-beta修剪的帮助下,我成功地从aprox中减少了minimax函数中所需的minimax调用的数量。59000到16000到11000,最后是8000次呼叫(这是假设一个插槽已填满的电路板的初始minimax呼叫)。
首先,我是java的初学者,我正在尝试模拟TicTacToe游戏。我想使用游戏树为所有状态创建一个可能的树。树中的每个节点都将代表状态并使用此树来决定下一步要做的事情。我计划按如下方式进行, > 接口类包括表示单个移动所需的信息。 抽象/接口类包括以下方法: a、 返回一个新的状态对象,该对象表示应用该移动后游戏的状态。 B.如果当前状态代表其中一名玩家的胜利,则此游戏的获胜者ID。 c、 返回当
我一直在尝试在我的tic-tac-toe游戏中使用minimax算法,以使我的AI无敌。然而,它并不总是返回最佳移动。 它给出了上面的指数[2]作为正确的最佳移动。 然而,有了下面的棋盘,它给出的答案是索引[3],这将允许人类玩家在轮到它时获胜。 我想知道我的代码出了什么问题。以下是我用作参考的一些网站: http://www.geeksforgeeks.org/minimax-algorithm