Aplicación Java Minimax para Android en un juego tic-tac-toe

Estoy tratando de hacer una aplicación Android de Tic-Tac-Toe, por supuesto en Java. Después de depurar por unas pocas horas, arreglando cosas aquí y allá, he encontrado un problema que no puedo resolver por mí mismo en este momento, con este conocimiento. He deducido que el problema se encuentra en el método AndroidPerform () donde en la primera línea se le pide encontrar el mejor movimiento y colocarlo en la variable apropiadamente llamada bestMove, que es un objeto Move.

public void AndroidPerform(){ Move bestMove = AndroidMove(NOUGHT); placeAMove(bestMove.x, bestMove.y, NOUGHT); minimaxActivity.setMove(bestMove.x, bestMove.y, NOUGHT); } 

Ahora, claramente, mirando el código anterior podemos ver que el método AndroidMove (jugador) se llama que es básicamente un algoritmo de minimax, o por lo menos debe ser. Durante la depuración he visto que encuentra buenos movimientos, pero cuando se trata de esta línea

 Move bestMove = AndroidMove(NOUGHT); 

Devuelve null para x y para yy aquí es donde consigo mi problema que no puedo arreglar. He proporcionado las dos clases más importantes, en realidad es en estas clases donde todo el trabajo está hecho. De todos modos, espero que pueda ayudar, como siempre puede ser, gracias de antemano.

Clase MyGame

 public class MyGame { // Name-constants to represent the seeds and cell contents public final int EMPTY = 0; public final int CROSS = 1; public final int NOUGHT = 2; // Name-constants to represent the various states of the game public final int PLAYING = 0; public final int CROSS_WON = 1; public final int NOUGHT_WON = 2; public final int DRAW = 3; // The game board and the game status public static final int ROWS = 3, COLS = 3; // number of rows and columns public static int[][] board = new int[ROWS][COLS]; // game board in 2D array // containing (EMPTY, CROSS, NOUGHT) public static int currentState; // the current state of the game // (PLAYING, DRAW, CROSS_WON, NOUGHT_WON) public static int currentPlayer; // the current player (CROSS or NOUGHT) public static int currentRow, currentCol; // current seed's row and column public int AndroidPlayer, HumanPlayer; MinimaxActivity minimaxActivity = new MinimaxActivity(); class Move { int x, y, score, player; public Move(int score){ this.score = score; } public Move(int x, int y) { this.x = x; this.y = y; } public Move(int x, int y, int player) { this.x = x; this.y = y; this.player = player; } } public void resetBoard() { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { board[i][j] = 0; } } } List<Move> availableMoves; public Move AndroidMove(int player) { // Computer is always NOUGHT // Base case int State = CheckGameState(); if (State == NOUGHT_WON){ return new Move(10); } else if (State == CROSS_WON){ return new Move(-10); } else if (State == DRAW){ return new Move(0); } List<Move> moves = getAvailableStates(); //if (moves.isEmpty()) return new Move(0); for (int i = 0; i < ROWS; ++i) { for (int j = 0; j < COLS; ++j) { if (board[i][j] == EMPTY){ Move move = new Move(i, j, player); placeAMove(i, j, player); if (player == NOUGHT){ move.score = AndroidMove(CROSS).score; } else { move.score = AndroidMove(NOUGHT).score; } moves.add(move); placeAMove(i, j, EMPTY); } } } int bestMove = 0; if (player == NOUGHT) { int bestScore = -1000000; for (int i = 0; i < moves.size(); i++) { if (moves.get(i).score > bestScore) { bestMove = i; bestScore = moves.get(i).score; } } } else { int bestScore = 1000000; for (int i = 0; i < moves.size(); i++) { if (moves.get(i).score < bestScore) { bestMove = i; bestScore = moves.get(i).score; } } } return moves.get(bestMove); } public void AndroidPerform(){ Move bestMove = AndroidMove(NOUGHT); placeAMove(bestMove.x, bestMove.y, NOUGHT); minimaxActivity.setMove(bestMove.x, bestMove.y, NOUGHT); } public void placeAMove(int x, int y, int player) { board[x][y] = player; //player = 1 for X, 2 for O } public void placeAMove(Point point, int player) { board[point.x][point.y] = player; //player = 1 for X, 2 for O } public List<Move> getAvailableStates() { availableMoves = new ArrayList<>(); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { if (board[i][j] == EMPTY) { availableMoves.add(new Move(i, j)); } } } return availableMoves; } public int CheckGameState() { /* 0 - Playing 1 - X Won 2 - O Won 3 - Draw */ // Check Rows - Horizontal Lines for (int i = 0; i< ROWS; i++){ if (board[i][0] == CROSS && board[i][1] == CROSS && board[i][2] == CROSS){ return CROSS_WON; } if (board[i][0] == NOUGHT && board[i][1] == NOUGHT && board[i][2] == NOUGHT){ return NOUGHT_WON; } } // Check Columns - Vertical Lines for (int i = 0; i< COLS; i++){ if (board[0][i] == CROSS && board[1][i] == CROSS && board[2][i] == CROSS){ return CROSS_WON; } if (board[0][i] == NOUGHT && board[1][i] == NOUGHT && board[2][i] == NOUGHT){ return NOUGHT_WON; } } // Check Diagonal if (board[0][0] == CROSS && board[1][1] == CROSS && board[2][2] == CROSS){ return CROSS_WON; } if (board[0][0] == NOUGHT && board[1][1] == NOUGHT && board[2][2] == NOUGHT){ return NOUGHT_WON; } // Check Reverse-Diagonal if (board[0][2] == CROSS && board[1][1] == CROSS && board[2][0] == CROSS){ return CROSS_WON; } if (board[0][2] == NOUGHT && board[1][1] == NOUGHT && board[2][0] == NOUGHT){ return NOUGHT_WON; } // Check for Tie for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { if (board[i][j] != CROSS && board[i][j] != NOUGHT){ return PLAYING; } } } return DRAW; } } 

Clase MinimaxActivity

 public class MinimaxActivity extends AppCompatActivity { private Board BoardGame; private MyGame myGame; private Button mBoardButtons[][]; private TextView mInfoTextView; private TextView mPlayerOneCount; private TextView mTieCount; private TextView mPlayerTwoCount; private TextView mPlayerOneText; private TextView mPlayerTwoText; private int mPlayerOneCounter = 0; private int mTieCounter = 0; private int mPlayerTwoCounter = 0; private Button newGame, exitGame; public int HUMAN = 1; public int COMPUTER = 2; Random random; private int First=0; private int Counter = 0; private boolean isGameOver = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_play); mBoardButtons = new Button[3][3]; mBoardButtons[0][0] = (Button) findViewById(R.id.one); mBoardButtons[0][1] = (Button) findViewById(R.id.two); mBoardButtons[0][2] = (Button) findViewById(R.id.three); mBoardButtons[1][0] = (Button) findViewById(R.id.four); mBoardButtons[1][1] = (Button) findViewById(R.id.five); mBoardButtons[1][2] = (Button) findViewById(R.id.six); mBoardButtons[2][0] = (Button) findViewById(R.id.seven); mBoardButtons[2][1] = (Button) findViewById(R.id.eight); mBoardButtons[2][2] = (Button) findViewById(R.id.nine); newGame = (Button) findViewById(R.id.newGame1); exitGame = (Button) findViewById(R.id.exitGame1); // Text Fields mInfoTextView = (TextView) findViewById(R.id.information); mPlayerOneCount = (TextView) findViewById(R.id.humanCount); mTieCount = (TextView) findViewById(R.id.tiesCount); mPlayerTwoCount = (TextView) findViewById(R.id.androidCount); mPlayerOneText = (TextView) findViewById(R.id.human); mPlayerTwoText = (TextView) findViewById(R.id.android); // Counters mPlayerOneCount.setText(Integer.toString(mPlayerOneCounter)); mTieCount.setText(Integer.toString(mTieCounter)); mPlayerTwoCount.setText(Integer.toString(mPlayerTwoCounter)); random = new Random(); exitGame.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MinimaxActivity.this.finish(); } }); final CharSequence[] items = {"Computer", "Player"}; final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this); alertDialog.setCancelable(false); alertDialog.setTitle("Who goes first?"); alertDialog.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { if (items[item] == "Computer") { First = 1; // Computer } else if (items[item] == "Player") { First = 2; // Player } dialog.dismiss(); //BoardGame = new Board(); myGame = new MyGame(); if (First == 1) { startNewGame(true); // True For Computer } if (First == 2) { startNewGame(false); // False For Player } } }); alertDialog.show(); newGame.setOnClickListener(new View.OnClickListener() { // FIX STARTNEWGAME @Override public void onClick(View v) { if (Counter % 2 == 0) { startNewGame(false); Counter++; } else { startNewGame(true); Counter++; } // Here we stop, counter can be used for session concept } }); // http://developer.android.com/guide/topics/ui/dialogs.html // Adding a persistent multiple-choice or single-choice list } private void startNewGame(boolean GoesFirst) { MyResetBoard(); // Look at board reset mPlayerOneText.setText("Human:"); mPlayerTwoText.setText("Android:"); if(GoesFirst){ // Computer Goes First mInfoTextView.setText("Android's Turn."); //myGame.AndroidPerform(); setMove(random.nextInt(3), random.nextInt(3), COMPUTER); GoesFirst = false; }else{ //Player Goes First mInfoTextView.setText("Human's Turn."); GoesFirst = true; } isGameOver = false; } private void MyResetBoard(){ myGame.resetBoard(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { mBoardButtons[i][j].setText(""); mBoardButtons[i][j].setEnabled(true); mBoardButtons[i][j].setOnClickListener(new ButtonClickListener(i,j)); mBoardButtons[i][j].setBackgroundResource(R.drawable.empty); } } } private class ButtonClickListener implements View.OnClickListener { int x,y; public ButtonClickListener(int i, int j) { this.x = i; this.y = j; } @Override public void onClick(View v) { if (!isGameOver){ // If the game is not over if (mBoardButtons[x][y].isEnabled()){ setMove(x, y, myGame.CROSS); // Human makes a move int winner = myGame.CheckGameState(); if (winner == myGame.PLAYING) { // If still playing mInfoTextView.setText(R.string.turn_computer); myGame.AndroidPerform(); //int move = mGame.getComputerMove(); //setMove(TicTacToeGame.PLAYER_TWO, move); winner = myGame.CheckGameState(); } if (winner == myGame.PLAYING){ mInfoTextView.setText(R.string.turn_human); } else if (winner == myGame.DRAW) { // If draw mInfoTextView.setText(R.string.result_tie); mTieCounter++; mTieCount.setText(Integer.toString(mTieCounter)); isGameOver = true; } else if (winner == myGame.CROSS_WON) { // X Won mInfoTextView.setText(R.string.result_human_wins); mPlayerOneCounter++; mPlayerOneCount.setText(Integer.toString(mPlayerOneCounter)); isGameOver = true; } else if (winner == myGame.NOUGHT_WON){ // O Won mInfoTextView.setText(R.string.result_android_wins); mPlayerTwoCounter++; mPlayerTwoCount.setText(Integer.toString(mPlayerTwoCounter)); isGameOver = true; } } } } } public void setMove(int x, int y, int player){ myGame.placeAMove(x, y, player); mBoardButtons[x][y].setEnabled(false); if (player == 1) { mBoardButtons[x][y].setBackgroundResource(R.drawable.x); } else { mBoardButtons[x][y].setBackgroundResource(R.drawable.o); } } 

3 Solutions collect form web for “Aplicación Java Minimax para Android en un juego tic-tac-toe”

Muy bien creo que he resuelto su problema de ambos x, y, y la puntuación está regresando nulo. Cuando cambie el código anotado a continuación, las variables no serán todas nulas.

También ponga la clase Move en un nuevo archivo java. Añadir "extends Move" después de "public class myGame"

La razón por la que se estaban convirtiendo en nulo es porque las variables x, y y score estaban en la clase Move y no se puede acceder desde la clase myGame sin public static delante de las variables x, y y score.

public class MyGame { necesita ser public class MyGame extends Move{

 // Name-constants to represent the seeds and cell contents public final int EMPTY = 0; public final int CROSS = 1; public final int NOUGHT = 2; // Name-constants to represent the various states of the game public final int PLAYING = 0; public final int CROSS_WON = 1; public final int NOUGHT_WON = 2; public final int DRAW = 3; // The game board and the game status public static final int ROWS = 3, COLS = 3; // number of rows and columns public static int[][] board = new int[ROWS][COLS]; // game board in 2D array // containing (EMPTY, CROSS, NOUGHT) public static int currentState; // the current state of the game // (PLAYING, DRAW, CROSS_WON, NOUGHT_WON) public static int currentPlayer; // the current player (CROSS or NOUGHT) public static int currentRow, currentCol; // current seed's row and column public int AndroidPlayer, HumanPlayer; MinimaxActivity minimaxActivity = new MinimaxActivity(); public void resetBoard() { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { board[i][j] = 0; } } } List<Move> availableMoves; public Move AndroidMove(int player) { // Computer is always NOUGHT // Base case int State = CheckGameState(); if (State == NOUGHT_WON){ return new Move(10); } else if (State == CROSS_WON){ return new Move(-10); } else if (State == DRAW){ return new Move(0); } List<Move> moves = getAvailableStates(); //if (moves.isEmpty()) return new Move(0); for (int i = 0; i < ROWS; ++i) { for (int j = 0; j < COLS; ++j) { if (board[i][j] == EMPTY){ Move move = new Move(i, j, player); placeAMove(i, j, player); if (player == NOUGHT){ move.score = AndroidMove(CROSS).score; } else { move.score = AndroidMove(NOUGHT).score; } moves.add(move); placeAMove(i, j, EMPTY); } } } int bestMove = 0; if (player == NOUGHT) { int bestScore = -1000000; for (int i = 0; i < moves.size(); i++) { if (moves.get(i).score > bestScore) { bestMove = i; bestScore = moves.get(i).score; } } } else { int bestScore = 1000000; for (int i = 0; i < moves.size(); i++) { if (moves.get(i).score < bestScore) { bestMove = i; bestScore = moves.get(i).score; } } } return moves.get(bestMove); } public void AndroidPerform(){ Move bestMove = AndroidMove(NOUGHT); placeAMove(bestMove.x, bestMove.y, NOUGHT); minimaxActivity.setMove(bestMove.x, bestMove.y, NOUGHT); } public void placeAMove(int x, int y, int player) { board[x][y] = player; //player = 1 for X, 2 for O } public void placeAMove(Point point, int player) { board[point.x][point.y] = player; //player = 1 for X, 2 for O } public List<Move> getAvailableStates() { availableMoves = new ArrayList<>(); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { if (board[i][j] == EMPTY) { availableMoves.add(new Move(i, j)); } } } return availableMoves; } public int CheckGameState() { /* 0 - Playing 1 - X Won 2 - O Won 3 - Draw */ // Check Rows - Horizontal Lines for (int i = 0; i< ROWS; i++){ if (board[i][0] == CROSS && board[i][1] == CROSS && board[i][2] == CROSS){ return CROSS_WON; } if (board[i][0] == NOUGHT && board[i][1] == NOUGHT && board[i][2] == NOUGHT){ return NOUGHT_WON; } } // Check Columns - Vertical Lines for (int i = 0; i< COLS; i++){ if (board[0][i] == CROSS && board[1][i] == CROSS && board[2][i] == CROSS){ return CROSS_WON; } if (board[0][i] == NOUGHT && board[1][i] == NOUGHT && board[2][i] == NOUGHT){ return NOUGHT_WON; } } // Check Diagonal if (board[0][0] == CROSS && board[1][1] == CROSS && board[2][2] == CROSS){ return CROSS_WON; } if (board[0][0] == NOUGHT && board[1][1] == NOUGHT && board[2][2] == NOUGHT){ return NOUGHT_WON; } // Check Reverse-Diagonal if (board[0][2] == CROSS && board[1][1] == CROSS && board[2][0] == CROSS){ return CROSS_WON; } if (board[0][2] == NOUGHT && board[1][1] == NOUGHT && board[2][0] == NOUGHT){ return NOUGHT_WON; } // Check for Tie for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { if (board[i][j] != CROSS && board[i][j] != NOUGHT){ return PLAYING; } } } return DRAW; } } 

La clase Move debe estar en un archivo Java distinto de la clase myGame.

 public class Move { 

int x, y, score, player; Necesita ser public static int x, y, score, player;

  public Move(int score){ this.score = score; } public Move(int x, int y) { this.x = x; this.y = y; } public Move(int x, int y, int player) { this.x = x; this.y = y; this.player = player; } } 

EDIT: Parece que esto ayudó a arreglar el accidente, pero usted está buscando ayuda en Minimax ahora. Estás en el camino correcto, pero hay algunos problemas con tu implementación. He aquí un ejemplo de cómo podría hacerlo, usando parte de su código:

 public static final int SCORE_DRAW = 0; public static final int SCORE_WIN = 999; public static final int SCORE_LOSS = -999; public static final int SCORE_ILLEGAL_MOVE = Integer.MIN_VALUE; /** * Look at all moves for the specified player, score them, and return the move with the highest score * * @param currentPlayer the player who will be making the move * @param board current state of the board * * @return the move with the highest score */ @NonNull public Move pickBestMove(int currentPlayer, int[][] board){ List<Move> availableMoves = getAvailableMoves(board); if(availableMoves.size() < 1){ //Something went wrong. throw new IllegalStateException("No available moves to pick from."); } //Default to the first available move Move bestMove = availableMoves.get(0); //Iterate through the available moves for(Move move : availableMoves){ move.player = currentPlayer; move.score = getScoreForMove(move, board, currentPlayer); if(move.score == SCORE_WIN){ //If the move would win the game, it's obviously the best move. return move; }else if(move.score > bestMove.score){ //Found a better move. update bestMove. bestMove = move; } } //Return the best one return bestMove; } /** * Score a move by making the move and then assuming each player will play optimally until * we reach the end of the game. * * @param move the move to score * @param board current state of the game board. This will be copied. * @param scoredPlayer the player for whom to score the move * * @return a score for the move, assuming each player plays optimally */ private int getScoreForMove(@NonNull Move move, int[][] board, int scoredPlayer){ //Make a copy of the board so we can change it int[][] boardCopy = copyArray(board); if(boardCopy[move.x][move.y] != EMPTY){ //this is not a legal move, score negative infinity. return SCORE_ILLEGAL_MOVE; }else{ //the move is legal. Update the board with the proposed move boardCopy[move.x][move.y] = move.player; //Get the game state based on the proposed move. int newState = checkGameState(boardCopy); //check if it's a draw, win, or a loss if(newState == DRAW){ //This move would cause a draw. return SCORE_DRAW; }else if(newState == NOUGHT_WON){ //Somebody wins with this move. Check if it's the player we're scoring return scoredPlayer == NOUGHT ? SCORE_WIN : SCORE_LOSS; }else if(newState == CROSS_WON){ //Somebody wins with this move. Check if it's the player we're scoring return scoredPlayer == CROSS ? SCORE_WIN : SCORE_LOSS; } //Game isn't over yet. Assume the next player will make their best possible move, //and check what the resulting score would be. int nextPlayer = move.player == NOUGHT ? CROSS : NOUGHT; //note that we change the player for pickBestMove, but not the scored player return getScoreForMove(pickBestMove(nextPlayer, boardCopy), boardCopy, scoredPlayer); } } public void performComputerMove(){ Move bestMove = pickBestMove(NOUGHT, mBoard); minimaxActivity.setMove(bestMove.x, bestMove.y, NOUGHT); } /** * Perform a deep copy of the array. Most library array copy methods * (Arrays.copyOf, System.arrayCopy) create a shallow copy and are * therefore unsuitable for making an editable copy of a 2d array * * @param toCopy the array to copy * @return a deep copy of the array */ private int[][] copyArray(int[][] toCopy) { int[][] newArray = new int[toCopy.length][toCopy[0].length]; for (int i = 0; i < toCopy.length; i++) { for(int j = 0; j < toCopy[i].length; j++) { newArray[i][j] = toCopy[i][j]; } } return newArray; } 

Además, en la actividad, cuando es el turno de Android para ir primero, en lugar de elegir un movimiento aleatorio, debe utilizar pickBestMove ():

 mInfoTextView.setText("Android's Turn."); myGame.performComputerMove(); 

Espero que eso sea comprensible. Hazme saber si tienes alguna pregunta.

Respuesta anterior:

Su pregunta no especifica cuál es su problema real, pero creo que debe ser un accidente, después de mirar en el código.

El problema no es que bestMove es nulo, o que bestMove.x o .y son nulos. El problema es que en MyGame, cuando se llama MinimaxActivity.setMove (), setMove hace lo siguiente:

 myGame.placeAMove(x, y, player); 

Desafortunadamente, myGame sí mismo será null cuando esto se llama de MyGame. La razón es bastante simple. En MyGame tienes:

 MinimaxActivity minimaxActivity = new MinimaxActivity(); 

Esto significa que está creando una nueva MinimaxActivity cada vez que cree un nuevo MyGame. A continuación, está llamando a setMove en la MinimaxActivity recién creada en lugar de la que ya ha configurado. Cuando crea su MinimaxActivity, myGame comienza como null. Puesto que usted nunca lo fijó, usted conseguirá una excepción:

 public void setMove(int x, int y, int player) { myGame.placeAMove(x, y, player); //---myGame = null!!! Crash. ... } 

Convenientemente, sin embargo, usted ya tiene una actividad Minimax donde ha configurado un nuevo MyGame.

Para solucionar esto, necesitas pasar la instancia existente de la actividad a MyGame. El lugar más fácil es probablemente en el constructor de MyGame:

 MinimaxActivity minimaxActivity; public MyGame(MinimaxActivity minimaxActivity) { this.minimaxActivity = minimaxActivity; } 

Entonces, por supuesto, en MinimaxActivity, en lugar de nuevo MyGame (), tendrás que hacer esto:

 myGame = new MyGame(MinimaxActivity.this); 

Código de Trabajo, cambios realizados en ambas clases.

Clase MinimaxActivity

 public class MinimaxActivity extends AppCompatActivity { private Board BoardGame; private MyGame myGame; private Button mBoardButtons[][]; private TextView mInfoTextView; private TextView mPlayerOneCount; private TextView mTieCount; private TextView mPlayerTwoCount; private TextView mPlayerOneText; private TextView mPlayerTwoText; private int mPlayerOneCounter = 0; private int mTieCounter = 0; private int mPlayerTwoCounter = 0; private Button newGame, exitGame; public int HUMAN = 1; public int COMPUTER = 2; Random random; private int First=0; private int Counter = 0; private boolean isGameOver = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_play); mBoardButtons = new Button[3][3]; mBoardButtons[0][0] = (Button) findViewById(R.id.one); mBoardButtons[0][1] = (Button) findViewById(R.id.two); mBoardButtons[0][2] = (Button) findViewById(R.id.three); mBoardButtons[1][0] = (Button) findViewById(R.id.four); mBoardButtons[1][1] = (Button) findViewById(R.id.five); mBoardButtons[1][2] = (Button) findViewById(R.id.six); mBoardButtons[2][0] = (Button) findViewById(R.id.seven); mBoardButtons[2][1] = (Button) findViewById(R.id.eight); mBoardButtons[2][2] = (Button) findViewById(R.id.nine); newGame = (Button) findViewById(R.id.newGame1); exitGame = (Button) findViewById(R.id.exitGame1); // Text Fields mInfoTextView = (TextView) findViewById(R.id.information); mPlayerOneCount = (TextView) findViewById(R.id.humanCount); mTieCount = (TextView) findViewById(R.id.tiesCount); mPlayerTwoCount = (TextView) findViewById(R.id.androidCount); mPlayerOneText = (TextView) findViewById(R.id.human); mPlayerTwoText = (TextView) findViewById(R.id.android); // Counters mPlayerOneCount.setText(Integer.toString(mPlayerOneCounter)); mTieCount.setText(Integer.toString(mTieCounter)); mPlayerTwoCount.setText(Integer.toString(mPlayerTwoCounter)); random = new Random(); exitGame.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MinimaxActivity.this.finish(); } }); final CharSequence[] items = {"Computer", "Player"}; final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this); alertDialog.setCancelable(false); alertDialog.setTitle("Who goes first?"); alertDialog.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { if (items[item] == "Computer") { First = 1; // Computer } else if (items[item] == "Player") { First = 2; // Player } dialog.dismiss(); myGame = new MyGame(MinimaxActivity.this); if (First == 1) { startNewGame(true); // True For Computer } if (First == 2) { startNewGame(false); // False For Player } } }); alertDialog.show(); newGame.setOnClickListener(new View.OnClickListener() { // FIX STARTNEWGAME @Override public void onClick(View v) { if (Counter % 2 == 0) { startNewGame(false); Counter++; } else { startNewGame(true); Counter++; } } }); } private void startNewGame(boolean GoesFirst) { MyResetBoard(); // Look at board reset mPlayerOneText.setText("Human:"); mPlayerTwoText.setText("Android:"); if(GoesFirst){ // Computer Goes First mInfoTextView.setText("Android's Turn."); setMove(random.nextInt(3), random.nextInt(3), COMPUTER); GoesFirst = false; }else{ //Player Goes First mInfoTextView.setText("Human's Turn."); GoesFirst = true; } isGameOver = false; } private void MyResetBoard(){ myGame.resetBoard(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { mBoardButtons[i][j].setText(""); mBoardButtons[i][j].setEnabled(true); mBoardButtons[i][j].setOnClickListener(new ButtonClickListener(i,j)); mBoardButtons[i][j].setBackgroundResource(R.drawable.empty); } } } private class ButtonClickListener implements View.OnClickListener { int x,y; public ButtonClickListener(int i, int j) { this.x = i; this.y = j; } @Override public void onClick(View v) { if (!isGameOver){ // If the game is not over if (mBoardButtons[x][y].isEnabled()){ setMove(x, y, HUMAN); // Human makes a move CROSS int winner = myGame.CheckGameState(); if (winner == myGame.PLAYING) { // If still playing mInfoTextView.setText(R.string.turn_computer); int[] result = myGame.move(); setMove(result[0], result[1], COMPUTER); winner = myGame.CheckGameState(); } winner = myGame.CheckGameState(); if (winner == myGame.PLAYING){ mInfoTextView.setText(R.string.turn_human); } else if (winner == myGame.DRAW) { // If draw mInfoTextView.setText(R.string.result_tie); mTieCounter++; mTieCount.setText(Integer.toString(mTieCounter)); isGameOver = true; } else if (winner == myGame.CROSS_WON) { // X Won mInfoTextView.setText(R.string.result_human_wins); mPlayerOneCounter++; mPlayerOneCount.setText(Integer.toString(mPlayerOneCounter)); isGameOver = true; } else if (winner == myGame.NOUGHT_WON){ // O Won mInfoTextView.setText(R.string.result_android_wins); mPlayerTwoCounter++; mPlayerTwoCount.setText(Integer.toString(mPlayerTwoCounter)); isGameOver = true; } } } } } public void setMove(int x, int y, int player){ myGame.placeAMove(x, y, player); mBoardButtons[x][y].setEnabled(false); if (player == 1) { mBoardButtons[x][y].setBackgroundResource(R.drawable.x); } else { mBoardButtons[x][y].setBackgroundResource(R.drawable.o); } } } 

Clase MyGame

 public class MyGame { // Name-constants to represent the seeds and cell contents public final int EMPTY = 0; public final int CROSS = 1; public final int NOUGHT = 2; // Name-constants to represent the various states of the game public final int PLAYING = 0; public final int CROSS_WON = 1; public final int NOUGHT_WON = 2; public final int DRAW = 3; // The game board and the game status public static final int ROWS = 3, COLS = 3; // number of rows and columns public static int[][] board = new int[ROWS][COLS]; // game board in 2D array // containing (EMPTY, CROSS, NOUGHT) public static int currentState; // the current state of the game // (PLAYING, DRAW, CROSS_WON, NOUGHT_WON) public static int currentPlayer; // the current player (CROSS or NOUGHT) public static int currentRow, currentCol; // current seed's row and column MinimaxActivity minimaxActivity; public MyGame(MinimaxActivity minimaxActivity) { this.minimaxActivity = minimaxActivity; } public void resetBoard() { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { board[i][j] = 0; } } } /** Get next best move for computer. Return int[2] of {row, col} */ public int[] move() { int[] result = minimax(2, NOUGHT); // depth, max turn return new int[] {result[1], result[2]}; // row, col } public int[] minimax(int depth, int player){ // Generate possible next moves in a List of int[2] of {row, col}. List<int[]> nextMoves = generateMoves(); // mySeed(NOUGHT) is maximizing; while oppSeed(CROSS) is minimizing int bestScore = (player == NOUGHT) ? Integer.MIN_VALUE : Integer.MAX_VALUE; int currentScore; int bestRow = -1; int bestCol = -1; if (nextMoves.isEmpty() || depth == 0){ bestScore = evaluate(); } else { for (int[] move : nextMoves){ board[move[0]][move[1]] = player; if (player == NOUGHT) { // NOUGHT is Maximizing Player currentScore = minimax(depth - 1, CROSS)[0]; if (currentScore > bestScore) { bestScore = currentScore; bestRow = move[0]; bestCol = move[1]; } } else { // CROSS is Minimizing Player currentScore = minimax(depth - 1, NOUGHT)[0]; if (currentScore < bestScore) { bestScore = currentScore; bestRow = move[0]; bestCol = move[1]; } } // Undo move board[move[0]][move[1]] = EMPTY; } } return new int[] {bestScore, bestRow, bestCol}; } private int evaluate() { int score = 0; // Evaluate score for each of the 8 lines (3 rows, 3 columns, 2 diagonals) score += evaluateLine(0, 0, 0, 1, 0, 2); // row 0 score += evaluateLine(1, 0, 1, 1, 1, 2); // row 1 score += evaluateLine(2, 0, 2, 1, 2, 2); // row 2 score += evaluateLine(0, 0, 1, 0, 2, 0); // col 0 score += evaluateLine(0, 1, 1, 1, 2, 1); // col 1 score += evaluateLine(0, 2, 1, 2, 2, 2); // col 2 score += evaluateLine(0, 0, 1, 1, 2, 2); // diagonal score += evaluateLine(0, 2, 1, 1, 2, 0); // alternate diagonal return score; } /** The heuristic evaluation function for the given line of 3 cells @Return +100, +10, +1 for 3-, 2-, 1-in-a-line for computer. -100, -10, -1 for 3-, 2-, 1-in-a-line for opponent. 0 otherwise */ private int evaluateLine(int row1, int col1, int row2, int col2, int row3, int col3) { int score = 0; // First cell if (board[row1][col1] == NOUGHT) { score = 1; } else if (board[row1][col1] == CROSS) { score = -1; } // Second cell if (board[row2][col2] == NOUGHT) { if (score == 1) { // cell1 is mySeed score = 10; } else if (score == -1) { // cell1 is oppSeed return 0; } else { // cell1 is empty score = 1; } } else if (board[row2][col2] == CROSS) { if (score == -1) { // cell1 is oppSeed score = -10; } else if (score == 1) { // cell1 is mySeed return 0; } else { // cell1 is empty score = -1; } } // Third cell if (board[row3][col3] == NOUGHT) { if (score > 0) { // cell1 and/or cell2 is mySeed score *= 10; } else if (score < 0) { // cell1 and/or cell2 is oppSeed return 0; } else { // cell1 and cell2 are empty score = 1; } } else if (board[row3][col3] == CROSS) { if (score < 0) { // cell1 and/or cell2 is oppSeed score *= 10; } else if (score > 1) { // cell1 and/or cell2 is mySeed return 0; } else { // cell1 and cell2 are empty score = -1; } } return score; } private List<int[]> generateMoves() { List<int[]> nextMoves = new ArrayList<int[]>(); // allocate List int State = CheckGameState(); // If gameover, ie, no next move if (State == 1 || // X Won State == 2 || // O Won State == 3) // Draw { return nextMoves; // return empty list } // Search for empty cells and add to the List for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { if (board[row][col] == EMPTY) { nextMoves.add(new int[] {row, col}); } } } return nextMoves; } public void placeAMove(int x, int y, int player) { board[x][y] = player; //player = 1 for X, 2 for O } public int CheckGameState() { /* 0 - Playing 1 - X Won 2 - O Won 3 - Draw */ // Check Rows - Horizontal Lines for (int i = 0; i< ROWS; i++){ if (board[i][0] == CROSS && board[i][1] == CROSS && board[i][2] == CROSS){ return CROSS_WON; } if (board[i][0] == NOUGHT && board[i][1] == NOUGHT && board[i][2] == NOUGHT){ return NOUGHT_WON; } } // Check Columns - Vertical Lines for (int i = 0; i< COLS; i++){ if (board[0][i] == CROSS && board[1][i] == CROSS && board[2][i] == CROSS){ return CROSS_WON; } if (board[0][i] == NOUGHT && board[1][i] == NOUGHT && board[2][i] == NOUGHT){ return NOUGHT_WON; } } // Check Diagonal if (board[0][0] == CROSS && board[1][1] == CROSS && board[2][2] == CROSS){ return CROSS_WON; } if (board[0][0] == NOUGHT && board[1][1] == NOUGHT && board[2][2] == NOUGHT){ return NOUGHT_WON; } // Check Reverse-Diagonal if (board[0][2] == CROSS && board[1][1] == CROSS && board[2][0] == CROSS){ return CROSS_WON; } if (board[0][2] == NOUGHT && board[1][1] == NOUGHT && board[2][0] == NOUGHT){ return NOUGHT_WON; } // Check for Tie for (int i = 0; i < ROWS; i++) { for (int j = 0; j < COLS; j++) { if (board[i][j] != CROSS && board[i][j] != NOUGHT){ return PLAYING; } } } return DRAW; } } 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.