mirror of
https://github.com/TheAlgorithms/Java.git
synced 2026-03-13 08:40:43 +08:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package com.thealgorithms.others;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -26,56 +23,82 @@ class MiniMaxAlgorithmTest {
|
||||
System.setOut(new PrintStream(outputStream));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
System.setOut(originalOut);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructorCreatesValidScores() {
|
||||
// The default constructor should create scores array of length 8 (2^3)
|
||||
assertEquals(8, miniMax.getScores().length);
|
||||
assertEquals(3, miniMax.getHeight());
|
||||
Assertions.assertEquals(8, miniMax.getScores().length);
|
||||
Assertions.assertEquals(3, miniMax.getHeight());
|
||||
|
||||
// All scores should be positive (between 1 and 99)
|
||||
for (int score : miniMax.getScores()) {
|
||||
assertTrue(score >= 1 && score <= 99);
|
||||
Assertions.assertTrue(score >= 1 && score <= 99);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructorWithValidScores() {
|
||||
int[] validScores = {10, 20, 30, 40};
|
||||
MiniMaxAlgorithm customMiniMax = new MiniMaxAlgorithm(validScores);
|
||||
|
||||
Assertions.assertArrayEquals(validScores, customMiniMax.getScores());
|
||||
Assertions.assertEquals(2, customMiniMax.getHeight()); // log2(4) = 2
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructorWithInvalidScoresThrowsException() {
|
||||
int[] invalidScores = {10, 20, 30}; // Length 3 is not a power of 2
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> new MiniMaxAlgorithm(invalidScores));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructorDoesNotModifyOriginalArray() {
|
||||
int[] originalScores = {10, 20, 30, 40};
|
||||
int[] copyOfOriginal = {10, 20, 30, 40};
|
||||
MiniMaxAlgorithm customMiniMax = new MiniMaxAlgorithm(originalScores);
|
||||
|
||||
// Modify the original array
|
||||
originalScores[0] = 999;
|
||||
|
||||
// Constructor should have made a copy, so internal state should be unchanged
|
||||
Assertions.assertArrayEquals(copyOfOriginal, customMiniMax.getScores());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetScoresWithValidPowerOfTwo() {
|
||||
int[] validScores = {10, 20, 30, 40};
|
||||
miniMax.setScores(validScores);
|
||||
|
||||
assertArrayEquals(validScores, miniMax.getScores());
|
||||
assertEquals(2, miniMax.getHeight()); // log2(4) = 2
|
||||
Assertions.assertArrayEquals(validScores, miniMax.getScores());
|
||||
Assertions.assertEquals(2, miniMax.getHeight()); // log2(4) = 2
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetScoresWithInvalidLength() {
|
||||
int[] invalidScores = {10, 20, 30}; // Length 3 is not a power of 2
|
||||
miniMax.setScores(invalidScores);
|
||||
|
||||
// Should print error message and not change the scores
|
||||
String output = outputStream.toString();
|
||||
assertTrue(output.contains("The number of scores must be a power of 2."));
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> miniMax.setScores(invalidScores));
|
||||
|
||||
// Scores should remain unchanged (original length 8)
|
||||
assertEquals(8, miniMax.getScores().length);
|
||||
Assertions.assertEquals(8, miniMax.getScores().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetScoresWithZeroLength() {
|
||||
int[] emptyScores = {}; // Length 0 is not a power of 2
|
||||
miniMax.setScores(emptyScores);
|
||||
|
||||
// Should print error message and not change the scores
|
||||
String output = outputStream.toString();
|
||||
assertTrue(output.contains("The number of scores must be a power of 2."));
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> miniMax.setScores(emptyScores));
|
||||
|
||||
// Scores should remain unchanged (original length 8)
|
||||
assertEquals(8, miniMax.getScores().length);
|
||||
Assertions.assertEquals(8, miniMax.getScores().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetScoresWithVariousInvalidLengths() {
|
||||
// Test multiple invalid lengths to ensure isPowerOfTwo function is fully covered
|
||||
// Test multiple invalid lengths to ensure isPowerOfTwo function is fully
|
||||
// covered
|
||||
int[][] invalidScoreArrays = {
|
||||
{1, 2, 3, 4, 5}, // Length 5
|
||||
{1, 2, 3, 4, 5, 6}, // Length 6
|
||||
@@ -86,17 +109,11 @@ class MiniMaxAlgorithmTest {
|
||||
};
|
||||
|
||||
for (int[] invalidScores : invalidScoreArrays) {
|
||||
// Clear the output stream for each test
|
||||
outputStream.reset();
|
||||
miniMax.setScores(invalidScores);
|
||||
|
||||
// Should print error message for each invalid length
|
||||
String output = outputStream.toString();
|
||||
assertTrue(output.contains("The number of scores must be a power of 2."), "Failed for array length: " + invalidScores.length);
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> miniMax.setScores(invalidScores), "Failed for array length: " + invalidScores.length);
|
||||
}
|
||||
|
||||
// Scores should remain unchanged (original length 8)
|
||||
assertEquals(8, miniMax.getScores().length);
|
||||
Assertions.assertEquals(8, miniMax.getScores().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -104,8 +121,8 @@ class MiniMaxAlgorithmTest {
|
||||
int[] singleScore = {42};
|
||||
miniMax.setScores(singleScore);
|
||||
|
||||
assertArrayEquals(singleScore, miniMax.getScores());
|
||||
assertEquals(0, miniMax.getHeight()); // log2(1) = 0
|
||||
Assertions.assertArrayEquals(singleScore, miniMax.getScores());
|
||||
Assertions.assertEquals(0, miniMax.getHeight()); // log2(1) = 0
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,7 +133,7 @@ class MiniMaxAlgorithmTest {
|
||||
|
||||
// Maximizer starts: should choose max(min(3,12), min(8,2)) = max(3, 2) = 3
|
||||
int result = miniMax.miniMax(0, true, 0, false);
|
||||
assertEquals(3, result);
|
||||
Assertions.assertEquals(3, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -127,7 +144,7 @@ class MiniMaxAlgorithmTest {
|
||||
|
||||
// Minimizer starts: should choose min(max(3,12), max(8,2)) = min(12, 8) = 8
|
||||
int result = miniMax.miniMax(0, false, 0, false);
|
||||
assertEquals(8, result);
|
||||
Assertions.assertEquals(8, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -139,8 +156,8 @@ class MiniMaxAlgorithmTest {
|
||||
// Maximizer starts
|
||||
int result = miniMax.miniMax(0, true, 0, false);
|
||||
// Expected: max(min(max(5,6), max(7,4)), min(max(5,3), max(6,2)))
|
||||
// = max(min(6, 7), min(5, 6)) = max(6, 5) = 6
|
||||
assertEquals(6, result);
|
||||
// = max(min(6, 7), min(5, 6)) = max(6, 5) = 6
|
||||
Assertions.assertEquals(6, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -151,41 +168,41 @@ class MiniMaxAlgorithmTest {
|
||||
miniMax.miniMax(0, true, 0, true);
|
||||
|
||||
String output = outputStream.toString();
|
||||
assertTrue(output.contains("Maximizer"));
|
||||
assertTrue(output.contains("Minimizer"));
|
||||
assertTrue(output.contains("chooses"));
|
||||
Assertions.assertTrue(output.contains("Maximizer"));
|
||||
Assertions.assertTrue(output.contains("Minimizer"));
|
||||
Assertions.assertTrue(output.contains("chooses"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetRandomScoresLength() {
|
||||
int[] randomScores = MiniMaxAlgorithm.getRandomScores(4, 50);
|
||||
assertEquals(16, randomScores.length); // 2^4 = 16
|
||||
Assertions.assertEquals(16, randomScores.length); // 2^4 = 16
|
||||
|
||||
// All scores should be between 1 and 50
|
||||
for (int score : randomScores) {
|
||||
assertTrue(score >= 1 && score <= 50);
|
||||
Assertions.assertTrue(score >= 1 && score <= 50);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetRandomScoresWithDifferentParameters() {
|
||||
int[] randomScores = MiniMaxAlgorithm.getRandomScores(2, 10);
|
||||
assertEquals(4, randomScores.length); // 2^2 = 4
|
||||
Assertions.assertEquals(4, randomScores.length); // 2^2 = 4
|
||||
|
||||
// All scores should be between 1 and 10
|
||||
for (int score : randomScores) {
|
||||
assertTrue(score >= 1 && score <= 10);
|
||||
Assertions.assertTrue(score >= 1 && score <= 10);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMainMethod() {
|
||||
// Test that main method runs without errors
|
||||
assertDoesNotThrow(() -> MiniMaxAlgorithm.main(new String[] {}));
|
||||
Assertions.assertDoesNotThrow(() -> MiniMaxAlgorithm.main(new String[] {}));
|
||||
|
||||
String output = outputStream.toString();
|
||||
assertTrue(output.contains("The best score for"));
|
||||
assertTrue(output.contains("Maximizer"));
|
||||
Assertions.assertTrue(output.contains("The best score for"));
|
||||
Assertions.assertTrue(output.contains("Maximizer"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -193,11 +210,11 @@ class MiniMaxAlgorithmTest {
|
||||
// Test height calculation for different array sizes
|
||||
int[] scores2 = {1, 2};
|
||||
miniMax.setScores(scores2);
|
||||
assertEquals(1, miniMax.getHeight()); // log2(2) = 1
|
||||
Assertions.assertEquals(1, miniMax.getHeight()); // log2(2) = 1
|
||||
|
||||
int[] scores16 = new int[16];
|
||||
miniMax.setScores(scores16);
|
||||
assertEquals(4, miniMax.getHeight()); // log2(16) = 4
|
||||
Assertions.assertEquals(4, miniMax.getHeight()); // log2(16) = 4
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -206,7 +223,7 @@ class MiniMaxAlgorithmTest {
|
||||
miniMax.setScores(zeroScores);
|
||||
|
||||
int result = miniMax.miniMax(0, true, 0, false);
|
||||
assertEquals(0, result);
|
||||
Assertions.assertEquals(0, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -218,11 +235,7 @@ class MiniMaxAlgorithmTest {
|
||||
// Level 1 (minimizer): min(-5,-2) = -5, min(-8,-1) = -8
|
||||
// Level 0 (maximizer): max(-5, -8) = -5
|
||||
int result = miniMax.miniMax(0, true, 0, false);
|
||||
assertEquals(-5, result);
|
||||
}
|
||||
|
||||
void tearDown() {
|
||||
System.setOut(originalOut);
|
||||
Assertions.assertEquals(-5, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -233,12 +246,9 @@ class MiniMaxAlgorithmTest {
|
||||
|
||||
// Test with array length 0 (edge case for n > 0 condition)
|
||||
int[] emptyArray = new int[0];
|
||||
outputStream.reset();
|
||||
miniMax.setScores(emptyArray);
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> miniMax.setScores(emptyArray));
|
||||
|
||||
String output = outputStream.toString();
|
||||
assertTrue(output.contains("The number of scores must be a power of 2."));
|
||||
assertEquals(8, miniMax.getScores().length); // Should remain unchanged
|
||||
Assertions.assertEquals(8, miniMax.getScores().length); // Should remain unchanged
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -250,8 +260,8 @@ class MiniMaxAlgorithmTest {
|
||||
}
|
||||
|
||||
miniMax.setScores(largeValidScores);
|
||||
assertArrayEquals(largeValidScores, miniMax.getScores());
|
||||
assertEquals(5, miniMax.getHeight()); // log2(32) = 5
|
||||
Assertions.assertArrayEquals(largeValidScores, miniMax.getScores());
|
||||
Assertions.assertEquals(5, miniMax.getHeight()); // log2(32) = 5
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -270,8 +280,65 @@ class MiniMaxAlgorithmTest {
|
||||
|
||||
for (int i = 0; i < validPowersOf2.length; i++) {
|
||||
miniMax.setScores(validPowersOf2[i]);
|
||||
assertEquals(validPowersOf2[i].length, miniMax.getScores().length, "Failed for array length: " + validPowersOf2[i].length);
|
||||
assertEquals(expectedHeights[i], miniMax.getHeight(), "Height calculation failed for array length: " + validPowersOf2[i].length);
|
||||
Assertions.assertEquals(validPowersOf2[i].length, miniMax.getScores().length, "Failed for array length: " + validPowersOf2[i].length);
|
||||
Assertions.assertEquals(expectedHeights[i], miniMax.getHeight(), "Height calculation failed for array length: " + validPowersOf2[i].length);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetScoresReturnsDefensiveCopy() {
|
||||
int[] originalScores = {10, 20, 30, 40};
|
||||
miniMax.setScores(originalScores);
|
||||
|
||||
// Get the scores and modify them
|
||||
int[] retrievedScores = miniMax.getScores();
|
||||
retrievedScores[0] = 999;
|
||||
|
||||
// Internal state should remain unchanged
|
||||
Assertions.assertEquals(10, miniMax.getScores()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetScoresCreatesDefensiveCopy() {
|
||||
int[] originalScores = {10, 20, 30, 40};
|
||||
miniMax.setScores(originalScores);
|
||||
|
||||
// Modify the original array after setting
|
||||
originalScores[0] = 999;
|
||||
|
||||
// Internal state should remain unchanged
|
||||
Assertions.assertEquals(10, miniMax.getScores()[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMiniMaxWithAllSameScores() {
|
||||
int[] sameScores = {5, 5, 5, 5};
|
||||
miniMax.setScores(sameScores);
|
||||
|
||||
// When all scores are the same, result should be that score
|
||||
int result = miniMax.miniMax(0, true, 0, false);
|
||||
Assertions.assertEquals(5, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMiniMaxAtDifferentDepths() {
|
||||
int[] testScores = {3, 12, 8, 2, 14, 5, 2, 9};
|
||||
miniMax.setScores(testScores);
|
||||
|
||||
// Test maximizer first
|
||||
int result = miniMax.miniMax(0, true, 0, false);
|
||||
// Expected: max(min(max(3,12), max(8,2)), min(max(14,5), max(2,9)))
|
||||
// = max(min(12, 8), min(14, 9)) = max(8, 9) = 9
|
||||
Assertions.assertEquals(9, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMiniMaxWithMinIntAndMaxInt() {
|
||||
int[] extremeScores = {Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 1};
|
||||
miniMax.setScores(extremeScores);
|
||||
|
||||
int result = miniMax.miniMax(0, true, 0, false);
|
||||
// Expected: max(min(MIN, MAX), min(0, 1)) = max(MIN, 0) = 0
|
||||
Assertions.assertEquals(0, result);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user