import java.util.Scanner;
public class Main {
// global variables
private static String[] board = new String[9];
private static int turn = 0;
private static boolean playing = false;
private static Player player1 = new Player(1, "X", false);
private static Player player2 = new Player(2, "O", false);
public static void main(String[] args) {
// caleb b
// Tic Tac Toe
for(int i = 0; i < board.length; i++) {
// Set up the board by setting each index to its index number
board[i] = String.valueOf(i);
}
// Print the welcome message & choice messages
System.out.println("Welcome to Tic Tac Toe");
System.out.println("Would you like to play against another player or against a computer?");
System.out.println("1. Player");
System.out.println("2. Computer");
// Get the user's choice
Scanner input = new Scanner(System.in);
int choice = input.nextInt();
if (choice == 2) {
player1.isComputer = true;
} else {
player1.isComputer = false;
}
// Start the game loop
playing = true;
while(playing) {
clearScreen();
printBoard();
if (turn % 2 == 0) {
// Handle turns for player 1
if (player1.isComputer) {
// Computer logic
System.out.println("Computer's turn");
int computerMove = player1.computerMove(); // Use the minimax algorithm to pick the best move
try {
board[computerMove] = player1.playerSymbol;
} catch (ArrayIndexOutOfBoundsException e) {
board[computerMove - 1] = player1.playerSymbol;
}
turn++;
sleep(1000);
if (checkWin(player1.playerSymbol)) {
// Check if the computer won
clearScreen();
printBoard();
System.out.println("Computer won!");
playing = false;
continue;
}
} else {
// Regular player 1 logic
System.out.println("Player 1's turn");
System.out.println("Enter a number to place your symbol");
int move = input.nextInt();
if (move < 0 || move > 8) {
System.out.println("That is not a valid move!");
continue;
}
if (!board[move].equals(String.valueOf(move))) {
System.out.println("That space is already taken!");
continue;
}
board[move] = player1.playerSymbol;
if (checkWin(player1.playerSymbol)) {
clearScreen();
printBoard();
System.out.println("Player 1 won!");
playing = false;
continue;
}
turn++;
}
} else {
// Player 2 logic
System.out.println("Player 2's turn");
System.out.println("Enter a number to place your symbol (0-8)");
int move = input.nextInt();
if (move < 0 || move > 8) {
System.out.println("That is not a valid move!");
continue;
}
if (!board[move].equals(String.valueOf(move))) {
System.out.println("That space is already taken!");
continue;
}
board[move] = player2.playerSymbol;
if (checkWin(player2.playerSymbol)) {
// Check if player 2 won
clearScreen();
printBoard();
System.out.println("Player 2 won!");
playing = false;
continue;
}
turn++;
}
if (Player.getAvailableMoves(board).length == 0) {
// Check if the board is full, if it is, then there was a draw.
clearScreen();
printBoard();
System.out.println("It's a tie!");
playing = false;
break;
}
}
// Ask to play again
System.out.println("Thanks for playing!");
System.out.println("Play again? (y/n)");
Scanner newInput = new Scanner(System.in);
String playAgain = newInput.nextLine();
if (playAgain.toLowerCase().equals("y")) {
// Reset the board and turn counter, then start a new game
turn = 0;
board = new String[9];
main(args);
} else {
System.out.println("Goodbye!");
}
}
// Quick function to clear the screen on command, just prints 100 blank lines.
public static void clearScreen() {
for (int i = 0; i < 100; i++) {
System.out.println();
}
}
// Quick function for safe Thread sleeping.
public static void sleep(double millis) {
try {
Thread.sleep((long) millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Function to print the board in a nice format
public static void printBoard() {
String[] board2 = board.clone();
for (int i = 0; i < board.length; i++) {
if (board2[i].equals(String.valueOf(i))) {
// If the index is equal to its index number, then it is empty, so set it to a space
board2[i] = " ";
}
}
System.out.println(" " + board2[0] + " | " + board2[1] + " | " + board2[2] + " ");
System.out.println("-----------");
System.out.println(" " + board2[3] + " | " + board2[4] + " | " + board2[5] + " ");
System.out.println("-----------");
System.out.println(" " + board2[6] + " | " + board2[7] + " | " + board2[8] + " ");
System.out.println();
}
// Function to check if a player has won
public static boolean checkWin(String symbol) {
if (board[0].equals(symbol) && board[1].equals(symbol) && board[2].equals(symbol)) {
return true;
}
if (board[3].equals(symbol) && board[4].equals(symbol) && board[5].equals(symbol)) {
return true;
}
if (board[6].equals(symbol) && board[7].equals(symbol) && board[8].equals(symbol)) {
return true;
}
if (board[0].equals(symbol) && board[3].equals(symbol) && board[6].equals(symbol)) {
return true;
}
if (board[1].equals(symbol) && board[4].equals(symbol) && board[7].equals(symbol)) {
return true;
}
if (board[2].equals(symbol) && board[5].equals(symbol) && board[8].equals(symbol)) {
return true;
}
if (board[0].equals(symbol) && board[4].equals(symbol) && board[8].equals(symbol)) {
return true;
}
if (board[2].equals(symbol) && board[4].equals(symbol) && board[6].equals(symbol)) {
return true;
}
return false;
}
// Getters and setters for global variables so they can be accessed from other classes safely.
public static String[] getBoard() {
return board;
}
public static void setBoard(String[] board) {
Main.board = board;
}
public static int getTurn() {
return turn;
}
public static void setTurn(int turn) {
Main.turn = turn;
}
public static Player getPlayer1() {
return player1;
}
public static void setPlayer1(Player player1) {
Main.player1 = player1;
}
public static Player getPlayer2() {
return player2;
}
public static void setPlayer2(Player player2) {
Main.player2 = player2;
}
public static boolean isPlaying() {
return playing;
}
public static void setPlaying(boolean playing) {
Main.playing = playing;
}
}
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class Player {
public int playerNumber;
public String playerSymbol;
public boolean isComputer;
private List<Integer> moves = new ArrayList<>();
// Constructor, take in the player number, symbol, and whether or not the player is a computer
public Player(int playerNumber, String playerSymbol, boolean isComputer) {
this.playerNumber = playerNumber;
this.playerSymbol = playerSymbol;
this.isComputer = isComputer;
}
// Logic for the computer minmaxing algorithm
public int computerMove() {
// Get the board and available moves
String[] board = Main.getBoard();
String[] availableMoves = getAvailableMoves(board);
int bestMove = -1;
int bestScore = -1000;
// The computer always goes first, so pick a random good location for the first move.
if (Main.getTurn() == 0 || Main.getTurn() == 1) {
double random = Math.random();
if (random < 0.33) {
return 0;
} else if (random < 0.66) {
return 4;
} else {
return 8;
}
}
// Loop through each available move and get the score for each move
for (int i = 0; i < availableMoves.length; i++) {
String[] boardCopy = board.clone();
boardCopy[Integer.parseInt(availableMoves[i])] = "X";
int score = minimax(boardCopy, getAvailableMoves(boardCopy), false);
// If the score is better than the current best score, set the best score to the current score and set the best move to the current move
if (score > bestScore) {
bestScore = score;
bestMove = Integer.parseInt(availableMoves[i]);
}
}
// Return the best move
return bestMove;
}
private int minimax(String[] board, String[] availableMoves, boolean isMaximizing) {
// While iterating through the available moves, check if the current move is a winning move, a losing move, or a tie.
if (winning(board, "O")) {
return -10;
} else if (winning(board, "X")) {
return 10;
} else if (availableMoves.length == 0) {
return 0;
}
int bestScore = isMaximizing ? Integer.MIN_VALUE : Integer.MAX_VALUE;
// for each available move, make a copy of the board, place the move, and then call the minimax function again with the new board and available moves
for (int i = 0; i < availableMoves.length; i++) {
String[] boardCopy = board.clone();
boardCopy[Integer.parseInt(availableMoves[i])] = isMaximizing ? "X" : "O";
int score = minimax(boardCopy, getAvailableMoves(boardCopy), !isMaximizing);
if (isMaximizing) {
bestScore = Math.max(bestScore, score);
} else {
bestScore = Math.min(bestScore, score);
}
// Undo the move for the next iteration
boardCopy[Integer.parseInt(availableMoves[i])] = availableMoves[i];
}
return bestScore;
}
static String[] getAvailableMoves(String[] board) {
// Get the available moves by checking if the index is equal to its index number
List<String> availableMoves = new ArrayList<>();
for (int i = 0; i < board.length; i++) {
if (board[i].equals(String.valueOf(i))) {
availableMoves.add(String.valueOf(i));
}
}
return availableMoves.toArray(new String[0]);
}
public static boolean winning(String[] board, String symbol) {
// Check if a player has won
if (board[0].equals(symbol) && board[1].equals(symbol) && board[2].equals(symbol)) {
return true;
}
if (board[3].equals(symbol) && board[4].equals(symbol) && board[5].equals(symbol)) {
return true;
}
if (board[6].equals(symbol) && board[7].equals(symbol) && board[8].equals(symbol)) {
return true;
}
if (board[0].equals(symbol) && board[3].equals(symbol) && board[6].equals(symbol)) {
return true;
}
if (board[1].equals(symbol) && board[4].equals(symbol) && board[7].equals(symbol)) {
return true;
}
if (board[2].equals(symbol) && board[5].equals(symbol) && board[8].equals(symbol)) {
return true;
}
if (board[0].equals(symbol) && board[4].equals(symbol) && board[8].equals(symbol)) {
return true;
}
if (board[2].equals(symbol) && board[4].equals(symbol) && board[6].equals(symbol)) {
return true;
}
return false;
}
}