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

@@ -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);
}
}