Skip to content

Instantly share code, notes, and snippets.

@Da9el00
Created March 28, 2022 14:33
Show Gist options
  • Save Da9el00/c6a4794c17039a7d62761a256d1b4c93 to your computer and use it in GitHub Desktop.
Save Da9el00/c6a4794c17039a7d62761a256d1b4c93 to your computer and use it in GitHub Desktop.
Tic-Tac-Toe Minimax search AI
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;
}
}
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));
}
}
}
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