Skip to content
Open
64 changes: 64 additions & 0 deletions src/main/java/com/thealgorithms/recursion/TowerOfHanoi.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.thealgorithms.recursion;

import java.util.ArrayList;
import java.util.List;

/**
* TowerOfHanoi - Solves the classic Tower of Hanoi puzzle
*
* This algorithm uses recursion to move a stack of disks from a source rod to a
* destination rod, following the rules:
* 1. Only one disk can be moved at a time.
* 2. Each move consists of taking the upper disk from one of the stacks and
* placing it on top of another stack.
* 3. No disk may be placed on top of a smaller disk.
*
* Example: If n = 3, Source = 'A', Destination = 'C', Auxiliary = 'B'
* Resulting moves will guide disks from A to C using B.
*
* @author justanothercoder-hub
* @see <a href="https://en.wikipedia.org/wiki/Tower_of_Hanoi">Tower of Hanoi</a>
*/
public final class TowerOfHanoi {

private TowerOfHanoi() {
// Utility class
}

/**
* Solves the Tower of Hanoi puzzle and returns the list of moves
*
* @param n number of disks
* @param source the source rod
* @param destination the destination rod
* @param auxiliary the auxiliary rod
* @return list of moves as strings
*/
public static List<String> solveTowerOfHanoi(int n, char source, char destination, char auxiliary) {
List<String> moves = new ArrayList<>();
if (n < 0) {
throw new IllegalArgumentException("Number of disks cannot be negative");
}
moveDisks(n, source, destination, auxiliary, moves);
return moves;
}

/**
* Recursive helper method to move disks
*
* @param n number of disks
* @param source the source rod
* @param destination the destination rod
* @param auxiliary the auxiliary rod
* @param moves list to record the moves
*/
private static void moveDisks(int n, char source, char destination, char auxiliary, List<String> moves) {
if (n == 1) {
moves.add("Move disk 1 from rod " + source + " to rod " + destination);
return;
}
moveDisks(n - 1, source, auxiliary, destination, moves);
moves.add("Move disk " + n + " from rod " + source + " to rod " + destination);
moveDisks(n - 1, auxiliary, destination, source, moves);
}
}
39 changes: 39 additions & 0 deletions src/test/java/com/thealgorithms/recursion/TowerOfHanoiTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.thealgorithms.recursion;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.Test;

public class TowerOfHanoiTest {

@Test
public void testBaseCase() {
List<String> result = TowerOfHanoi.solveTowerOfHanoi(1, 'A', 'C', 'B');
assertEquals(1, result.size(), "Should have exactly 1 move for 1 disk");
assertEquals("Move disk 1 from rod A to rod C", result.get(0));
}

@Test
public void testSmallRecursion() {
List<String> result = TowerOfHanoi.solveTowerOfHanoi(2, 'A', 'C', 'B');
assertEquals(3, result.size());
List<String> expected = Arrays.asList("Move disk 1 from rod A to rod B", "Move disk 2 from rod A to rod C", "Move disk 1 from rod B to rod C");
assertEquals(expected, result, "Sequence of moves for 2 disks is incorrect");
}

@Test
public void testStandardCase() {
List<String> result = TowerOfHanoi.solveTowerOfHanoi(3, 'A', 'C', 'B');
assertEquals(7, result.size());
assertEquals("Move disk 1 from rod A to rod C", result.get(0));
assertEquals("Move disk 1 from rod A to rod C", result.get(6));
}

@Test
public void testNegativeInput() {
assertThrows(IllegalArgumentException.class, () -> { TowerOfHanoi.solveTowerOfHanoi(-5, 'A', 'C', 'B'); }, "Should throw exception for negative disks");
}
}
Loading