diff --git a/20_hanoi/README.md b/20_hanoi/README.md new file mode 100644 index 00000000000..4892fb40106 --- /dev/null +++ b/20_hanoi/README.md @@ -0,0 +1,41 @@ +# Exercise 20 - hanoi + +Good job, you have come a very long way. You should be proud of yourself for making it this far! At this point, you hopefully feel comfortable with recursion, and so, this **final recursive exercise** should give you a proper challenge and take your abilities to the *next* level. + +Tower of Hanoi is a classic challenge in which we have 3 towers. Tower 2 and tower 3 are empty. Tower 1 has `n` disks. The goal is to move all disks from tower 1 to tower 3 so that they are in the exact same order. For instance: + +```javascript +[[3, 2, 1], [], []] + +// ... + +[[], [], [3, 2, 1]] +``` + +The rules are as follows: +- Each disk has a length. For abstraction purposes, we will imagine each disk as an integer with that disk's length. +- We can only move 1 disk at a time between any of the towers +- The towers are modelled as stacks. We can move disks from the top of one stack to the top of another stack with the `Array.prototype.pop` and `Array.prototype.push` methods. +- **Disks can only be placed on top of disks that are smaller**. For instance, we cannot have the tower `[3, 4]` but `[4, 3]` is fine. + +Your task is to create a function, `hanoi(n)`, that when given the number of disks in the starting tower (`n`), will return an array containing the steps that have to be taken in order to get from the initial state to the final state. Each step will be a string in the form: + +```javascript +`Move disc ${discNumber} from tower ${fromTower} to tower ${toTower}` +``` + +The function **must** return a solution in the minimum number of moves. i.e. there will be no duplicates in the solution array returned. + +For example, here is the expected output of `hanoi(3)`: + +```javascript +[ + "Move disc 1 from tower 1 to tower 3", + "Move disc 2 from tower 1 to tower 2", + "Move disc 1 from tower 3 to tower 2", + "Move disc 3 from tower 1 to tower 3", + "Move disc 1 from tower 2 to tower 1", + "Move disc 2 from tower 2 to tower 3", + "Move disc 1 from tower 1 to tower 3", +] +``` diff --git a/20_hanoi/hanoi.js b/20_hanoi/hanoi.js new file mode 100644 index 00000000000..1f84ae7b454 --- /dev/null +++ b/20_hanoi/hanoi.js @@ -0,0 +1,6 @@ +const hanoi = function() { + +}; + +// Do not edit below this line +module.exports = hanoi; diff --git a/20_hanoi/hanoi.spec.js b/20_hanoi/hanoi.spec.js new file mode 100644 index 00000000000..da85314d734 --- /dev/null +++ b/20_hanoi/hanoi.spec.js @@ -0,0 +1,52 @@ +const hanoi = require("./hanoi"); + +describe("hanoi", () => { + test("hanoi(1) should solve the puzzle in 1 move", () => { + const result = hanoi(1); + expect(result).toEqual(["Move disc 1 from tower 1 to tower 3"]); + }); + + test.skip("hanoi(2) should solve the puzzle in 3 moves", () => { + const result = hanoi(2); + expect(result).toEqual([ + "Move disc 1 from tower 1 to tower 2", + "Move disc 2 from tower 1 to tower 3", + "Move disc 1 from tower 2 to tower 3", + ]); + }); + + test.skip("hanoi(3) should solve the puzzle in 7 moves", () => { + const result = hanoi(3); + expect(result).toEqual([ + "Move disc 1 from tower 1 to tower 3", + "Move disc 2 from tower 1 to tower 2", + "Move disc 1 from tower 3 to tower 2", + "Move disc 3 from tower 1 to tower 3", + "Move disc 1 from tower 2 to tower 1", + "Move disc 2 from tower 2 to tower 3", + "Move disc 1 from tower 1 to tower 3", + ]); + }); + + test.skip("hanoi(4) should solve the puzzle in 15 moves", () => { + const result = hanoi(4); + expect(result).toEqual([ + "Move disc 1 from tower 1 to tower 2", + "Move disc 2 from tower 1 to tower 3", + "Move disc 1 from tower 2 to tower 3", + "Move disc 3 from tower 1 to tower 2", + "Move disc 1 from tower 3 to tower 1", + "Move disc 2 from tower 3 to tower 2", + "Move disc 1 from tower 1 to tower 2", + "Move disc 4 from tower 1 to tower 3", + "Move disc 1 from tower 2 to tower 3", + "Move disc 2 from tower 2 to tower 1", + "Move disc 1 from tower 3 to tower 1", + "Move disc 3 from tower 2 to tower 3", + "Move disc 1 from tower 1 to tower 2", + "Move disc 2 from tower 1 to tower 3", + "Move disc 1 from tower 2 to tower 3", + ]); + }); +}); + diff --git a/20_hanoi/solution/hanoi-solution.js b/20_hanoi/solution/hanoi-solution.js new file mode 100644 index 00000000000..7c65cdf9107 --- /dev/null +++ b/20_hanoi/solution/hanoi-solution.js @@ -0,0 +1,24 @@ +const hanoi = function ( + n, + fromTower = 0, + storageTower = 1, + toTower = 2, + steps = [], +) { + if (n <= 0) { + return steps; + } + + hanoi(n - 1, fromTower, toTower, storageTower, steps); + + steps.push( + `Move disc ${n} from tower ${fromTower + 1} to tower ${toTower + 1}`, + ); + + hanoi(n - 1, storageTower, fromTower, toTower, steps); + + return steps; +}; + +// Do not edit below this line +module.exports = hanoi; diff --git a/20_hanoi/solution/hanoi-solution.spec.js b/20_hanoi/solution/hanoi-solution.spec.js new file mode 100644 index 00000000000..2c7501324e2 --- /dev/null +++ b/20_hanoi/solution/hanoi-solution.spec.js @@ -0,0 +1,51 @@ +const hanoi = require("./hanoi-solution"); + +describe("hanoi", () => { + test("hanoi(1) should solve the puzzle in 1 move", () => { + const result = hanoi(1); + expect(result).toEqual(["Move disc 1 from tower 1 to tower 3"]); + }); + + test("hanoi(2) should solve the puzzle in 3 moves", () => { + const result = hanoi(2); + expect(result).toEqual([ + "Move disc 1 from tower 1 to tower 2", + "Move disc 2 from tower 1 to tower 3", + "Move disc 1 from tower 2 to tower 3", + ]); + }); + + test("hanoi(3) should solve the puzzle in 7 moves", () => { + const result = hanoi(3); + expect(result).toEqual([ + "Move disc 1 from tower 1 to tower 3", + "Move disc 2 from tower 1 to tower 2", + "Move disc 1 from tower 3 to tower 2", + "Move disc 3 from tower 1 to tower 3", + "Move disc 1 from tower 2 to tower 1", + "Move disc 2 from tower 2 to tower 3", + "Move disc 1 from tower 1 to tower 3", + ]); + }); + + test("hanoi(4) should solve the puzzle in 15 moves", () => { + const result = hanoi(4); + expect(result).toEqual([ + "Move disc 1 from tower 1 to tower 2", + "Move disc 2 from tower 1 to tower 3", + "Move disc 1 from tower 2 to tower 3", + "Move disc 3 from tower 1 to tower 2", + "Move disc 1 from tower 3 to tower 1", + "Move disc 2 from tower 3 to tower 2", + "Move disc 1 from tower 1 to tower 2", + "Move disc 4 from tower 1 to tower 3", + "Move disc 1 from tower 2 to tower 3", + "Move disc 2 from tower 2 to tower 1", + "Move disc 1 from tower 3 to tower 1", + "Move disc 3 from tower 2 to tower 3", + "Move disc 1 from tower 1 to tower 2", + "Move disc 2 from tower 1 to tower 3", + "Move disc 1 from tower 2 to tower 3", + ]); + }); +});