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