Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 106 additions & 34 deletions src/main/java/com/thealgorithms/others/MiniMaxAlgorithm.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,54 +4,99 @@
import java.util.Random;

/**
* MiniMax is an algorithm used int artificial intelligence and game theory for
* minimizing the possible loss for the worst case scenario.
* MiniMax is an algorithm used in artificial intelligence and game theory for
* minimizing the possible loss for the worst case scenario. It is commonly used
* in two-player turn-based games such as Tic-Tac-Toe, Chess, and Checkers.
*
* See more (https://en.wikipedia.org/wiki/Minimax,
* https://www.geeksforgeeks.org/minimax-algorithm-in-game-theory-set-1-introduction/).
* <p>
* The algorithm simulates all possible moves in a game tree and chooses the
* move that minimizes the maximum possible loss. The algorithm assumes both
* players play optimally.
*
* <p>
* Time Complexity: O(b^d) where b is the branching factor and d is the depth
* <p>
* Space Complexity: O(d) for the recursive call stack
*
* <p>
* See more:
* <ul>
* <li><a href="https://en.wikipedia.org/wiki/Minimax">Wikipedia - Minimax</a>
* <li><a href=
* "https://www.geeksforgeeks.org/minimax-algorithm-in-game-theory-set-1-introduction/">
* GeeksforGeeks - Minimax Algorithm</a>
* </ul>
*
* @author aitofi (https://github.com/aitorfi)
*/
public class MiniMaxAlgorithm {
public final class MiniMaxAlgorithm {

private static final Random RANDOM = new Random();

/**
* Game tree represented as an int array containing scores. Each array
* element is a leaf node.
* element is a leaf node. The array length must be a power of 2.
*/
private int[] scores;

/**
* The height of the game tree, calculated as log2(scores.length).
*/
private int height;

/**
* Initializes the scores with 8 random leaf nodes
* Initializes the MiniMaxAlgorithm with 8 random leaf nodes (2^3 = 8).
* Each score is a random integer between 1 and 99 inclusive.
*/
public MiniMaxAlgorithm() {
scores = getRandomScores(3, 99);
height = log2(scores.length);
this(getRandomScores(3, 99));
}

/**
* Initializes the MiniMaxAlgorithm with the provided scores.
*
* @param scores An array of scores representing leaf nodes. The length must be
* a power of 2.
* @throws IllegalArgumentException if the scores array length is not a power of
* 2
*/
public MiniMaxAlgorithm(int[] scores) {
if (!isPowerOfTwo(scores.length)) {
throw new IllegalArgumentException("The number of scores must be a power of 2.");
}
this.scores = Arrays.copyOf(scores, scores.length);
this.height = log2(scores.length);
}

/**
* Demonstrates the MiniMax algorithm with a random game tree.
*
* @param args Command line arguments (not used)
*/
public static void main(String[] args) {
MiniMaxAlgorithm miniMaxAlgorith = new MiniMaxAlgorithm();
MiniMaxAlgorithm miniMaxAlgorithm = new MiniMaxAlgorithm();
boolean isMaximizer = true; // Specifies the player that goes first.
boolean verbose = true; // True to show each players choices.
int bestScore;

bestScore = miniMaxAlgorith.miniMax(0, isMaximizer, 0, verbose);
bestScore = miniMaxAlgorithm.miniMax(0, isMaximizer, 0, true);

if (verbose) {
System.out.println();
}

System.out.println(Arrays.toString(miniMaxAlgorith.getScores()));
System.out.println();
System.out.println(Arrays.toString(miniMaxAlgorithm.getScores()));
System.out.println("The best score for " + (isMaximizer ? "Maximizer" : "Minimizer") + " is " + bestScore);
}

/**
* Returns the optimal score assuming that both players play their best.
*
* @param depth Indicates how deep we are into the game tree.
* @param isMaximizer True if it is maximizers turn; otherwise false.
* @param index Index of the leaf node that is being evaluated.
* @param verbose True to show each players choices.
* <p>
* This method recursively evaluates the game tree using the minimax algorithm.
* At each level, the maximizer tries to maximize the score while the minimizer
* tries to minimize it.
*
* @param depth The current depth in the game tree (0 at root).
* @param isMaximizer True if it is the maximizer's turn; false for minimizer.
* @param index Index of the current node in the game tree.
* @param verbose True to print each player's choice during evaluation.
* @return The optimal score for the player that made the first move.
*/
public int miniMax(int depth, boolean isMaximizer, int index, boolean verbose) {
Expand All @@ -75,7 +120,7 @@ public int miniMax(int depth, boolean isMaximizer, int index, boolean verbose) {
}

// Leaf nodes can be sequentially inspected by
// recurssively multiplying (0 * 2) and ((0 * 2) + 1):
// recursively multiplying (0 * 2) and ((0 * 2) + 1):
// (0 x 2) = 0; ((0 x 2) + 1) = 1
// (1 x 2) = 2; ((1 x 2) + 1) = 3
// (2 x 2) = 4; ((2 x 2) + 1) = 5 ...
Expand All @@ -87,46 +132,73 @@ public int miniMax(int depth, boolean isMaximizer, int index, boolean verbose) {
}

/**
* Returns an array of random numbers which lenght is a power of 2.
* Returns an array of random numbers whose length is a power of 2.
*
* @param size The power of 2 that will determine the lenght of the array.
* @param maxScore The maximum possible score.
* @return An array of random numbers.
* @param size The power of 2 that will determine the length of the array
* (array length = 2^size).
* @param maxScore The maximum possible score (scores will be between 1 and
* maxScore inclusive).
* @return An array of random numbers with length 2^size.
*/
public static int[] getRandomScores(int size, int maxScore) {
int[] randomScores = new int[(int) Math.pow(2, size)];
Random rand = new Random();

for (int i = 0; i < randomScores.length; i++) {
randomScores[i] = rand.nextInt(maxScore) + 1;
randomScores[i] = RANDOM.nextInt(maxScore) + 1;
}

return randomScores;
}

// A utility function to find Log n in base 2
/**
* Calculates the logarithm base 2 of a number.
*
* @param n The number to calculate log2 for (must be a power of 2).
* @return The log2 of n.
*/
private int log2(int n) {
return (n == 1) ? 0 : log2(n / 2) + 1;
}

// A utility function to check if a number is a power of 2
/**
* Checks if a number is a power of 2.
*
* @param n The number to check.
* @return True if n is a power of 2, false otherwise.
*/
private boolean isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}

/**
* Sets the scores array for the game tree.
*
* @param scores The array of scores. Length must be a power of 2.
* @throws IllegalArgumentException if the scores array length is not a power of
* 2
*/
public void setScores(int[] scores) {
if (!isPowerOfTwo(scores.length)) {
System.out.println("The number of scores must be a power of 2.");
return;
throw new IllegalArgumentException("The number of scores must be a power of 2.");
}
this.scores = scores;
this.scores = Arrays.copyOf(scores, scores.length);
height = log2(this.scores.length);
}

/**
* Returns a copy of the scores array.
*
* @return A copy of the scores array.
*/
public int[] getScores() {
return scores;
return Arrays.copyOf(scores, scores.length);
}

/**
* Returns the height of the game tree.
*
* @return The height of the game tree (log2 of the number of leaf nodes).
*/
public int getHeight() {
return height;
}
Expand Down
Loading