Two player game program – e.g. TIC TAC TOE…

This is an abstract State class
public abstract class State {

// This method is abstract, the extending class has to give the implementation
// It will return a list of all the possible children from the current state.
public abstract List getChildren();
public abstract String getTurn();
public abstract void setTurn(String turn);

// Find out who is the winner from the current state.
public abstract String getWinner();

// Check if the game has been finished or not.
public abstract boolean isGameOver();

// I have given the implementation specifically for TIC-TAC-TOE.
// You can override this functionality in the extending class according to your game requirements.
//
// This function will give the score of the current state.
// Then you can compare it with its siblings to check which state will be better to move to..
//
public int minimax()
{

if(!getWinner().equals(""))
{
if("X".equalsIgnoreCase(getWinner()))
{
return 1;
}
if("O".equalsIgnoreCase(getWinner()))
{
return -1;
}
if("draw".equalsIgnoreCase(getWinner()))
{
return 0;
}
}
List children = getChildren();
if(getTurn().equals("X"))
{
int maxScore = -100;
for(State child : children)
{
int childScore = child.minimax();
if(childScore > maxScore)
{
maxScore = childScore;
}
}
return maxScore;
}
else
{
int minScore = 100;
for(State child : children)
{
int childScore = child.minimax();
if(childScore < minScore)
{
minScore = childScore;
}
}
return minScore;
}

}
}

Extend the above State class and give some implementations…as follows:
Lets assume the class to be TttState…
We’ll override the abstract methods of the parent class State..

Give a member variable that’ll hold the current state of your game…In my case, I have taken it as array..

// This variable is going to hold the current state of your game.
// It can be any depending on your game requirements...
private String state[][] = null ;

Give a copy constructor

public TttState(State x)
{
TttState tState = (TttState)x;
state = new String[3][3];
for(int i=0;i<=2; i++)
{
for(int j=0; j<=2;j++)
{
this.state[i][j]=tState.state[i][j];
}
}
this.turn = x.getTurn();
}

Give a default constructor

public TttState() {
state = new String[3][3];
for(int i=0;i<=2; i++)
{
for(int j=0; j<=2;j++)
{
this.state[i][j]="";
}
}
this.turn = "X";
}

Give a toString implementation, so that you can print the current state

public String toString()
{
StringBuilder sb = new StringBuilder("");
for(int i=0;i<=2; i++)
{
for(int j=0; j<=2;j++)
{
if(state[i][j].equals(""))
{
sb.append("-");
}
else
{
sb.append(state[i][j]);
}
sb.append("t");
}
sb.append("n");
}
return sb.toString();
}

Override the method getWinner() as follows

public String getWinner()
{
// ROW CHECK BEGINS
if(state[0][0].equalsIgnoreCase("X") && state[0][1].equalsIgnoreCase("X") && state[0][2].equalsIgnoreCase("X"))
{
return "X";
}
if(state[0][0].equalsIgnoreCase("O") && state[0][1].equalsIgnoreCase("O") && state[0][2].equalsIgnoreCase("O"))
{
return "O";
}
if(state[1][0].equalsIgnoreCase("X") && state[1][1].equalsIgnoreCase("X") && state[1][2].equalsIgnoreCase("X"))
{
return "X";
}
if(state[1][0].equalsIgnoreCase("O") && state[1][1].equalsIgnoreCase("O") && state[1][2].equalsIgnoreCase("O"))
{
return "O";
}
if(state[2][0].equalsIgnoreCase("X") && state[2][1].equalsIgnoreCase("X") && state[2][2].equalsIgnoreCase("X"))
{
return "X";
}
if(state[2][0].equalsIgnoreCase("O") && state[2][1].equalsIgnoreCase("O") && state[2][2].equalsIgnoreCase("O"))
{
return "O";
}

// COLUMN CHECK BEGINS
if(state[0][0].equalsIgnoreCase("X") && state[1][0].equalsIgnoreCase("X") && state[2][0].equalsIgnoreCase("X"))
{
return "X";
}
if(state[0][0].equalsIgnoreCase("O") && state[1][0].equalsIgnoreCase("O") && state[2][0].equalsIgnoreCase("O"))
{
return "O";
}
if(state[0][1].equalsIgnoreCase("X") && state[1][1].equalsIgnoreCase("X") && state[2][1].equalsIgnoreCase("X"))
{
return "X";
}
if(state[0][1].equalsIgnoreCase("O") && state[1][1].equalsIgnoreCase("O") && state[2][1].equalsIgnoreCase("O"))
{
return "O";
}
if(state[0][2].equalsIgnoreCase("X") && state[1][2].equalsIgnoreCase("X") && state[2][2].equalsIgnoreCase("X"))
{
return "X";
}
if(state[0][2].equalsIgnoreCase("O") && state[1][2].equalsIgnoreCase("O") && state[2][2].equalsIgnoreCase("O"))
{
return "O";
}
// DIAGNONAL CHECKS
if(state[0][0].equalsIgnoreCase("X") && state[1][1].equalsIgnoreCase("X") && state[2][2].equalsIgnoreCase("X"))
{
return "X";
}
if(state[0][0].equalsIgnoreCase("O") && state[1][1].equalsIgnoreCase("O") && state[2][2].equalsIgnoreCase("O"))
{
return "O";
}

if(state[0][2].equalsIgnoreCase("X") && state[1][1].equalsIgnoreCase("X") && state[2][0].equalsIgnoreCase("X"))
{
return "X";
}
if(state[0][2].equalsIgnoreCase("O") && state[1][1].equalsIgnoreCase("O") && state[2][0].equalsIgnoreCase("O"))
{
return "O";
}
int nonBlank = 0;
for(int i=0;i<=2;i++)
{
for(int j=0;j<=2;j++)
{
if(!state[i][j].equals(""))
{
nonBlank++;
}
}
}
if(nonBlank==9)
{
return "draw";
}

return "";
}

Override the method isGameOver()

public boolean isGameOver()
{
if(!getWinner().equals("") && !getWinner().equals("draw"))
{
return true;
}
else
{
return false;
}
}

Override the method getChildren()

@Override
public List getChildren() {
List children = new ArrayList();
if(turn.equals("X"))
{
for(int i=0;i<=2; i++)
{
for(int j=0; j<=2;j++)
{
if(state[i][j].equals(""))
{
TttState child = new TttState(this);
child.state[i][j]="X";
String newTurn = "O";
child.setTurn(newTurn);
children.add(child);
}
}
}
}
else
{
for(int i=0;i<=2; i++)
{
for(int j=0; j<=2;j++)
{
if(state[i][j].equals(""))
{
TttState child = new TttState(this);
child.state[i][j]="O";
String newTurn = "X";
child.setTurn(newTurn);
children.add(child);
}
}
}
}
return children;
}

Override getTurn and setTurn

public String getTurn() {
return turn;
}

public void setTurn(String turn) {
this.turn = turn;
}

Your main program will look something like this:

public static void main(String args[])
{
TttState x = new TttState();
System.out.println("WINNER : " + x.getWinner());
x.setTurn("X");
Scanner sc = new Scanner(System.in);
while(!x.isGameOver())
{
String xIndex = sc.nextLine();
String yIndex = sc.nextLine();
int xint = Integer.parseInt(xIndex);
int yint = Integer.parseInt(yIndex);
x.getState()[xint][yint]="X";
x.setTurn("O");
int minScore = 100;
List children = x.getChildren();
TttState bestMove = new TttState(x);
for(State child : children)
{
int childScore = child.minimax();
// We are using < because we are always evaluating
// the best move from the options that O player has....
if(childScore < minScore)
{
minScore = childScore;
bestMove = (TttState)child;
bestMove.setTurn("X");
}
}
System.out.print(bestMove);
x = bestMove;
}
System.out.println("GAME OVER...");

}

Leave a Reply