MiniMaxを持つTiC TAC TOE:プレーヤーが最初に行くときにコンピュータを失うことがあります。さもなければ作ります
-
12-12-2019 - |
質問
私は無敵のTiC TAC TOEのためのMiniMAXアルゴリズムに取り組んでいます。私は、コンピュータが最初に行くときとプレイヤーが最初に行くときの両方で仕事をする必要があります。現在のバージョンでは、最初に行くとコンピュータは失われません。ただし、Playerが最初に行く場合は、MiniMaxは決して最適な動きを見つけることは決してないようです(常にスコアとして-1を返します)。
プレイヤーが最初の動きをする場合は、コンピュータの-1に戻ったものが-1に戻ったのですか?
例:
board.addMark( 1, Mark2.PLAYER ); // add a 'PLAYER' mark to an arbitrary location
Minimax.minimax( board, Mark2.COMPUTER ); // will always return -1
.
これは「MiniMax」クラスです。
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
}
. 解決 3
拡張休憩をとった後、@Guyadiniによる回答の助けを借りて、私はエピファニーを持っていました。Minimax()によって返された3つの可能なスコアの発生を数えるためのテストを書きました。それは結果として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;
}
}
. 他のヒント
より単純なものを見てみましょう - ボードは1×1、そして最初のプレイヤーが勝ちます。
今すぐコンピュータの起動、SCORE= -1。 勝利の解決策はありません(1つの勝者セットがチェックされ、1行目ではありません)、 そして空き容量があります。 だから私たちはバックトラッキングを進めて1つの利用可能な位置を試す。
今マーク=プレーヤーで、ボードに勝利のソリューションがあります。そのため、コンピュータがWINS、SCORE= -1。
最初の通話に戻ると、「int nextscore= minimax(ボード、ネクストマーク)」という行。もう一度-1を返し、最終スコアは-1です。
同じことが大きい問題であなたに起こります。
TiC TAC TOEゲームでは、3つの可能性があり、ただ2:Player1 Wins、Player2がWINS、誰も勝ちます。
このような行を置き換える必要があります:
int score = (currentMark == Mark2.COMPUTER) ? -1 : 1;
.
このようなもの:
int score = (currentMark == Mark2.COMPUTER) ? -1 : ((currentMark == Mark2.PLAYER) ? 1 : 0);
.