Last active
August 21, 2018 19:19
-
-
Save mAlaliSy/efd83bf3f4e150059b84db4ed6c16b33 to your computer and use it in GitHub Desktop.
This code is for the article: https://3alam.pro/mohammadalali/articles/adversarial-search/
This file contains 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; | |
import java.util.List; | |
import java.util.Random; | |
import java.util.Scanner; | |
public class XOGame { | |
private static final int DEPTH = 9; | |
private static int[] iStart = {0, 1, 2, 0, 0, 0, 0, 0}; | |
private static int[] jStart = {0, 0, 0, 0, 1, 2, 0, 2}; | |
private static int[] iIncrement = {0, 0, 0, 1, 1, 1, 1, 1}; | |
private static int[] jIncrement = {1, 1, 1, 0, 0, 0, 1, -1}; | |
private static Scanner scanner; | |
public static void main(String[] args) { | |
scanner = new Scanner(System.in); | |
System.out.println( | |
"1\t|\t2\t|\t3\t\n" + | |
"4\t|\t5\t|\t6\t\n" + | |
"7\t|\t8\t|\t9\t\n"); | |
char[][] board = new char[3][3]; | |
for (int i = 0; i < 3; i++) { | |
for (int j = 0; j < 3; j++) { | |
board[i][j] = ' '; | |
} | |
} | |
char winner = ' '; | |
boolean playerTurn = new Random().nextBoolean(); | |
char playerLetter; | |
char computerLetter; | |
if (playerTurn) { | |
playerLetter = 'X'; | |
computerLetter = 'O'; | |
} else { | |
playerLetter = 'O'; | |
computerLetter = 'X'; | |
} | |
if (playerTurn) { | |
playerTurn(board, playerLetter); | |
printBoard(board); | |
} | |
do { | |
int bestMove = minMax(board, DEPTH, computerLetter); | |
board[bestMove / 3][bestMove % 3] = computerLetter; | |
printBoard(board); | |
winner = getWinnerIfThere(board); | |
if (winner == ' ' && !isDraw(board)) { | |
playerTurn(board, playerLetter); | |
printBoard(board); | |
} else { | |
/* | |
* Computer Won Or Draw, Break! | |
* */ | |
break; | |
} | |
playerTurn = !playerTurn; | |
winner = getWinnerIfThere(board); | |
} while (!isDraw(board) && winner == ' '); | |
if (winner == ' ') { | |
System.out.println("Draw"); | |
} else { | |
System.out.println(winner + " Wins"); | |
} | |
} | |
private static void playerTurn(char[][] board, char playerLetter) { | |
System.out.println("#===============Your Turn===============#"); | |
boolean validMove; | |
do { | |
System.out.print("Enter a number for an empty square:"); | |
int move = scanner.nextInt(); | |
if (move > 9 || move < 1 || board[(move - 1) / 3][(move - 1) % 3] != ' ') | |
validMove = false; | |
else { | |
board[(move - 1) / 3][(move - 1) % 3] = playerLetter; | |
validMove = true; | |
} | |
if (!validMove) | |
System.out.println("Invalid Square!"); | |
} while (!validMove); | |
} | |
private static void printBoard(char[][] board) { | |
System.out.println("-------------------------"); | |
for (int i = 0; i < 3; i++) { | |
System.out.print("|\t"); | |
for (int j = 0; j < 3; j++) { | |
System.out.print(board[i][j] + "\t|\t"); | |
} | |
System.out.println("\n-------------------------"); | |
} | |
System.out.println(); | |
} | |
private static List<Integer> availableMoves(char[][] board) { | |
List<Integer> availableSquares = new ArrayList<>(); | |
for (int i = 0; i < 3; i++) { | |
for (int j = 0; j < 3; j++) { | |
if (board[i][j] == ' ') | |
availableSquares.add(i * 3 + j); | |
} | |
} | |
return availableSquares; | |
} | |
private static int evaluate(char[][] board, char player) { | |
int value = 0; | |
/* | |
* For each line of the eight lines | |
* */ | |
for (int line = 0; line < iStart.length; line++) { | |
int i = iStart[line]; | |
int j = jStart[line]; | |
/* | |
* Number of squares in the line for each player | |
* */ | |
int playersSquare = 0; | |
int opponentSquare = 0; | |
int emptySquares = 0; | |
int k = 0; | |
do { | |
char currentSquare = board[i][j]; | |
if (currentSquare != ' ') { | |
if (currentSquare == player) | |
playersSquare++; | |
else | |
opponentSquare++; | |
} else | |
emptySquares++; | |
i += iIncrement[line]; | |
j += jIncrement[line]; | |
k++; | |
} while (k < 3); | |
if (playersSquare == 2 && emptySquares == 1) | |
value += 10; | |
else if (playersSquare == 1 && emptySquares == 2) | |
value += 1; | |
if (opponentSquare == 2 && emptySquares == 1) | |
value -= 10; | |
else if (opponentSquare == 1 && emptySquares == 2) | |
value -= 1; | |
} | |
return value; | |
} | |
private static int minMax(char[][] board, int depth, char player) { | |
double bestMoveValue = Double.NEGATIVE_INFINITY; | |
int bestMove = 0; | |
for (int move : availableMoves(board)) { | |
/* | |
* Avoid modifying the board because it is needed for the other moves | |
* */ | |
char[][] copyBoard = copyBoard(board); | |
copyBoard[move / 3][move % 3] = player; | |
double moveValue = min(copyBoard, depth - 1, player); | |
if (moveValue > bestMoveValue) { | |
bestMoveValue = moveValue; | |
bestMove = move; | |
} | |
} | |
return bestMove; | |
} | |
private static double min(char[][] board, int depth, char player) { | |
char winner = getWinnerIfThere(board); | |
if (winner != ' ') { | |
if (winner == player) { | |
return Double.POSITIVE_INFINITY; | |
} else { | |
return Double.NEGATIVE_INFINITY; | |
} | |
} | |
if (isDraw(board)) | |
return 0; | |
if (depth == 0) { | |
return evaluate(board, player); | |
} | |
char opponent = player == 'X' ? 'O' : 'X'; | |
double bestMoveValue = Double.POSITIVE_INFINITY; | |
for (int move : availableMoves(board)) { | |
char[][] copyBoard = copyBoard(board); | |
copyBoard[move / 3][move % 3] = opponent; | |
double moveValue = max(copyBoard, depth - 1, player); | |
if (moveValue < bestMoveValue) { | |
bestMoveValue = moveValue; | |
} | |
} | |
return bestMoveValue; | |
} | |
private static double max(char[][] board, int depth, char player) { | |
char winner = getWinnerIfThere(board); | |
if (winner != ' ') { | |
if (winner == player) { | |
return Double.POSITIVE_INFINITY; | |
} else { | |
return Double.NEGATIVE_INFINITY; | |
} | |
} | |
if (isDraw(board)) | |
return 0; | |
if (depth == 0) { | |
return evaluate(board, player); | |
} | |
double bestMoveValue = Double.NEGATIVE_INFINITY; | |
for (int move : availableMoves(board)) { | |
char[][] copyBoard = copyBoard(board); | |
copyBoard[move / 3][move % 3] = player; | |
double moveValue = min(copyBoard, depth - 1, player); | |
if (moveValue > bestMoveValue) { | |
bestMoveValue = moveValue; | |
} | |
} | |
return bestMoveValue; | |
} | |
private static char getWinnerIfThere(char[][] board) { | |
for (int line = 0; line < iStart.length; line++) { | |
boolean xWin = true; | |
boolean oWin = true; | |
int i = iStart[line]; | |
int j = jStart[line]; | |
int k = 0; | |
do { | |
char currentSquare = board[i][j]; | |
if (currentSquare != 'X') { | |
xWin = false; | |
} | |
if (currentSquare != 'O') { | |
oWin = false; | |
} | |
i += iIncrement[line]; | |
j += jIncrement[line]; | |
k++; | |
} while (k < 3); | |
if (xWin) | |
return 'X'; | |
if (oWin) | |
return 'O'; | |
} | |
return ' '; | |
} | |
private static boolean isDraw(char[][] board) { | |
for (int i = 0; i < 3; i++) { | |
for (int j = 0; j < 3; j++) { | |
if (board[i][j] == ' ') | |
return false; | |
} | |
} | |
return true; | |
} | |
private static char[][] copyBoard(char[][] board) { | |
char[][] copyBoard = new char[3][3]; | |
for (int i = 0; i < 3; i++) { | |
System.arraycopy(board[i], 0, copyBoard[i], 0, 3); | |
} | |
return copyBoard; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment