import java.awt.Font; import java.awt.Graphics; import java.awt.Color; import java.util.ArrayList; import java.util.TreeSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; /** This class sets up and solves arbitrary games of * Tic-Tac-Toe. * * It animates the process to help show what recursive * backtracking looks like. * * @author Adam Blank * */ public class TicTacToe { public static final int SQUARE_SIZE = 200; public static final int HEIGHT = 3 * SQUARE_SIZE + 2; public static final int WIDTH = 3 * SQUARE_SIZE + 2; public static DrawingPanel panel; public static Map moves = new TreeMap<>(); public static void makeMove(Point p, String player) { Graphics g = panel.getGraphics(); Font font = new Font("Arial", Font.PLAIN, 250); g.setFont(font); int x = p.getX(); int y = p.getY(); g.drawString(player, x *(1 + SQUARE_SIZE) + 5, (1 + y) * (1 + SQUARE_SIZE) - 10); moves.put(p, player); } public static void undoMove(Point p) { Graphics g = panel.getGraphics(); int x = p.getX(); int y = p.getY(); g.setColor(Color.WHITE); g.fillRect(x*SQUARE_SIZE + 5, y*SQUARE_SIZE + 5, SQUARE_SIZE - 5, SQUARE_SIZE - 5); moves.put(p, ""); g.setColor(Color.BLACK); } public static String wonGame(String player) { Graphics g = panel.getGraphics(); Set inDiag1 = new TreeSet<>(); Set inDiag2 = new TreeSet<>(); for (int i = 0; i < 3; i++) { inDiag1.add(moves.get(new Point(i, i))); inDiag2.add(moves.get(new Point(2 - i, i))); Set inRow = new TreeSet<>(); Set inCol = new TreeSet<>(); for (int j = 0; j < 3; j++) { inRow.add(moves.get(new Point(i, j))); inCol.add(moves.get(new Point(j, i))); } if (!inRow.contains("") && inRow.size() == 1) { String winner = inRow.iterator().next(); g.setColor(Color.BLUE); for (int j = 0; j < 3; j++) { makeMove(new Point(i, j), winner); } panel.sleep(300); g.setColor(Color.BLACK); for (int j = 0; j < 3; j++) { makeMove(new Point(i, j), winner); } return winner.equals(player) ? winner : null; } if (!inCol.contains("") && inCol.size() == 1) { String winner = inCol.iterator().next(); g.setColor(Color.BLUE); for (int j = 0; j < 3; j++) { makeMove(new Point(j, i), winner); } panel.sleep(300); g.setColor(Color.BLACK); for (int j = 0; j < 3; j++) { makeMove(new Point(j, i), winner); } return winner.equals(player) ? winner : null; } } if (!inDiag1.contains("") && inDiag1.size() == 1) { String winner = inDiag1.iterator().next(); g.setColor(Color.BLUE); for (int i = 0; i < 3; i++) { makeMove(new Point(i, i), winner); } panel.sleep(300); g.setColor(Color.BLACK); for (int i = 0; i < 3; i++) { makeMove(new Point(i, i), winner); } return winner.equals(player) ? winner : null; } if (!inDiag2.contains("") && inDiag2.size() == 1) { String winner = inDiag2.iterator().next(); g.setColor(Color.BLUE); for (int i = 0; i < 3; i++) { makeMove(new Point(4 - i, i), winner); } panel.sleep(300); g.setColor(Color.BLACK); for (int i = 0; i < 3; i++) { makeMove(new Point(4 - i, i), winner); } return winner.equals(player) ? winner : null; } if (moves.values().size() == 2) { return player; } return null; } public static List getRemainingMoves() { List movesLeft = new ArrayList<>(); for (Point move : moves.keySet()) { if (moves.get(move).equals("")) { movesLeft.add(move); } } return movesLeft; } public static Point findBestMove(String player) { // Loop through all the possible moves trying to // figure out which one is best. for (Point move : getRemainingMoves()) { makeMove(move, player); panel.sleep(500); // Check if we won the game using this move. String winner = wonGame(player); // If we did, return it. if (winner != null) { panel.sleep(500); undoMove(move); return move; } // Otherwise, recursively try to find the best move. else { Point bestMove = findBestMove(player.equals("O") ? "X" : "O"); panel.sleep(500); undoMove(move); // If the other player loses, that's the same as us winning. if (bestMove == null) { return move; } } } // None of the moves were good, give up. return null; } public static void main(String[] args) { panel = new DrawingPanel(WIDTH, HEIGHT); Graphics g = panel.getGraphics(); for (int i = 1; i < 3; i++) { g.drawLine(0, i * (1 + SQUARE_SIZE), WIDTH, i * (1 + SQUARE_SIZE)); } for (int i = 1; i < 3; i++) { g.drawLine(i * (1 + SQUARE_SIZE), 0, i * (1 + SQUARE_SIZE), HEIGHT); } for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { moves.put(new Point(i, j), ""); } } makeMove(new Point(0, 0), "X"); makeMove(new Point(2, 2), "O"); makeMove(new Point(1, 1), "X"); makeMove(new Point(0, 1), "O"); makeMove(new Point(0, 2), "X"); makeMove(new Point(2, 0), "O"); String player = "X"; Point bestMove = findBestMove(player); g.setColor(Color.GREEN); makeMove(bestMove, player); g.setColor(Color.BLACK); } }