-
Notifications
You must be signed in to change notification settings - Fork 42
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The complete code.
- Loading branch information
LazoCoder
committed
Nov 13, 2016
1 parent
511de66
commit 0d48754
Showing
14 changed files
with
1,271 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package ArtificialIntelligence; | ||
|
||
import TicTacToe.Board; | ||
|
||
/** | ||
* Uses various algorithms to play Tic Tac Toe. | ||
*/ | ||
public class Algorithms { | ||
|
||
/** | ||
* Algorithms cannot be instantiated. | ||
*/ | ||
private Algorithms() {} | ||
|
||
/** | ||
* Play a random move. | ||
* @param board the Tic Tac Toe board to play on | ||
*/ | ||
public static void random (Board board) { | ||
Random.run(board); | ||
} | ||
|
||
/** | ||
* Play using the MiniMax Algorithm. | ||
* @param board the Tic Tac Toe board to play on | ||
*/ | ||
public static void miniMax (Board board) { | ||
MiniMax.run(board.getTurn(), board, Double.POSITIVE_INFINITY); | ||
} | ||
|
||
/** | ||
* Play using the MiniMax algorithm. Include a depth limit. | ||
* @param board the Tic Tac Toe board to play on | ||
* @param ply the maximum depth | ||
*/ | ||
public static void miniMax (Board board, int ply) { | ||
MiniMax.run(board.getTurn(), board, ply); | ||
} | ||
|
||
/** | ||
* Play using the Alpha-Beta Pruning algorithm. | ||
* @param board the Tic Tac Toe board to play on | ||
*/ | ||
public static void alphaBetaPruning (Board board) { | ||
AlphaBetaPruning.run(board.getTurn(), board, Double.POSITIVE_INFINITY); | ||
} | ||
|
||
/** | ||
* Play using the Alpha-Beta Pruning algorithm. Include a depth limit. | ||
* @param board the Tic Tac Toe board to play on | ||
* @param ply the maximum depth | ||
*/ | ||
public static void alphaBetaPruning (Board board, int ply) { | ||
AlphaBetaPruning.run(board.getTurn(), board, ply); | ||
} | ||
|
||
/** | ||
* Play using the Alpha-Beta Pruning algorithm. Include depth in the | ||
* evaluation function. | ||
* @param board the Tic Tac Toe board to play on | ||
*/ | ||
public static void alphaBetaAdvanced (Board board) { | ||
AlphaBetaAdvanced.run(board.getTurn(), board, Double.POSITIVE_INFINITY); | ||
} | ||
|
||
/** | ||
* Play using the Alpha-Beta Pruning algorithm. Include depth in the | ||
* evaluation function and a depth limit. | ||
* @param board the Tic Tac Toe board to play on | ||
* @param ply the maximum depth | ||
*/ | ||
public static void alphaBetaAdvanced (Board board, int ply) { | ||
AlphaBetaAdvanced.run(board.getTurn(), board, ply); | ||
} | ||
|
||
} |
157 changes: 157 additions & 0 deletions
157
TicTacToe/ArtificialIntelligence/AlphaBetaAdvanced.java
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package ArtificialIntelligence; | ||
|
||
import TicTacToe.Board; | ||
|
||
/** | ||
* Uses the Alpha-Beta Pruning algorithm to play a move in a game of Tic Tac Toe | ||
* but includes depth in the evaluation function. | ||
* | ||
* The vanilla MiniMax algorithm plays perfectly but it may occasionally | ||
* decide to make a move that will results in a slower victory or a faster loss. | ||
* For example, playing the move 0, 1, and then 7 gives the AI the opportunity | ||
* to play a move at index 6. This would result in a victory on the diagonal. | ||
* But the AI does not choose this move, instead it chooses another one. It | ||
* still wins inevitably, but it chooses a longer route. By adding the depth | ||
* into the evaluation function, it allows the AI to pick the move that would | ||
* make it win as soon as possible. | ||
*/ | ||
class AlphaBetaAdvanced { | ||
|
||
private static double maxPly; | ||
|
||
/** | ||
* AlphaBetaAdvanced cannot be instantiated. | ||
*/ | ||
private AlphaBetaAdvanced() {} | ||
|
||
/** | ||
* Execute the algorithm. | ||
* @param player the player that the AI will identify as | ||
* @param board the Tic Tac Toe board to play on | ||
* @param maxPly the maximum depth | ||
*/ | ||
static void run (Board.State player, Board board, double maxPly) { | ||
|
||
if (maxPly < 1) { | ||
throw new IllegalArgumentException("Maximum depth must be greater than 1."); | ||
} | ||
|
||
AlphaBetaAdvanced.maxPly = maxPly; | ||
alphaBetaPruning(player, board, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 0); | ||
} | ||
|
||
/** | ||
* The meat of the algorithm. | ||
* @param player the player that the AI will identify as | ||
* @param board the Tic Tac Toe board to play on | ||
* @param alpha the alpha value | ||
* @param beta the beta value | ||
* @param currentPly the current depth | ||
* @return the score of the board | ||
*/ | ||
private static int alphaBetaPruning (Board.State player, Board board, double alpha, double beta, int currentPly) { | ||
if (currentPly++ == maxPly || board.isGameOver()) { | ||
return score(player, board, currentPly); | ||
} | ||
|
||
if (board.getTurn() == player) { | ||
return getMax(player, board, alpha, beta, currentPly); | ||
} else { | ||
return getMin(player, board, alpha, beta, currentPly); | ||
} | ||
} | ||
|
||
/** | ||
* Play the move with the highest score. | ||
* @param player the player that the AI will identify as | ||
* @param board the Tic Tac Toe board to play on | ||
* @param alpha the alpha value | ||
* @param beta the beta value | ||
* @param currentPly the current depth | ||
* @return the score of the board | ||
*/ | ||
private static int getMax (Board.State player, Board board, double alpha, double beta, int currentPly) { | ||
int indexOfBestMove = -1; | ||
|
||
for (Integer theMove : board.getAvailableMoves()) { | ||
|
||
Board modifiedBoard = board.getDeepCopy(); | ||
modifiedBoard.move(theMove); | ||
int score = alphaBetaPruning(player, modifiedBoard, alpha, beta, currentPly); | ||
|
||
if (score > alpha) { | ||
alpha = score; | ||
indexOfBestMove = theMove; | ||
} | ||
|
||
if (alpha >= beta) { | ||
break; | ||
} | ||
} | ||
|
||
if (indexOfBestMove != -1) { | ||
board.move(indexOfBestMove); | ||
} | ||
return (int)alpha; | ||
} | ||
|
||
/** | ||
* Play the move with the lowest score. | ||
* @param player the player that the AI will identify as | ||
* @param board the Tic Tac Toe board to play on | ||
* @param alpha the alpha value | ||
* @param beta the beta value | ||
* @param currentPly the current depth | ||
* @return the score of the board | ||
*/ | ||
private static int getMin (Board.State player, Board board, double alpha, double beta, int currentPly) { | ||
int indexOfBestMove = -1; | ||
|
||
for (Integer theMove : board.getAvailableMoves()) { | ||
|
||
Board modifiedBoard = board.getDeepCopy(); | ||
modifiedBoard.move(theMove); | ||
|
||
int score = alphaBetaPruning(player, modifiedBoard, alpha, beta, currentPly); | ||
|
||
if (score < beta) { | ||
beta = score; | ||
indexOfBestMove = theMove; | ||
} | ||
|
||
if (alpha >= beta) { | ||
break; | ||
} | ||
} | ||
|
||
if (indexOfBestMove != -1) { | ||
board.move(indexOfBestMove); | ||
} | ||
return (int)beta; | ||
} | ||
|
||
/** | ||
* Get the score of the board. Takes depth into account. | ||
* @param player the play that the AI will identify as | ||
* @param board the Tic Tac Toe board to play on | ||
* @param currentPly the current depth | ||
* @return the score of the board | ||
*/ | ||
private static int score (Board.State player, Board board, int currentPly) { | ||
|
||
if (player == Board.State.Blank) { | ||
throw new IllegalArgumentException("Player must be X or O."); | ||
} | ||
|
||
Board.State opponent = (player == Board.State.X) ? Board.State.O : Board.State.X; | ||
|
||
if (board.isGameOver() && board.getWinner() == player) { | ||
return 10 - currentPly; | ||
} else if (board.isGameOver() && board.getWinner() == opponent) { | ||
return -10 + currentPly; | ||
} else { | ||
return 0; | ||
} | ||
} | ||
|
||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package ArtificialIntelligence; | ||
|
||
import TicTacToe.Board; | ||
|
||
/** | ||
* Uses the Alpha-Beta Pruning algorithm to play a move in a game of Tic Tac Toe. | ||
*/ | ||
class AlphaBetaPruning { | ||
|
||
private static double maxPly; | ||
|
||
/** | ||
* AlphaBetaPruning cannot be instantiated. | ||
*/ | ||
private AlphaBetaPruning () {} | ||
|
||
/** | ||
* Execute the algorithm. | ||
* @param player the player that the AI will identify as | ||
* @param board the Tic Tac Toe board to play on | ||
* @param maxPly the maximum depth | ||
*/ | ||
static void run (Board.State player, Board board, double maxPly) { | ||
if (maxPly < 1) { | ||
throw new IllegalArgumentException("Maximum depth must be greater than 1."); | ||
} | ||
|
||
AlphaBetaPruning.maxPly = maxPly; | ||
alphaBetaPruning(player, board, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 0); | ||
} | ||
|
||
/** | ||
* The meat of the algorithm. | ||
* @param player the player that the AI will identify as | ||
* @param board the Tic Tac Toe board to play on | ||
* @param alpha the alpha value | ||
* @param beta the beta value | ||
* @param currentPly the current depth | ||
* @return the score of the board | ||
*/ | ||
private static int alphaBetaPruning (Board.State player, Board board, double alpha, double beta, int currentPly) { | ||
if (currentPly++ == maxPly || board.isGameOver()) { | ||
return score(player, board); | ||
} | ||
|
||
if (board.getTurn() == player) { | ||
return getMax(player, board, alpha, beta, currentPly); | ||
} else { | ||
return getMin(player, board, alpha, beta, currentPly); | ||
} | ||
} | ||
|
||
/** | ||
* Play the move with the highest score. | ||
* @param player the player that the AI will identify as | ||
* @param board the Tic Tac Toe board to play on | ||
* @param alpha the alpha value | ||
* @param beta the beta value | ||
* @param currentPly the current depth | ||
* @return the score of the board | ||
*/ | ||
private static int getMax (Board.State player, Board board, double alpha, double beta, int currentPly) { | ||
int indexOfBestMove = -1; | ||
|
||
for (Integer theMove : board.getAvailableMoves()) { | ||
|
||
Board modifiedBoard = board.getDeepCopy(); | ||
modifiedBoard.move(theMove); | ||
int score = alphaBetaPruning(player, modifiedBoard, alpha, beta, currentPly); | ||
|
||
if (score > alpha) { | ||
alpha = score; | ||
indexOfBestMove = theMove; | ||
} | ||
|
||
// Pruning. | ||
if (alpha >= beta) { | ||
break; | ||
} | ||
} | ||
|
||
if (indexOfBestMove != -1) { | ||
board.move(indexOfBestMove); | ||
} | ||
return (int)alpha; | ||
} | ||
|
||
/** | ||
* Play the move with the lowest score. | ||
* @param player the player that the AI will identify as | ||
* @param board the Tic Tac Toe board to play on | ||
* @param alpha the alpha value | ||
* @param beta the beta value | ||
* @param currentPly the current depth | ||
* @return the score of the board | ||
*/ | ||
private static int getMin (Board.State player, Board board, double alpha, double beta, int currentPly) { | ||
int indexOfBestMove = -1; | ||
|
||
for (Integer theMove : board.getAvailableMoves()) { | ||
|
||
Board modifiedBoard = board.getDeepCopy(); | ||
modifiedBoard.move(theMove); | ||
|
||
int score = alphaBetaPruning(player, modifiedBoard, alpha, beta, currentPly); | ||
|
||
if (score < beta) { | ||
beta = score; | ||
indexOfBestMove = theMove; | ||
} | ||
|
||
// Pruning. | ||
if (alpha >= beta) { | ||
break; | ||
} | ||
} | ||
|
||
if (indexOfBestMove != -1) { | ||
board.move(indexOfBestMove); | ||
} | ||
return (int)beta; | ||
} | ||
|
||
/** | ||
* Get the score of the board. | ||
* @param player the play that the AI will identify as | ||
* @param board the Tic Tac Toe board to play on | ||
* @return the score of the board | ||
*/ | ||
private static int score (Board.State player, Board board) { | ||
if (player == Board.State.Blank) { | ||
throw new IllegalArgumentException("Player must be X or O."); | ||
} | ||
|
||
Board.State opponent = (player == Board.State.X) ? Board.State.O : Board.State.X; | ||
|
||
if (board.isGameOver() && board.getWinner() == player) { | ||
return 10; | ||
} else if (board.isGameOver() && board.getWinner() == opponent) { | ||
return -10; | ||
} else { | ||
return 0; | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.