Created
October 14, 2015 23:16
-
-
Save rhulha/37b78e042f2f2546c6fc to your computer and use it in GitHub Desktop.
This file contains hidden or 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
| package net.raysforge.visual.demo; | |
| import java.awt.Color; | |
| import java.awt.Container; | |
| import java.awt.Font; | |
| import java.awt.GridLayout; | |
| import java.awt.Point; | |
| import java.awt.event.ActionEvent; | |
| import java.awt.event.ActionListener; | |
| import java.util.ArrayList; | |
| import java.util.List; | |
| import javax.swing.JButton; | |
| import javax.swing.JFrame; | |
| public class TicTacToe extends JButton implements ActionListener { | |
| final static TicTacToe[][] cells = new TicTacToe[3][3]; | |
| final static Font font = new Font("Comic Sans MS", Font.BOLD, 100); | |
| final static int EMPTY = 0; | |
| final static int computer = 1; | |
| final static int human = -1; | |
| private int content; | |
| public TicTacToe() { | |
| addActionListener(this); | |
| setFocusPainted(false); | |
| setFont(font); | |
| } | |
| public static void main(String[] args) { | |
| JFrame f = new JFrame("TicTacToe"); | |
| f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); | |
| f.setSize(640, 640); | |
| Container cp = f.getContentPane(); | |
| cp.setLayout(new GridLayout(3, 3)); | |
| for (int x = 0; x < 3; x++) { | |
| for (int y = 0; y < 3; y++) { | |
| cp.add(cells[x][y] = new TicTacToe()); | |
| } | |
| } | |
| f.setLocationRelativeTo(null); | |
| f.setVisible(true); | |
| } | |
| @Override | |
| public void actionPerformed(ActionEvent e) { | |
| if (getText().length() > 0) | |
| return; | |
| setText("X"); | |
| setForeground(Color.GREEN); | |
| content = human; | |
| int[] result = minimax(4, computer); | |
| if (result[1] == -1) | |
| return; | |
| cells[result[1]][result[2]].setText("O"); | |
| cells[result[1]][result[2]].content = computer; | |
| cells[result[1]][result[2]].setForeground(Color.RED); | |
| } | |
| /** Recursive minimax at level of depth for either maximizing or minimizing player. | |
| * Return int[3] of {score, row, col} | |
| */ | |
| private int[] minimax(int depth, int player) { | |
| List<Point> nextMoves = findAllValidMoves(); | |
| // computer is maximizing; while human is minimizing | |
| int bestScore = (player == computer) ? Integer.MIN_VALUE : Integer.MAX_VALUE; | |
| Point bestCell = new Point(-1, -1); | |
| if (nextMoves.isEmpty() || depth == 0) { | |
| bestScore = evaluate(); // Gameover or depth reached, evaluate score | |
| } else { | |
| for (Point p : nextMoves) { | |
| cells[p.x][p.y].content = player; // Try this move for the current "player" | |
| int currentScore = minimax(depth - 1, -1 * player)[0]; | |
| if (player == computer) { // computer is maximizing player | |
| if (currentScore > bestScore) { | |
| bestScore = currentScore; | |
| bestCell.setLocation(p); | |
| } | |
| } else { // human is minimizing player | |
| if (currentScore < bestScore) { | |
| bestScore = currentScore; | |
| bestCell.setLocation(p); | |
| } | |
| } | |
| cells[p.x][p.y].content = EMPTY; // Undo move | |
| } | |
| } | |
| return new int[] { bestScore, bestCell.x, bestCell.y }; | |
| } | |
| private List<Point> findAllValidMoves() { | |
| return new ArrayList<Point>() { | |
| { | |
| if (!hasWon(computer) && !hasWon(human)) { | |
| for (int row = 0; row < 3; ++row) { | |
| for (int col = 0; col < 3; ++col) { | |
| if (cells[row][col].content == EMPTY) { | |
| add(new Point(row, col)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }; | |
| } | |
| /** The heuristic evaluation function for the current board | |
| @Return +100, +10, +1 for EACH 3-, 2-, 1-in-a-line for computer. | |
| -100, -10, -1 for EACH 3-, 2-, 1-in-a-line for opponent. | |
| 0 otherwise */ | |
| private int evaluate() { | |
| int score = 0; | |
| // Evaluate score for each of the 8 lines (3 rows, 3 columns, 2 diagonals) | |
| score += evalCell(0, 2, evalCell(0, 1, cells[0][0].content)); // row 0 | |
| score += evalCell(1, 2, evalCell(1, 1, cells[1][0].content)); // row 1 | |
| score += evalCell(2, 2, evalCell(2, 1, cells[2][0].content)); // row 2 | |
| score += evalCell(2, 0, evalCell(1, 0, cells[0][0].content)); // col 0 | |
| score += evalCell(2, 1, evalCell(1, 1, cells[0][1].content)); // col 1 | |
| score += evalCell(2, 2, evalCell(1, 2, cells[0][2].content)); // col 2 | |
| score += evalCell(2, 2, evalCell(1, 1, cells[0][0].content)); // diagonal | |
| score += evalCell(2, 0, evalCell(1, 1, cells[0][2].content)); // alternate diagonal | |
| return score; | |
| } | |
| /** | |
| * The heuristic evaluation function for the given cell plus previous score | |
| * @Return +100, +10, +1 for 3-, 2-, 1-in-a-line for computer. | |
| * -100, -10, -1 for 3-, 2-, 1-in-a-line for opponent. | |
| * 0 otherwise | |
| */ | |
| public int evalCell(int row, int col, int score) { | |
| if (cells[row][col].content == computer) { | |
| if (score > 0) { // cell1 and/or cell2 is computer | |
| score *= 10; | |
| } else if (score < 0) { // cell1 and/or cell2 is human | |
| return 0; | |
| } else { // cell1 and cell2 are empty | |
| score = 1; | |
| } | |
| } else if (cells[row][col].content == human) { | |
| if (score < 0) { // cell1 and/or cell2 is human | |
| score *= 10; | |
| } else if (score > 1) { // cell1 and/or cell2 is computer | |
| return 0; | |
| } else { // cell1 and cell2 are empty | |
| score = -1; | |
| } | |
| } | |
| return score; | |
| } | |
| private int[] winningPatterns = { 0b111000000, 0b000111000, 0b000000111, // rows | |
| 0b100100100, 0b010010010, 0b001001001, // cols | |
| 0b100010001, 0b001010100 // diagonals | |
| }; | |
| /** Returns true if thePlayer wins */ | |
| private boolean hasWon(int thePlayer) { | |
| int pattern = 0b000000000; // 9-bit pattern for the 9 cells | |
| for (int row = 0; row < 3; ++row) { | |
| for (int col = 0; col < 3; ++col) { | |
| if (cells[row][col].content == thePlayer) { | |
| pattern |= (1 << (row * 3 + col)); | |
| } | |
| } | |
| } | |
| for (int winningPattern : winningPatterns) { | |
| if ((pattern & winningPattern) == winningPattern) | |
| return true; | |
| } | |
| return false; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment