Implementing the GameManager Interface
The GameManager is responsible for handling the overall state of the game, including determining whether or not a move is valid and if the player or AI opponent has won the game. The implementation of the interface is posted below.
package com.dreamdom.tictactoe.gamedriver; import java.util.ArrayList; import java.util.Collections; public class ClassicGameManager implements GameManager { // Constants public static final int NUM_ROWS = 3; public static final int NUM_COLS = 3; private int mStartingTurn; private ArrayList<TicTacToeDrawable> mTileList; private ArrayList<TicTacToeDrawable> mAvailableTiles; private GameAI mGameAI; private int mGameState; /** * * @param tileList * All the tiles on the gameboard. Must be non-null * @param gameAI * The default gameAI for the game. Must be non-null */ public ClassicGameManager(ArrayList<TicTacToeDrawable> tileList, GameAI gameAI) { mGameAI = gameAI; mTileList = tileList; // Init available tiles mAvailableTiles = new ArrayList<TicTacToeDrawable>(mTileList); Collections.copy(mAvailableTiles, mTileList); // Initialize the turn and game state mStartingTurn = TicTacToeConstants.TURN_PLAYER; mGameState = TicTacToeConstants.GAME_STATE_PLAYING; } @Override public void playerClickedTile(TicTacToeDrawable tile) { // The player is always x if (tile.getState() == TicTacToeConstants.TILE_STATE_EMPTY) changeTileValue(tile, TicTacToeConstants.TILE_STATE_X); if (mGameState == TicTacToeConstants.GAME_STATE_PLAYING) { // have the gameAI play TicTacToeDrawable t = mGameAI.playPiece(this); changeTileValue(t, TicTacToeConstants.TILE_STATE_O); } } @Override public void setStartingTurn(int startTurn) { mStartingTurn = startTurn; } @Override public boolean checkWin(int tileState, int row, int col) { int northSouth = 0; int eastWest = 0; int northwestSoutheast = 0; int northeastSouthwest = 0; // Loop through all the pieces for (TicTacToeDrawable curTile : mTileList) { int curRow = curTile.getRow(); int curCol = curTile.getCol(); int curState = curTile.getState(); if (curState == tileState) { if (curCol == col) { if (curRow < row || curRow > row) northSouth++; } else if (curRow == row) { if (curCol < col || curCol > col) eastWest++; } else if ((curRow - row) == (curCol - col)) { northwestSoutheast++; } else if ((curRow - row) == -(curCol - col)) { northeastSouthwest++; } } } // Check for a win in all the possible directions if (northSouth == 2) return true; if (eastWest == 2) return true; if (northwestSoutheast == 2) return true; if (northeastSouthwest == 2) return true; return false; } @Override public void setGameAI(GameAI gameAI) { mGameAI = gameAI; } @Override public void setTileList(ArrayList<TicTacToeDrawable> tileList) { mTileList = tileList; mAvailableTiles = new ArrayList<TicTacToeDrawable>(mTileList); Collections.copy(mAvailableTiles, mTileList); } @Override public void reset() { // Change all the tiles back to being available mAvailableTiles = new ArrayList<TicTacToeDrawable>(mTileList); Collections.copy(mAvailableTiles, mTileList); // Loop through and set all the tiles to empty for (TicTacToeDrawable curTile : mAvailableTiles) curTile.setState(TicTacToeConstants.TILE_STATE_EMPTY); // Set the game state to ready mGameState = TicTacToeConstants.GAME_STATE_PLAYING; // If the computer starts, have them play if (mStartingTurn == TicTacToeConstants.TURN_COMPUTER) { TicTacToeDrawable t = mGameAI.playPiece(this); changeTileValue(t, TicTacToeConstants.TILE_STATE_O); } } @Override public void changeTileValue(TicTacToeDrawable tile, int value) { // Set the state and update the list of available tiles tile.setState(value); mAvailableTiles.remove(tile); // Check for the win if (checkWin(value, tile.getRow(), tile.getCol())) { if (value == TicTacToeConstants.TILE_STATE_X) { mGameState = TicTacToeConstants.GAME_STATE_PLAYER_WINS; } else if (value == TicTacToeConstants.TILE_STATE_O) { mGameState = TicTacToeConstants.GAME_STATE_COMPUTER_WINS; } } // Check for a cats game if (mGameState == TicTacToeConstants.GAME_STATE_PLAYING && mAvailableTiles.size() <= 0) { mGameState = TicTacToeConstants.GAME_STATE_CATS_GAME; } } @Override public ArrayList<TicTacToeDrawable> getAvailableTiles() { return mAvailableTiles; } @Override public int getGameState() { return mGameState; } }
The checkWin function is implemented by simply checking the four different directions you can win. It takes a parameter tileState, which is the state of the tile that was last placed. The check win function is called everytime a tile has been placed.
Implementing the GameAI Interface
In this version of Tic Tac Toe that we are building, we are going to implement two different AIs. An easy AI and a difficult AI.
Implementing the EasyAI
EasyAI is actually somewhat of a misnomer. The AI is easy to beat, but a more descriptive name would be random AI. It is certainly possible for this AI to win, but not very likely if it is facing a determined opponent. This implementation just places a piece randomly on the game board.
package com.dreamdom.tictactoe.gamedriver; import java.util.ArrayList; public class EasyGameAI implements GameAI { @Override public TicTacToeDrawable playPiece(GameManager gameManager) { // Pick a piece at random ArrayList<TicTacToeDrawable> availableTiles = gameManager.getAvailableTiles(); TicTacToeDrawable t = availableTiles.get((int) (availableTiles.size()*Math.random())); return t; } }
Implementing the DifficultAI
The difficult AI is a little smarter. It will check to see if it can put a piece down that will cause it to win. If it can, it will perform this move. If it can't it will check to see if it can block the player from winning, and go forward with this move. If all else fails it places a piece randomly.
package com.dreamdom.tictactoe.gamedriver; import java.util.ArrayList; public class HardGameAI implements GameAI { /** * Play piece * Assumes the player is X and the computer is O */ @Override public TicTacToeDrawable playPiece(GameManager gameManager) { ArrayList<TicTacToeDrawable> availableTiles = gameManager.getAvailableTiles(); // Loop through all the tiles and to check for a win for(TicTacToeDrawable t : availableTiles) { if(gameManager.checkWin(TicTacToeConstants.TILE_STATE_O, t.getRow(), t.getCol())) { return t; } } // Loop through all the tiles and prevent a player win for(TicTacToeDrawable t : availableTiles) { if(gameManager.checkWin(TicTacToeConstants.TILE_STATE_X, t.getRow(), t.getCol())) { return t; } } // Default to picking a random tile TicTacToeDrawable t = availableTiles.get((int) (availableTiles.size()*Math.random())); return t; } }
Keep in mind that this AI was not designed to be unbeatable, but rather just something slightly more complex than the EasyAI.
Next in the Tutorial Series
Now that the logic for playing the game exists, we will implement it in an Android App.
No comments:
Post a Comment