Skip to content

Commit 44b0a99

Browse files
committed
Add Tower of Hanoi.
1 parent 26ba21b commit 44b0a99

File tree

5 files changed

+165
-1
lines changed

5 files changed

+165
-1
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
* [Eulerian Path and Eulerian Circuit](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/eulerian-path) - Fleury's algorithm
7878
* [Strongly Connected Components](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/strongly-connected-components) - Kosaraju's algorithm
7979
* **Uncategorized**
80+
* [Tower of Hanoi](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/uncategorized/hanoi-tower)
8081
* Union-Find
8182
* Maze
8283
* Sudoku
@@ -89,6 +90,7 @@
8990
* [Prim’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
9091
* [Kruskal’s Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/kruskal) - finding Minimum Spanning Tree (MST) for weighted undirected graph
9192
* **Divide and Conquer**
93+
* [Tower of Hanoi](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/uncategorized/hanoi-tower)
9294
* [Euclidean Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
9395
* [Permutations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/permutations) (with and without repetitions)
9496
* [Combinations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/combinations) (with and without repetitions)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Tower of Hanoi
2+
3+
The Tower of Hanoi (also called the Tower of Brahma or Lucas'
4+
Tower and sometimes pluralized) is a mathematical game or puzzle.
5+
It consists of three rods and a number of disks of different sizes,
6+
which can slide onto any rod. The puzzle starts with the disks in
7+
a neat stack in ascending order of size on one rod, the smallest
8+
at the top, thus making a conical shape.
9+
10+
The objective of the puzzle is to move the entire stack to another
11+
rod, obeying the following simple rules:
12+
13+
- Only one disk can be moved at a time.
14+
- Each move consists of taking the upper disk from one of the
15+
stacks and placing it on top of another stack or on an empty rod.
16+
- No disk may be placed on top of a smaller disk.
17+
18+
![Hanoi Tower](https://upload.wikimedia.org/wikipedia/commons/8/8d/Iterative_algorithm_solving_a_6_disks_Tower_of_Hanoi.gif)
19+
20+
Animation of an iterative algorithm solving 6-disk problem
21+
22+
With `3` disks, the puzzle can be solved in `7` moves. The minimal
23+
number of moves required to solve a Tower of Hanoi puzzle
24+
is `2n − 1`, where `n` is the number of disks.
25+
26+
## References
27+
28+
- [Wikipedia](https://en.wikipedia.org/wiki/Tower_of_Hanoi)
29+
- [HackerEarth](https://www.hackerearth.com/blog/algorithms/tower-hanoi-recursion-game-algorithm-explained/)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import hanoiTower from '../hanoiTower';
2+
3+
describe('hanoiTower', () => {
4+
it('should solve tower of hanoi puzzle with 2 discs', () => {
5+
const moveCallbackMock = jest.fn();
6+
7+
hanoiTower(2, moveCallbackMock);
8+
9+
expect(moveCallbackMock).toHaveBeenCalledTimes(3);
10+
11+
expect(moveCallbackMock.mock.calls[0][0]).toBe(1);
12+
expect(moveCallbackMock.mock.calls[0][1]).toEqual([1, 2]);
13+
expect(moveCallbackMock.mock.calls[0][2]).toEqual([]);
14+
15+
expect(moveCallbackMock.mock.calls[1][0]).toBe(2);
16+
expect(moveCallbackMock.mock.calls[1][1]).toEqual([2]);
17+
expect(moveCallbackMock.mock.calls[1][2]).toEqual([]);
18+
19+
expect(moveCallbackMock.mock.calls[2][0]).toBe(1);
20+
expect(moveCallbackMock.mock.calls[2][1]).toEqual([1]);
21+
expect(moveCallbackMock.mock.calls[2][2]).toEqual([2]);
22+
});
23+
24+
it('should solve tower of hanoi puzzle with 3 discs', () => {
25+
const moveCallbackMock = jest.fn();
26+
27+
hanoiTower(3, moveCallbackMock);
28+
29+
expect(moveCallbackMock).toHaveBeenCalledTimes(7);
30+
});
31+
32+
it('should solve tower of hanoi puzzle with 6 discs', () => {
33+
const moveCallbackMock = jest.fn();
34+
35+
hanoiTower(6, moveCallbackMock);
36+
37+
expect(moveCallbackMock).toHaveBeenCalledTimes(63);
38+
});
39+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import Stack from '../../../data-structures/stack/Stack';
2+
3+
/**
4+
* @param {Stack} fromPole
5+
* @param {Stack} toPole
6+
* @param {function(disc: number, fromPole: number[], toPole: number[])} moveCallback
7+
*/
8+
function moveDisc(fromPole, toPole, moveCallback) {
9+
moveCallback(fromPole.peek(), fromPole.toArray(), toPole.toArray());
10+
11+
const disc = fromPole.pop();
12+
toPole.push(disc);
13+
}
14+
15+
/**
16+
* @param {number} numberOfDiscs
17+
* @param {Stack} fromPole
18+
* @param {Stack} withPole
19+
* @param {Stack} toPole
20+
* @param {function(disc: number, fromPole: number[], toPole: number[])} moveCallback
21+
*/
22+
function hanoiTowerRecursive({
23+
numberOfDiscs,
24+
fromPole,
25+
withPole,
26+
toPole,
27+
moveCallback,
28+
}) {
29+
if (numberOfDiscs === 1) {
30+
// Base case with just one disc.
31+
moveDisc(fromPole, toPole, moveCallback);
32+
} else {
33+
// In case if there are more discs then move them recursively.
34+
35+
// Expose the bottom disc on fromPole stack.
36+
hanoiTowerRecursive({
37+
numberOfDiscs: numberOfDiscs - 1,
38+
fromPole,
39+
withPole: toPole,
40+
toPole: withPole,
41+
moveCallback,
42+
});
43+
44+
// Move the disc that was exposed to its final destination.
45+
hanoiTowerRecursive({
46+
numberOfDiscs: 1,
47+
fromPole,
48+
withPole,
49+
toPole,
50+
moveCallback,
51+
});
52+
53+
// Move temporary tower from auxiliary pole to its final destination.
54+
hanoiTowerRecursive({
55+
numberOfDiscs: numberOfDiscs - 1,
56+
fromPole: withPole,
57+
withPole: fromPole,
58+
toPole,
59+
moveCallback,
60+
});
61+
}
62+
}
63+
64+
/**
65+
* @param {number} numberOfDiscs
66+
* @param {function(disc: number, fromPole: number[], toPole: number[])} moveCallback
67+
*/
68+
export default function hanoiTower(numberOfDiscs, moveCallback) {
69+
// Each of three poles of Tower of Hanoi puzzle is represented as a stack
70+
// that might contain elements (discs). Each disc is represented as a number.
71+
// Larger discs have bigger number equivalent.
72+
73+
// The pole from where the discs should be moved.
74+
const fromPole = new Stack();
75+
76+
// The middle pole that should be used as a helper.
77+
const withPole = new Stack();
78+
79+
// The destination pole where all discs need to be moved.
80+
const toPole = new Stack();
81+
82+
// Let's create the discs and put them to the fromPole.
83+
for (let discSize = numberOfDiscs; discSize > 0; discSize -= 1) {
84+
fromPole.push(discSize);
85+
}
86+
87+
hanoiTowerRecursive({
88+
numberOfDiscs,
89+
fromPole,
90+
withPole,
91+
toPole,
92+
moveCallback,
93+
});
94+
}

src/data-structures/stack/Stack.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default class Stack {
1313
}
1414

1515
/**
16-
* @return {LinkedListNode}
16+
* @return {*}
1717
*/
1818
peek() {
1919
if (!this.linkedList.tail) {

0 commit comments

Comments
 (0)