Wednesday, July 30, 2014

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<State> 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<State> 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<State> getChildren() {
  List<State> children = new ArrayList<State>();
  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<State> 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...");

 }


No comments:

Post a Comment