Created
March 28, 2022 14:33
-
-
Save Da9el00/c6a4794c17039a7d62761a256d1b4c93 to your computer and use it in GitHub Desktop.
Tic-Tac-Toe Minimax search AI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
public class AdversarialSearchTicTacToe { | |
public int minMaxDecision(State state){ | |
ArrayList<State> possibleMoves = successorsOf(state); | |
ArrayList<Integer> movesList = new ArrayList<>(); | |
for (State states: possibleMoves) { | |
movesList.add(minValue(states)); | |
} | |
int max = movesList.get(0); | |
int bestIndex = 0; | |
for (int i = 1; i < movesList.size(); i++) { | |
if( movesList.get(i) > max){ | |
max = movesList.get(i); | |
bestIndex = i; | |
} | |
} | |
System.out.println(possibleMoves); | |
System.out.println(movesList); | |
int action = possibleMoves.get(bestIndex).getPosition(); | |
System.out.println("Action: " + action); | |
return action; | |
} | |
//Picks best option for the X-player | |
private int maxValue(State state){ | |
if (isTerminal(state)){ | |
return utilityOf(state); | |
} | |
int v = (int) -Double.POSITIVE_INFINITY; | |
for (State possibleMove: successorsOf(state)) { | |
v = Math.max(v, minValue(possibleMove)); | |
} | |
System.out.println(v); | |
return v; | |
} | |
//Picks best option for the O-player | |
private int minValue(State state){ | |
if (isTerminal(state)){ | |
return utilityOf(state); | |
} | |
int v = (int) Double.POSITIVE_INFINITY; | |
for (State possibleMove: successorsOf(state)) { | |
v = Math.min(v, maxValue(possibleMove)); | |
} | |
System.out.println(v); | |
return v; | |
} | |
//Returns true if the game is over | |
public boolean isTerminal(State state) { | |
int takenSpots = 0; | |
for (int a = 0; a < 9; a++) { | |
if(state.getStateIndex(a).equals("X") || state.getStateIndex(a).equals("O") ){ | |
takenSpots++; | |
} | |
String line = checkState(state, a); | |
//Check for Winners | |
if (line.equals("XXX")) { | |
return true; | |
} else if (line.equals("OOO")) { | |
return true; | |
} | |
if(takenSpots == 9){ | |
return true; | |
} | |
} | |
return false; | |
} | |
//Returns +1 if X is winner | |
//Return -1 if O is winner | |
//Returns 0 if no one won | |
private int utilityOf(State state){ | |
for (int a = 0; a < 8; a++) { | |
String line = checkState(state, a); | |
//Check for Winners | |
if (line.equals("XXX")) { | |
return 1; | |
} else if (line.equals("OOO")) { | |
return -1; | |
} | |
} | |
return 0; | |
} | |
//Find any win state if it exists | |
private String checkState(State state, int a) { | |
return switch (a) { | |
case 0 -> state.getStateIndex(0) + state.getStateIndex(1) + state.getStateIndex(2); | |
case 1 -> state.getStateIndex(3) + state.getStateIndex(4) + state.getStateIndex(5); | |
case 2 -> state.getStateIndex(6) + state.getStateIndex(7) + state.getStateIndex(8); | |
case 3 -> state.getStateIndex(0) + state.getStateIndex(3) + state.getStateIndex(6); | |
case 4 -> state.getStateIndex(1) + state.getStateIndex(4) + state.getStateIndex(7); | |
case 5 -> state.getStateIndex(2) + state.getStateIndex(5) + state.getStateIndex(8); | |
case 6 -> state.getStateIndex(0) + state.getStateIndex(4) + state.getStateIndex(8); | |
case 7 -> state.getStateIndex(2) + state.getStateIndex(4) + state.getStateIndex(6); | |
default -> ""; | |
}; | |
} | |
//Returns all possible states form a given state | |
private ArrayList<State> successorsOf(State state){ | |
ArrayList<State> possibleMoves = new ArrayList<>(); | |
int xMoves = 0; | |
int yMoves = 0; | |
String player; | |
//Calculate player turn | |
for (String s: state.getState()) { | |
if (s.equals("X")) { | |
xMoves++; | |
}else if(s.equals("O")){ | |
yMoves++; | |
} | |
} | |
if(xMoves <= yMoves){ | |
player = "X"; | |
} else { | |
player = "O"; | |
} | |
//Create all possible states | |
for (int i = 0; i < 9; i++) { | |
String[] newState = state.getState().clone(); | |
if(newState[i] != "X" && newState[i] != "O"){ | |
newState[i] = player; | |
possibleMoves.add(new State(i, newState)); | |
} | |
} | |
return possibleMoves; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package tictactoe; | |
import java.util.Scanner; | |
public class Main { | |
public static void main(String[] args) { | |
AdversarialSearchTicTacToe adsTicTacToe = new AdversarialSearchTicTacToe(); | |
String[] board = {"0","1","2","3","4","5","6","7","8"}; | |
State state = new State(0,board); | |
Scanner scanner = new Scanner(System.in); | |
while (!adsTicTacToe.isTerminal(state)){ | |
board[adsTicTacToe.minMaxDecision(state)] = "X"; | |
if (!adsTicTacToe.isTerminal(state)){ | |
drawBoard(state); | |
System.out.print(": "); | |
int userInput = Integer.parseInt(scanner.nextLine()); | |
state.changeState(userInput, "O"); | |
} | |
} | |
drawBoard(state); | |
System.out.println("Game is over"); | |
} | |
public static void drawBoard(State state){ | |
for (int i = 0; i < 7; i +=3) { | |
System.out.println(state.getStateIndex(i) + " " | |
+ state.getStateIndex(i + 1) + " " + state.getStateIndex(i + 2)); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package tictactoe; | |
import java.util.Arrays; | |
public class State { | |
private int position; | |
private String[] state; | |
public State(int position, String[] state) { | |
this.position = position; | |
this.state = state; | |
} | |
public int getPosition() { | |
return position; | |
} | |
public void setPosition(int position) { | |
this.position = position; | |
} | |
public String[] getState() { | |
return state; | |
} | |
public String getStateIndex(int i){ | |
return state[i]; | |
} | |
public void setState(String[] state) { | |
this.state = state; | |
} | |
public void changeState(int i, String player){ | |
state[i] = player; | |
} | |
@Override | |
public String toString() { | |
return "State{" + | |
"position=" + position + | |
", state=" + Arrays.toString(state) + | |
'}'; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment