Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 21.3k
Expand file tree
/
Copy pathMiniMaxAlgorithm.java
More file actions
Latest commit
205 lines (181 loc) · 6.79 KB
/
Copy pathMiniMaxAlgorithm.java
File metadata and controls
205 lines (181 loc) · 6.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
packagecom.thealgorithms.others;
importjava.util.Arrays;
importjava.util.Random;
/**
* 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.
*
* <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)
*/
publicfinalclassMiniMaxAlgorithm {
privatestaticfinalRandomRANDOM = newRandom();
/**
* Game tree represented as an int array containing scores. Each array
* element is a leaf node. The array length must be a power of 2.
*/
privateint[] scores;
/**
* The height of the game tree, calculated as log2(scores.length).
*/
privateintheight;
/**
* Initializes the MiniMaxAlgorithm with 8 random leaf nodes (2^3 = 8).
* Each score is a random integer between 1 and 99 inclusive.
*/
publicMiniMaxAlgorithm() {
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
*/
publicMiniMaxAlgorithm(int[] scores) {
if (!isPowerOfTwo(scores.length)) {
thrownewIllegalArgumentException("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)
*/
publicstaticvoidmain(String[] args) {
MiniMaxAlgorithmminiMaxAlgorithm = newMiniMaxAlgorithm();
booleanisMaximizer = true; // Specifies the player that goes first.
intbestScore;
bestScore = miniMaxAlgorithm.miniMax(0, isMaximizer, 0, true);
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.
*
* <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.
*/
publicintminiMax(intdepth, booleanisMaximizer, intindex, booleanverbose) {
intbestScore;
intscore1;
intscore2;
if (depth == height) { // Leaf node reached.
returnscores[index];
}
score1 = miniMax(depth + 1, !isMaximizer, index * 2, verbose);
score2 = miniMax(depth + 1, !isMaximizer, (index * 2) + 1, verbose);
if (isMaximizer) {
// Maximizer player wants to get the maximum possible score.
bestScore = Math.max(score1, score2);
} else {
// Minimizer player wants to get the minimum possible score.
bestScore = Math.min(score1, score2);
}
// Leaf nodes can be sequentially inspected by
// 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 ...
if (verbose) {
System.out.printf("From %02d and %02d, %s chooses %02d%n", score1, score2, (isMaximizer ? "Maximizer" : "Minimizer"), bestScore);
}
returnbestScore;
}
/**
* Returns an array of random numbers whose length is a power of 2.
*
* @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.
*/
publicstaticint[] getRandomScores(intsize, intmaxScore) {
int[] randomScores = newint[(int) Math.pow(2, size)];
for (inti = 0; i < randomScores.length; i++) {
randomScores[i] = RANDOM.nextInt(maxScore) + 1;
}
returnrandomScores;
}
/**
* 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.
*/
privateintlog2(intn) {
return (n == 1) ? 0 : log2(n / 2) + 1;
}
/**
* 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.
*/
privatebooleanisPowerOfTwo(intn) {
returnn > 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
*/
publicvoidsetScores(int[] scores) {
if (!isPowerOfTwo(scores.length)) {
thrownewIllegalArgumentException("The number of scores must be a power of 2.");
}
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.
*/
publicint[] getScores() {
returnArrays.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).
*/
publicintgetHeight() {
returnheight;
}
}