Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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‎

Lines changed: 2 additions & 0 deletions
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)
Lines changed: 29 additions & 0 deletions
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/)
Lines changed: 39 additions & 0 deletions
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+
});
Lines changed: 94 additions & 0 deletions
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‎

Lines changed: 1 addition & 1 deletion
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)

AltStyle によって変換されたページ (->オリジナル) /