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..."); }