refactor: Enhance docs, code, add tests in MinimaxAlgorithm (#6641)

* refactor: Enhance docs, code, add tests in `MinimaxAlgorithm`

* Fix

* Fix

* refactor: Enhance docs, code, add tests in `HappyNumbersSeq`

* Revert "refactor: Enhance docs, code, add tests in `HappyNumbersSeq`"

This reverts commit 9e8afb4f2c.
This commit is contained in:
Hardik Pawar
2025-10-12 11:37:17 +05:30
committed by GitHub
parent b5246c3d86
commit 7fb4c8d60f
2 changed files with 237 additions and 98 deletions

View File

@@ -4,54 +4,99 @@ import java.util.Arrays;
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) {
@@ -75,7 +120,7 @@ public class MiniMaxAlgorithm {
}
// 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 ...
@@ -87,46 +132,73 @@ public class MiniMaxAlgorithm {
}
/**
* 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;
}