44import java .util .Random ;
55
66/**
7- * MiniMax is an algorithm used int artificial intelligence and game theory for
8- * minimizing the possible loss for the worst case scenario.
7+ * MiniMax is an algorithm used in artificial intelligence and game theory for
8+ * minimizing the possible loss for the worst case scenario. It is commonly used
9+ * in two-player turn-based games such as Tic-Tac-Toe, Chess, and Checkers.
910 *
10- * See more (https://en.wikipedia.org/wiki/Minimax,
11- * https://www.geeksforgeeks.org/minimax-algorithm-in-game-theory-set-1-introduction/).
11+ * <p>
12+ * The algorithm simulates all possible moves in a game tree and chooses the
13+ * move that minimizes the maximum possible loss. The algorithm assumes both
14+ * players play optimally.
15+ *
16+ * <p>
17+ * Time Complexity: O(b^d) where b is the branching factor and d is the depth
18+ * <p>
19+ * Space Complexity: O(d) for the recursive call stack
20+ *
21+ * <p>
22+ * See more:
23+ * <ul>
24+ * <li><a href="https://en.wikipedia.org/wiki/Minimax">Wikipedia - Minimax</a>
25+ * <li><a href=
26+ * "https://www.geeksforgeeks.org/minimax-algorithm-in-game-theory-set-1-introduction/">
27+ * GeeksforGeeks - Minimax Algorithm</a>
28+ * </ul>
1229 *
1330 * @author aitofi (https://github.com/aitorfi)
1431 */
15- public class MiniMaxAlgorithm {
32+ public final class MiniMaxAlgorithm {
33+
34+ private static final Random RANDOM = new Random ();
1635
1736 /**
1837 * Game tree represented as an int array containing scores. Each array
19- * element is a leaf node.
38+ * element is a leaf node. The array length must be a power of 2.
2039 */
2140 private int [] scores ;
41+
42+ /**
43+ * The height of the game tree, calculated as log2(scores.length).
44+ */
2245 private int height ;
2346
2447 /**
25- * Initializes the scores with 8 random leaf nodes
48+ * Initializes the MiniMaxAlgorithm with 8 random leaf nodes (2^3 = 8).
49+ * Each score is a random integer between 1 and 99 inclusive.
2650 */
2751 public MiniMaxAlgorithm () {
28- scores = getRandomScores (3 , 99 );
29- height = log2 (scores .length );
52+ this (getRandomScores (3 , 99 ));
53+ }
54+
55+ /**
56+ * Initializes the MiniMaxAlgorithm with the provided scores.
57+ *
58+ * @param scores An array of scores representing leaf nodes. The length must be
59+ * a power of 2.
60+ * @throws IllegalArgumentException if the scores array length is not a power of
61+ * 2
62+ */
63+ public MiniMaxAlgorithm (int [] scores ) {
64+ if (!isPowerOfTwo (scores .length )) {
65+ throw new IllegalArgumentException ("The number of scores must be a power of 2." );
66+ }
67+ this .scores = Arrays .copyOf (scores , scores .length );
68+ this .height = log2 (scores .length );
3069 }
3170
71+ /**
72+ * Demonstrates the MiniMax algorithm with a random game tree.
73+ *
74+ * @param args Command line arguments (not used)
75+ */
3276 public static void main (String [] args ) {
33- MiniMaxAlgorithm miniMaxAlgorith = new MiniMaxAlgorithm ();
77+ MiniMaxAlgorithm miniMaxAlgorithm = new MiniMaxAlgorithm ();
3478 boolean isMaximizer = true ; // Specifies the player that goes first.
35- boolean verbose = true ; // True to show each players choices.
3679 int bestScore ;
3780
38- bestScore = miniMaxAlgorith .miniMax (0 , isMaximizer , 0 , verbose );
81+ bestScore = miniMaxAlgorithm .miniMax (0 , isMaximizer , 0 , true );
3982
40- if (verbose ) {
41- System .out .println ();
42- }
43-
44- System .out .println (Arrays .toString (miniMaxAlgorith .getScores ()));
83+ System .out .println ();
84+ System .out .println (Arrays .toString (miniMaxAlgorithm .getScores ()));
4585 System .out .println ("The best score for " + (isMaximizer ? "Maximizer" : "Minimizer" ) + " is " + bestScore );
4686 }
4787
4888 /**
4989 * Returns the optimal score assuming that both players play their best.
5090 *
51- * @param depth Indicates how deep we are into the game tree.
52- * @param isMaximizer True if it is maximizers turn; otherwise false.
53- * @param index Index of the leaf node that is being evaluated.
54- * @param verbose True to show each players choices.
91+ * <p>
92+ * This method recursively evaluates the game tree using the minimax algorithm.
93+ * At each level, the maximizer tries to maximize the score while the minimizer
94+ * tries to minimize it.
95+ *
96+ * @param depth The current depth in the game tree (0 at root).
97+ * @param isMaximizer True if it is the maximizer's turn; false for minimizer.
98+ * @param index Index of the current node in the game tree.
99+ * @param verbose True to print each player's choice during evaluation.
55100 * @return The optimal score for the player that made the first move.
56101 */
57102 public int miniMax (int depth , boolean isMaximizer , int index , boolean verbose ) {
@@ -75,7 +120,7 @@ public int miniMax(int depth, boolean isMaximizer, int index, boolean verbose) {
75120 }
76121
77122 // Leaf nodes can be sequentially inspected by
78- // recurssively multiplying (0 * 2) and ((0 * 2) + 1):
123+ // recursively multiplying (0 * 2) and ((0 * 2) + 1):
79124 // (0 x 2) = 0; ((0 x 2) + 1) = 1
80125 // (1 x 2) = 2; ((1 x 2) + 1) = 3
81126 // (2 x 2) = 4; ((2 x 2) + 1) = 5 ...
@@ -87,46 +132,73 @@ public int miniMax(int depth, boolean isMaximizer, int index, boolean verbose) {
87132 }
88133
89134 /**
90- * Returns an array of random numbers which lenght is a power of 2.
135+ * Returns an array of random numbers whose length is a power of 2.
91136 *
92- * @param size The power of 2 that will determine the lenght of the array.
93- * @param maxScore The maximum possible score.
94- * @return An array of random numbers.
137+ * @param size The power of 2 that will determine the length of the array
138+ * (array length = 2^size).
139+ * @param maxScore The maximum possible score (scores will be between 1 and
140+ * maxScore inclusive).
141+ * @return An array of random numbers with length 2^size.
95142 */
96143 public static int [] getRandomScores (int size , int maxScore ) {
97144 int [] randomScores = new int [(int ) Math .pow (2 , size )];
98- Random rand = new Random ();
99145
100146 for (int i = 0 ; i < randomScores .length ; i ++) {
101- randomScores [i ] = rand .nextInt (maxScore ) + 1 ;
147+ randomScores [i ] = RANDOM .nextInt (maxScore ) + 1 ;
102148 }
103149
104150 return randomScores ;
105151 }
106152
107- // A utility function to find Log n in base 2
153+ /**
154+ * Calculates the logarithm base 2 of a number.
155+ *
156+ * @param n The number to calculate log2 for (must be a power of 2).
157+ * @return The log2 of n.
158+ */
108159 private int log2 (int n ) {
109160 return (n == 1 ) ? 0 : log2 (n / 2 ) + 1 ;
110161 }
111162
112- // A utility function to check if a number is a power of 2
163+ /**
164+ * Checks if a number is a power of 2.
165+ *
166+ * @param n The number to check.
167+ * @return True if n is a power of 2, false otherwise.
168+ */
113169 private boolean isPowerOfTwo (int n ) {
114170 return n > 0 && (n & (n - 1 )) == 0 ;
115171 }
116172
173+ /**
174+ * Sets the scores array for the game tree.
175+ *
176+ * @param scores The array of scores. Length must be a power of 2.
177+ * @throws IllegalArgumentException if the scores array length is not a power of
178+ * 2
179+ */
117180 public void setScores (int [] scores ) {
118181 if (!isPowerOfTwo (scores .length )) {
119- System .out .println ("The number of scores must be a power of 2." );
120- return ;
182+ throw new IllegalArgumentException ("The number of scores must be a power of 2." );
121183 }
122- this .scores = scores ;
184+ this .scores = Arrays . copyOf ( scores , scores . length ) ;
123185 height = log2 (this .scores .length );
124186 }
125187
188+ /**
189+ * Returns a copy of the scores array.
190+ *
191+ * @return A copy of the scores array.
192+ */
126193 public int [] getScores () {
127- return scores ;
194+ return Arrays . copyOf ( scores , scores . length ) ;
128195 }
129196
197+ /**
198+ * Returns the height of the game tree.
199+ *
200+ * @return The height of the game tree (log2 of the number of leaf nodes).
201+ */
130202 public int getHeight () {
131203 return height ;
132204 }
0 commit comments