Skip to content

Instantly share code, notes, and snippets.

@kingsley-einstein
Created April 5, 2018 16:22
Show Gist options
  • Save kingsley-einstein/3a2f9a5d42d713ce036956ca7f07525b to your computer and use it in GitHub Desktop.
Save kingsley-einstein/3a2f9a5d42d713ce036956ca7f07525b to your computer and use it in GitHub Desktop.
A command line Tic-Tac-Toe game implemented with the minimax algorithm
import java.util.*;
//The move class. This will be instantiated with a row and column argument passed based on user input
class Move {
int r;
int c;
Move(int r, int c) {
this.r=r;
this.c=c;
}
@Override public String toString() {
return "Move [ "+(this.r+1)+" , "+(this.c+1)+" ]";
}
}
//Contains the move and the value allocated for that move
class ChildNodes {
Move move;
int value;
ChildNodes(Move move, int value) {
this.move=move;
this.value=value;
}
@Override public String toString() {
return " value: "+this.value;
}
}
//Contains the board parameters
class Board {
int[][] board=new int[3][3];
char[][] mark=new char[3][3];
List<Move> availableMoves;
List<ChildNodes> childNodes;
/**
*A list that contains every possible moves/empty points on the game board
*/
List<Move> getAvailableMoves() {
availableMoves=new ArrayList<>();
for (int i=0; i<board.length; i++) {
for (int j=0; j<board.length; j++) {
if (board[i][j] == 0) {
availableMoves.add(new Move(i, j));
}
}
}
return availableMoves;
}
//Prints the board
void showBoard() {
for (int i=0; i<board.length; i++) {
for (int j=0; j<board.length; j++) {
if (board[i][j] == 0) {
mark[i][j] = '-';
}
if (board[i][j] == 1) {
mark[i][j] = 'X';
}
if (board[i][j] == 2) {
mark[i][j] = 'O';
}
System.out.print("\t["+mark[i][j]+"]\t");
}
System.out.println();
}
}
//Shows hints.
void showHint() {
System.out.println();
for (int i=1; i<=3; i++) {
for (int j=1; j<=3; j++) {
System.out.print("\t["+i+""+j+"]\t");
}
System.out.println();
}
}
//Returns true if X has won. False otherwise
boolean xHasWon() {
for (int i=0; i<board.length; i++) {
if ((board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] == 1) || (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[2][i] == 1)) {
return true;
}
if ( i == 0) {
if ((board[i][i] == board[1][1] && board[1][1] == board[2][2] && board[2][2] == 1) || (board[i][2] == board[1][1] && board[1][1] == board[2][i] && board[i][2] == 1)) {
return true;
}
}
}
return false;
}
//Returns true if O has won. false otherwise.
boolean oHasWon() {
for (int i=0; i<board.length; i++) {
if ((board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] == 2) || (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[2][i] == 2)) {
return true;
}
if ( i == 0) {
if ((board[i][i] == board[1][1] && board[1][1] == board[2][2] && board[2][2] == 2) || (board[i][2] == board[1][1] && board[1][1] == board[2][i] && board[i][2] == 2)) {
return true;
}
}
}
return false;
}
//Checks if game is over
boolean isGameOver() {
return xHasWon() || oHasWon() || getAvailableMoves().isEmpty();
}
void placeMove(Move m, int player) {
board[m.r][m.c]=player;
}
/**The AI algorithm. Iterates through the game tree and checks every possible move
*/
int minimax(int depth, boolean turn) {
List<Move> legalStates=getAvailableMoves();
if (xHasWon()) {
return +1;
}
if (oHasWon()) {
return -1;
}
if (legalStates.isEmpty() && !xHasWon() && !oHasWon()) {
return 0;
}
int max=Integer.MIN_VALUE;
int min=Integer.MAX_VALUE;
if (turn) {
for (int i=0; i<legalStates.size(); i++) {
Move move=legalStates.get(i);
placeMove(move, 1);
int score=minimax(depth+1, false);
max=Math.max(max, score);
if (depth == 0 ) {
childNodes.add(new ChildNodes(move, max));
}
placeMove(move, 0);
}
}
else {
for (int i=0; i<legalStates.size(); i++) {
Move move=legalStates.get(i);
placeMove(move, 2);
int score=minimax(depth+1, true);
min=Math.min(min, score);
if (depth == 0) {
childNodes.add(new ChildNodes(move, min));
}
placeMove(move, 0);
}
}
return turn?max:min;
}
Move getBestMove() {
int max=Integer.MIN_VALUE;
int index=0;
for (int i=0; i<childNodes.size(); i++) {
if (max < childNodes.get(i).value) {
max = childNodes.get(i).value;
index = i;
}
}
return childNodes.get(index).move;
}
void callMinimax(int depth, boolean turn) {
childNodes=new ArrayList<>();
minimax(depth, turn);
}
void newGame() {
System.out.println("Welcome to J-Tac-Toe.\n ************************************************************ \n Author: Kingsley Victor \n Version: 1.0.0 \n ************************************************************ ");
showBoard(); showHint();
System.out.println("\n To place a mark, enter the corresponding row and column value\n \n");
Scanner gameInput=new Scanner(System.in);
System.out.println("Who plays first? Computer (1), User(2)");
int choice=gameInput.nextInt();
while (choice < 1 || choice > 2) {
System.out.println("Invalid choice. Enter either 1 or 2 for computer or human to make first move respectively");
choice=gameInput.nextInt();
}
if (choice == 1) {
Random rand=new Random();
Move m=new Move(rand.nextInt(3), rand.nextInt(3));
placeMove(m, 1);
showBoard();
showHint();
}
while (!isGameOver()) {
System.out.println("Make your move. \n \n");
int r=gameInput.nextInt()-1;
int c=gameInput.nextInt()-1;
Move m=new Move(r, c);
while(board[m.r][m.c] !=0) {
System.out.println("Invalid move. Point is already occupied \n \n");
r=gameInput.nextInt()-1;
c=gameInput.nextInt()-1;
m=new Move(r, c);
}
placeMove(m, 2);
showBoard();
callMinimax(0, true);
if (isGameOver()) {
System.out.println();
showBoard();
break;
}
try {
System.out.println();
System.out.println("Wait! Computer is thinking!");
System.out.println();
Thread.sleep(2000);
}
catch(InterruptedException e) {
}
placeMove(getBestMove(), 1);
showBoard();
if (!isGameOver()) {
showHint();
}
}
if (xHasWon()) {
System.out.println("Computer wins the game");
}
if (oHasWon()) {
System.out.println("Congratulations, you win the game");
}
if (getAvailableMoves().isEmpty() && !xHasWon() && !oHasWon()) {
System.out.println("It's a tie!");
}
}
}
public class TicTacToe {
public static void main(String[] args) {
Board b=new Board();
b.newGame();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment