feat(curriculum): added the Tower of Hanoi Python project (#51347)

Co-authored-by: Naomi Carrigan <nhcarrigan@gmail.com>
Co-authored-by: Krzysztof G. <60067306+gikf@users.noreply.github.com>
Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>
Co-authored-by: Estefania Cassingena Navone <85124911+estefaniacn@users.noreply.github.com>
This commit is contained in:
Dario-DC
2023-12-19 21:11:22 +01:00
committed by GitHub
parent 06bbc07ec6
commit b247803357
58 changed files with 3494 additions and 0 deletions
+7
View File
@@ -1191,6 +1191,13 @@
"title": "Upcoming Python Project",
"intro": ["placeholder"]
},
"learn-recursion-by-solving-the-tower-of-hanoi-puzzle": {
"title": "Learn Recursion by Solving the Tower of Hanoi Puzzle",
"intro": [
"Recursion allows you to write concise and elegant code to solve complex computational problems.",
"In this project, you'll start with an iterative approach to solve the tower of Hanoi mathematical puzzle and then you'll learn how to implement a recursive solution."
]
},
"learn-string-manipulation-by-building-a-cipher": {
"title": "Learn String Manipulation by Building a Cipher",
"intro": [
@@ -0,0 +1,10 @@
---
title: Introduction to the Learn Recursion by Solving the Tower of Hanoi Puzzle
block: learn-recursion-by-solving-the-tower-of-hanoi-puzzle
superBlock: upcoming-python
isBeta: true
---
## Introduction to the Learn Recursion by Solving the Tower of Hanoi Puzzle
This is a test for the new project-based curriculum.
@@ -0,0 +1,237 @@
{
"name": "Learn Recursion by Solving the Tower of Hanoi Puzzle",
"isUpcomingChange": true,
"usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-recursion-by-solving-the-tower-of-hanoi-puzzle",
"order": 3,
"time": "5 hours",
"template": "",
"required": [],
"superBlock": "upcoming-python",
"superOrder": 20,
"isBeta": true,
"challengeOrder": [
{
"id": "64dc8b1faf9c60304ca8a9b6",
"title": "Step 1"
},
{
"id": "64de1469fccce61940f498c4",
"title": "Step 2"
},
{
"id": "64dc8ea01436383a88256d7f",
"title": "Step 3"
},
{
"id": "64dc9004f658183afa85f4f1",
"title": "Step 4"
},
{
"id": "64dc90949ff85e3b37be40d0",
"title": "Step 5"
},
{
"id": "64dc90e837670a3ba26a1e61",
"title": "Step 6"
},
{
"id": "64dc916138dbe03bdfe6cec6",
"title": "Step 7"
},
{
"id": "64dc922df2919e3c38cead61",
"title": "Step 8"
},
{
"id": "64dc92a9718fb53ca3d1bad7",
"title": "Step 9"
},
{
"id": "64dc963d866fff3dd0329993",
"title": "Step 10"
},
{
"id": "64dc97005bc7943e2978df0a",
"title": "Step 11"
},
{
"id": "64dc976bf864693e668d67e8",
"title": "Step 12"
},
{
"id": "64de4bccf5becb208a48ca97",
"title": "Step 13"
},
{
"id": "64df72c6e0183a191fcd72dc",
"title": "Step 14"
},
{
"id": "64e33a488b4a2717fa22ebf3",
"title": "Step 15"
},
{
"id": "64dc97ab25730b3e9de86ea5",
"title": "Step 16"
},
{
"id": "64dc985e6720a23edac63b51",
"title": "Step 17"
},
{
"id": "65782342850feb3b8d62f936",
"title": "Step 18"
},
{
"id": "6578273de26b6e5965a9107d",
"title": "Step 19"
},
{
"id": "64dcd3d61c448e2676501f43",
"title": "Step 20"
},
{
"id": "64dcd9bbc2268127e7898d77",
"title": "Step 21"
},
{
"id": "64dcdee07a14f0299ec30559",
"title": "Step 22"
},
{
"id": "64dce5f6ff907c2b2c47bb05",
"title": "Step 23"
},
{
"id": "64dce7ee2494e82bf4205a3f",
"title": "Step 24"
},
{
"id": "64dceadec3c9cf2ca7b56de6",
"title": "Step 25"
},
{
"id": "64ddcc2ee182e0164c3539f8",
"title": "Step 26"
},
{
"id": "64ddce6f6e8066173208235b",
"title": "Step 27"
},
{
"id": "64ddd02838f36117cd9738eb",
"title": "Step 28"
},
{
"id": "64ddd280d69fa818ca782d50",
"title": "Step 29"
},
{
"id": "64ddd4fbb4b598199acf5ec5",
"title": "Step 30"
},
{
"id": "64ddd65848a12919d7e1c7d0",
"title": "Step 31"
},
{
"id": "64de6c8a5305d8173a3a9e09",
"title": "Step 32"
},
{
"id": "64de7357cc75bd18bdced920",
"title": "Step 33"
},
{
"id": "64de73f6c2486518e3064fec",
"title": "Step 34"
},
{
"id": "64de7662244db513d7b673ec",
"title": "Step 35"
},
{
"id": "64de773f81facd14653f49c8",
"title": "Step 36"
},
{
"id": "64de79de2fac6b1536ebcfdd",
"title": "Step 37"
},
{
"id": "64de7be06eb689161dd63cf0",
"title": "Step 38"
},
{
"id": "64df202aa1342114cd077920",
"title": "Step 39"
},
{
"id": "64df332162988b13c35b7f7d",
"title": "Step 40"
},
{
"id": "64df346f4c86461419974c1e",
"title": "Step 41"
},
{
"id": "64df353d7ae6dc148fd64f53",
"title": "Step 42"
},
{
"id": "64df3e2fac34d813d048f3f9",
"title": "Step 43"
},
{
"id": "64df3f1011888113fbd3d81b",
"title": "Step 44"
},
{
"id": "64e337e3096b7c1739d934e6",
"title": "Step 45"
},
{
"id": "64df45a3ad4f8719e5355244",
"title": "Step 46"
},
{
"id": "64e3392996b41d17a1375643",
"title": "Step 47"
},
{
"id": "64e340302bd28513f3e73740",
"title": "Step 48"
},
{
"id": "64df47b32b92301a815d5ef8",
"title": "Step 49"
},
{
"id": "64df496c6a8ddf1b38db1ed6",
"title": "Step 50"
},
{
"id": "64e340ecee18af1430939018",
"title": "Step 51"
},
{
"id": "650c6082e5586f9e3acfcd3b",
"title": "Step 52"
},
{
"id": "64e34146860065146733883b",
"title": "Step 53"
},
{
"id": "657b667a772ed53e82962c81",
"title": "Step 54"
},
{
"id": "657b6a4a49faec5c600287ba",
"title": "Step 55"
}
],
"helpCategory": "Python"
}
@@ -0,0 +1,42 @@
---
id: 64dc8b1faf9c60304ca8a9b6
title: Step 1
challengeType: 20
dashedName: step-1
---
# --description--
In this project, you will solve the mathematical puzzle known as the Tower of Hanoi. The puzzle consists of three rods and a number of disks of different diameters.
The goal of this puzzle is moving the disks from the first rod to the third rod, following specific rules that restrict placing a larger disk on top of a smaller one.
Start by creating an empty dictionary named `rods` to represent the rods.
# --hints--
You should have a variable named `rods`.
```js
({ test: () => assert(__userGlobals.has('rods')) })
```
Your `rods` variable should be an empty dictionary.
```js
({ test: () => assert(__pyodide.runPython(`
rods = __locals.get("rods")
rods == {}
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,50 @@
---
id: 64dc8ea01436383a88256d7f
title: Step 3
challengeType: 20
dashedName: step-3
---
# --description--
The puzzle starts with the disks piled up on the first rod, in decreasing size. You need to populate your first list with numbers representing the various disk sizes.
Instead of adding the items manually to the first list, generate a sequence of numbers from `3` to `1` by using the `range()` function and assign it to `rods['A']`.
The syntax is `range(x, y, h)`, where `x` is the starting integer (inclusive), `y` is the last integer (not inclusive), and `h` is the difference between a number and the next one in the sequence.
# --hints--
You should use the `range()` function to assign a sequence of numbers to `rods['A']`. The syntax for calling the `range()` function is `range(x, y, h)`.
```js
({ test: () => assert(__pyodide.runPython(`
a = __locals.get('rods')
type(a['A']) is range
`))
})
```
You should use the `range()` function to assign the sequence of numbers from `3` to `1` to `rods['A']`.
```js
({ test: () => assert(__pyodide.runPython(`
a = __locals.get('rods')
a['A'] == range(3, 0, -1)
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
rods = {
'A': [],
'B': [],
'C': []
}
--fcc-editable-region--
```
@@ -0,0 +1,38 @@
---
id: 64dc9004f658183afa85f4f1
title: Step 4
challengeType: 20
dashedName: step-4
---
# --description--
Now check the data type of your `'A'` key by passing it to the `type()` function and print the result on the terminal.
# --hints--
You should pass your `'A'` key to the `type()` function.
```js
({ test: () => assert.match(code, /type\s*\(\s*rods\s*\[\s*('|")\s*A\s*\1\s*\]\)/) })
```
You should print the type of `rods['A']` using the `print()` function.
```js
({ test: () => assert.match(code, /print\s*\(\s*type\s*\(\s*rods\s*\[\s*('|")\s*A\s*\1\s*\]\)\s*\)/) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
rods = {
'A': range(3, 0, -1),
'B': [],
'C': []
}
--fcc-editable-region--
```
@@ -0,0 +1,39 @@
---
id: 64dc90949ff85e3b37be40d0
title: Step 5
challengeType: 20
dashedName: step-5
---
# --description--
The `range()` function returns an immutable sequence of numbers. As you can see, the data type of `rods['A']` is `range`, but you want it to be a list.
Pass your `range()` call to the `list()` function to do that.
# --hints--
You should pass your `range()` call to the `list()` function.
```js
({ test: () => assert(__pyodide.runPython(`
a = __locals.get('rods')
a['A'] == list(range(3, 0, -1))
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
rods = {
'A': range(3, 0, -1),
'B': [],
'C': []
}
print(type(rods['A']))
--fcc-editable-region--
```
@@ -0,0 +1,33 @@
---
id: 64dc90e837670a3ba26a1e61
title: Step 6
challengeType: 20
dashedName: step-6
---
# --description--
Now that the type is `list` as required, you can remove the `print()` call.
# --hints--
You should remove `print(type(rods['A']))` from your code.
```js
({ test: () => assert.match(code, /\}(?!\s+print\s*\(\s*type\s*\(\s*rods\[\s*('|")A\1\s*\]\s*\)\s*\))/) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
rods = {
'A': list(range(3, 0, -1)),
'B': [],
'C': []
}
print(type(rods['A']))
--fcc-editable-region--
```
@@ -0,0 +1,45 @@
---
id: 64dc916138dbe03bdfe6cec6
title: Step 7
challengeType: 20
dashedName: step-7
---
# --description--
The goal of the Tower of Hanoi is moving all the disks to the last rod. To do that, you must follow three simple rules:
1. You can move only top-most disks
2. You can move only one disk at a time
3. You cannot place larger disks on top of smaller ones
Below your existing code, declare an empty function named `move`. Later on, you will use that function to move the disks between the rods. For now, to avoid errors, use the `pass` keyword inside the function body.
# --hints--
You should declare an empty function named `move`. Remember to use the `pass` keyword inside the function body with the correct indentation.
```js
({ test: () => {
assert(__pyodide.runPython(`
import inspect
inspect.isfunction(__locals.get('move'))
`))
}
})
```
# --seed--
## --seed-contents--
```py
rods = {
'A': list(range(3, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,48 @@
---
id: 64dc922df2919e3c38cead61
title: Step 8
challengeType: 20
dashedName: step-8
---
# --description--
At the top of your code, declare a variable named `NUMBER_OF_DISKS` to store the number of disks and give it the value of `3`. Then, replace the first argument passed in to the `range()` function with your new variable.
# --hints--
You should declare a variable named `NUMBER_OF_DISKS`.
```js
({test: () => assert(__userGlobals.has('NUMBER_OF_DISKS')) })
```
The `NUMBER_OF_DISKS` variable should have the value `3`.
```js
({test: () => assert.equal(__userGlobals.get('NUMBER_OF_DISKS'), 3) })
```
You should change the value of the `range()` function's first argument into the `NUMBER_OF_DISKS` variable.
```js
({test: () => assert.match(code, /('|")A\1\s*:\s*list\s*\(\s*range\s*\(\s*NUMBER_OF_DISKS\s*,\s*0\s*,\s*-\s*1\s*\)\s*\)/) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
rods = {
'A': list(range(3, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move():
pass
```
@@ -0,0 +1,49 @@
---
id: 64dc92a9718fb53ca3d1bad7
title: Step 9
challengeType: 20
dashedName: step-9
---
# --description--
The Tower of Hanoi puzzle can be solved in 2<sup>n</sup> - 1 moves, where n is the number of disks. Declare a variable named `number_of_moves` and assign the total number of moves to this variable.
The power operator in Python is `**`.
# --hints--
You should declare a variable named `number_of_moves`.
```js
({ test: () => assert(__userGlobals.has('number_of_moves')) })
```
The value of `number_of_moves` should be the expression to calculate the number of moves.
```js
({ test: () => {
assert(__pyodide.runPython(`
a = __locals.get('NUMBER_OF_DISKS')
__locals.get('number_of_moves') == 2**a -1
`))
} })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
NUMBER_OF_DISKS = 3
--fcc-editable-region--
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move():
pass
```
@@ -0,0 +1,39 @@
---
id: 64dc963d866fff3dd0329993
title: Step 10
challengeType: 20
dashedName: step-10
---
# --description--
Print the variable you declared in the previous step and feel free to change the number of disks to see how fast the required minimum number of moves increases.
# --hints--
You should print the `number_of_moves` variable.
```js
({ test: () => assert.match(code, /print\s*\(\s*number_of_moves\s*\)/) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
--fcc-editable-region--
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move():
pass
```
@@ -0,0 +1,43 @@
---
id: 64dc97005bc7943e2978df0a
title: Step 11
challengeType: 20
dashedName: step-11
---
# --description--
Now you can remove your `print()` call. Then, inside the `move()` function, remove the `pass` keyword and print the content of your `rods` dictionary.
# --hints--
You should not have `print(number_of_moves)` in your code.
```js
({ test: () => assert.isFalse(/print\s*\(\s*number_of_moves\s*\)/.test(code)) })
```
You should remove the `pass` keyword and print the `rods` dictionary.
```js
({ test: () => assert.match(code, /def\s+move\s*\(\s*\)\s*:\s+print\s*\(\s*rods\s*\)(?!\s*pass)/) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
print(number_of_moves)
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move():
pass
--fcc-editable-region--
```
@@ -0,0 +1,37 @@
---
id: 64dc976bf864693e668d67e8
title: Step 12
challengeType: 20
dashedName: step-12
---
# --description--
Now call your function and see the output on the terminal.
# --hints--
You should call your `move()` function.
```js
({test: () => assert.match(code, /(?<!def\s+)move\(\s*\)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move():
print(rods)
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,67 @@
---
id: 64dc97ab25730b3e9de86ea5
title: Step 16
challengeType: 20
dashedName: step-16
---
# --description--
At the end of this project, you will create a recursive solution to the Tower of Hanoi puzzle, but now you are going to explore an iterative approach to this problem.
Start by adding a `for` loop to your function that iterates through the number of moves and prints the current iteration number.
# --hints--
You should write a `for` loop to iterate through the number of moves. Use the `range()` function for that.
```js
({ test: () => {
const rgs = [
/for\s+\w+\s+in\s+range\s*\(\s*number_of_moves\s*\)\s*:/,
/for\s+\w+\s+in\s+range\s*\(\s*0\s*,\s*number_of_moves\s*\)\s*:/,
/for\s+\w+\s+in\s+range\s*\(\s*0\s*,\s*number_of_moves\s*,\s*1\s*\)\s*:/
]
const loop = rgs.some(r => code.match(r));
assert.isTrue(loop);
}
})
```
You should print the current move number at each iteration.
```js
({ test: () => {
const rgs = [
/for\s+(\w+)\s+in\s+range\s*\(\s*number_of_moves\s*\)\s*:\s+print\s*\(\s*\1\s*\)/,
/for\s+(\w+)\s+in\s+range\s*\(\s*0\s*,\s*number_of_moves\s*\)\s*:\s+print\s*\(\s*\1\s*\)/,
/for\s+(\w+)\s+in\s+range\s*\(\s*0\s*,\s*number_of_moves\s*,\s*1\s*\)\s*:\s+print\s*\(\s*\1\s*\)/
]
const loop = rgs.some(r => code.match(r));
assert.isTrue(loop);
}
})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,51 @@
---
id: 64dc985e6720a23edac63b51
title: Step 17
challengeType: 20
dashedName: step-17
---
# --description--
The allowed disk movements between the rods exhibit a repetitive pattern occurring every three moves. For example, movements between rod `A` and rod `C` are allowed on the first, the fourth and the seventh move, where the remainder of the division between the move number and 3 is equal to 1.
Inside the previously created `for` loop, replace the existing `print()` call with an `if` statement that is triggered when `(i + 1) % 3 == 1`. Within this `if` statement, print `f'Move {i + 1} allowed between {source} and {target}'` using an f-string. Please, note that `i + 1` is the move number since `i` is zero during the first iteration.
# --hints--
You should have an `if` statement that should be executed when `(i + 1) % 3 == 1`.
```js
({test: () => assert.match(code, /if\s+\(\s*i\s*\+\s*1\s*\)\s*%\s*3\s*==\s*1\s*:/)})
```
You should print the string `f'Move {i + 1} allowed between {source} and {target}'` using an f-string.
```js
({test: () => assert.match(code, /print\s*\(\s*f('|")Move\s\{\s*i\s*\+\s*1\s*\}\sallowed\sbetween\s\{\s*source\s*\}\sand\s\{\s*target\s*\s*\}\1\s*\)/)})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
print(i)
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,52 @@
---
id: 64dcd3d61c448e2676501f43
title: Step 20
challengeType: 20
dashedName: step-20
---
# --description--
When the remainder of the move number divided by 3 is equal to 2, the movement is allowed between `A` and `B` (the source and the auxiliary rods).
Add an `elif` statement for that. Then, print the appropriate string if the condition is met.
# --hints--
You should have an `elif` statement to execute when `remainder == 2`.
```js
({test: () => assert.match(code, /elif\s+remainder\s*==\s*2:/)})
```
You should print the string `f'Move {i + 1} allowed between {source} and {auxiliary}'`.
```js
({test: () => assert.match(code, /print\s*\(\s*f('|")Move\s\{\s*i\s*\+\s*1\s*\}\sallowed\sbetween\s\{\s*source\s*\}\sand\s\{\s*auxiliary\s*\s*\}\1\s*\)/)})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,54 @@
---
id: 64dcd9bbc2268127e7898d77
title: Step 21
challengeType: 20
dashedName: step-21
---
# --description--
Finally, when the move number divided by 3 has no remainder, the movement is allowed between `B` and `C`.
Add an `elif` statement for that. Then, print the appropriate string if the condition is met.
# --hints--
You should have an `elif` statement to execute when `remainder == 0`.
```js
({test: () => assert.match(code, /elif\s+remainder\s*==\s*0\s*:/)})
```
You should print the string `f'Move {i + 1} allowed between {auxiliary} and {target}'`.
```js
({test: () => assert.match(code, /print\s*\(\s*f('|")Move\s\{\s*i\s*\+\s*1\s*\}\sallowed\sbetween\s\{\s*auxiliary\s*\}\sand\s\{\s*target\s*\s*\}\1\s*\)/)})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,56 @@
---
id: 64dcdee07a14f0299ec30559
title: Step 22
challengeType: 20
dashedName: step-22
---
# --description--
You wrote the code to find the allowed movement between the rods, but the actual move could be in both directions.
After the `print()` call, declare a variable named `forward` and set it to `False`. You will use that variable to check in which direction you need to move the disk between the rods.
# --hints--
You should declare a variable named `forward`.
```js
({ test: () => assert.match(code, /forward\s*=\s*./) })
```
Your `forward` variable should be set to `False`.
```js
({ test: () => assert.match(code, /forward\s*=\s*False/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
--fcc-editable-region--
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
--fcc-editable-region--
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,78 @@
---
id: 64dce5f6ff907c2b2c47bb05
title: Step 23
challengeType: 20
dashedName: step-23
---
# --description--
When `target` is empty, the disk should be moved necessarily from `source` to `target`.
After the declaration of `forward`, add an `if` statement to check if `rods[target]` is empty. If it is, change `forward` to `True`.
# --hints--
You should have an `if` statement to check if `rods[target]` is empty, and assign `True` to `forward`.
```js
({
test: () => {
const tCode = code.replace(/\r/g, '');
const ifBlock = __helpers.python.getBlock(tCode, "if remainder == 1");
const { block_body } = ifBlock
const if2 = __helpers.python.getBlock(block_body, /if [^:]+/);
const if2Indent = if2.block_body.match(/ +/)[0];
const ifLoc = tCode.indexOf(block_body.slice(0,20));
const ifLen = block_body.length;
const newIf = block_body + `\n${if2Indent}__spy(forward)`
const newCode = `
__counter = 0
def __spy(x):
global __counter
if x:
__counter += 1
${tCode.slice(0,ifLoc)}
${newIf}
${tCode.slice(ifLoc + ifLen)}
__counter
`;
const out = __pyodide.runPython(newCode);
assert.equal(out, 3);
}
})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
--fcc-editable-region--
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
forward = False
--fcc-editable-region--
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,72 @@
---
id: 64dce7ee2494e82bf4205a3f
title: Step 24
challengeType: 20
dashedName: step-24
---
# --description--
The other case in which you have to move the disk necessarily from `source` to `target` is when the source list is **not** empty **and** the last disk in `source` is lower than the last disk in `target`.
Add an `elif` statement to check this condition. Then, set the `forward` variable to `True` if the condition is met.
# --hints--
You should have an `elif` statement to check if `rods[source]` is not empty and `rods[source][-1] < rods[target][-1]`.
```js
const allowedRes = [
"rods\\s*\\[\\s*source\\s*\\]",
"len\\(rods\\s*\\[\\s*source\\s*\\]\\)\\s*>\\s*0",
"len\\(rods\\s*\\[\\s*source\\s*\\]\\)\\s*>=\\s*1",
];
const re = new RegExp(`elif\\s+((${allowedRes.join(")|(")}))\\s+and\\s+(rods\\s*\\[\\s*source\\s*\\]\\[\\s*-\\s*1\\s*\\]\\s*<\\s*rods\\s*\\[\\s*target\\s*\\]\\[\\s*-\\s*1\\s*\\]|rods\\s*\\[\\s*target\\s*\\]\\[\\s*-\\s*1\\s*\\]\\s*>\\s*rods\\s*\\[\\s*source\\s*\\]\\[\\s*-\\s*1\\s*\\])\\s*:`);
assert.match(code, re);
```
You should set `forward` to `True` inside your new `elif` statement. You should not change the previous assignments of `forward`.
```js
const allowedRes = [
"rods\\s*\\[\\s*source\\s*\\]",
"len\\(rods\\s*\\[\\s*source\\s*\\]\\)\\s*>\\s*0",
"len\\(rods\\s*\\[\\s*source\\s*\\]\\)\\s*>=\\s*1",
];
const re = new RegExp(`elif\\s+((${allowedRes.join(")|(")}))\\s+and\\s+(rods\\s*\\[\\s*source\\s*\\]\\[\\s*-\\s*1\\s*\\]\\s*<\\s*rods\\s*\\[\\s*target\\s*\\]\\[\\s*-\\s*1\\s*\\]|rods\\s*\\[\\s*target\\s*\\]\\[\\s*-\\s*1\\s*\\]\\s*>\\s*rods\\s*\\[\\s*source\\s*\\]\\[\\s*-\\s*1\\s*\\])\\s*:\\s+forward\\s*=\\s*True`);
assert.match(code, re);
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
forward = False
--fcc-editable-region--
if not rods[target]:
forward = True
--fcc-editable-region--
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,60 @@
---
id: 64dceadec3c9cf2ca7b56de6
title: Step 25
challengeType: 20
dashedName: step-25
---
# --description--
Next, below the nested `elif` statement, add another `if` statement that should be executed when `forward` is `True`. Inside this conditional, print the following f-string: `f'Moving disk {rods[source][-1]} from {source} to {target}'`.
# --hints--
You should add an `if` statement to execute when `forward` is `True` after the `elif` block.
```js
({ test: () => assert.match(code, /^\s{12}if\s+forward(\s*==\s*True)?\s*:/m) })
```
You should print the provide string inside your new `if` statement.
```js
({ test: () => assert.match(code, /if\s+forward(\s*==\s*True)?\s*:\s+print\s*\(\s*f('|")Moving\sdisk\s\{\s*rods\[\s*source\s*\]\[\s*-\s*1\s*\]\s*\}\sfrom\s\{\s*source\s*\}\sto\s\{\s*target\s*\}\2\s*\)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
--fcc-editable-region--
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
forward = False
if not rods[target]:
forward = True
elif rods[source] and rods[source][-1] < rods[target][-1]:
forward = True
--fcc-editable-region--
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,62 @@
---
id: 64ddcc2ee182e0164c3539f8
title: Step 26
challengeType: 20
dashedName: step-26
---
# --description--
After printing the move, you need to remove the last element from the source rod and append it to target rod. Use the `.pop()` method and the `.append()` method for that.
# --hints--
You should remove the last element from `rods[source]`.
```js
({ test: () => assert.match(code, /rods\[\s*source\s*\]\.pop\s*\(\s*\)/) })
```
You should have `rods[target].append(rods[source].pop())` in your code.
```js
({ test: () => assert.match(code, /rods\[\s*target\s*\]\.append\s*\(\s*rods\[\s*source\s*\]\.pop\s*\(\s*\)\s*\)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
--fcc-editable-region--
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
forward = False
if not rods[target]:
forward = True
elif rods[source] and rods[source][-1] < rods[target][-1]:
forward = True
if forward:
print(f'Moving disk {rods[source][-1]} from {source} to {target}')
--fcc-editable-region--
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,75 @@
---
id: 64ddce6f6e8066173208235b
title: Step 27
challengeType: 20
dashedName: step-27
---
# --description--
When `forward` is `False`, the disk has to be moved in the opposite direction. Write an `else` clause for that. Print the move and change the content of the lists accordingly.
# --hints--
You should have an `else` clause to move the disks in the opposite direction.
```js
({ test: () => assert.match(code, /rods\[\s*target\s*\]\.append\s*\(\s*rods\[\s*source\s*\]\.pop\s*\(\s*\)\s*\)\s+else\s*:/) })
```
You should have `print(f'Moving disk {rods[target][-1]} from {target} to {source}')` inside your `else` clause.
```js
({ test: () => assert.match(code, /else\s*:\s+print\s*\(\s*f('|")Moving\sdisk\s\{\s*rods\[\s*target\s*\]\[\s*-\s*1\s*\]\s*\}\sfrom\s\{\s*target\s*\}\sto\s\{\s*source\s*\}\1\s*\)/) })
```
You should remove the last element from `rods[target]`.
```js
({ test: () => assert.match(code, /rods\[\s*target\s*\]\.pop\s*\(\s*\)/) })
```
You should have `rods[source].append(rods[target].pop())` in your code.
```js
({ test: () => assert.match(code, /rods\[\s*source\s*\]\.append\s*\(\s*rods\[\s*target\s*\]\.pop\s*\(\s*\)\s*\)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
forward = False
if not rods[target]:
forward = True
elif rods[source] and rods[source][-1] < rods[target][-1]:
forward = True
--fcc-editable-region--
if forward:
print(f'Moving disk {rods[source][-1]} from {source} to {target}')
rods[target].append(rods[source].pop())
--fcc-editable-region--
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,66 @@
---
id: 64ddd02838f36117cd9738eb
title: Step 28
challengeType: 20
dashedName: step-28
---
# --description--
Outside the `else` block, add a comment saying `display our progress` and print the content of the lists to check that everything is working.
# --hints--
You should use the provided text to add a comment outside the `else` block.
```js
({ test: () => assert.match(code, /rods\[\s*source\s*\]\.append\s*\(\s*rods\[\s*target\s*\]\.pop\s*\(\s*\)\s*\)\s+#\s*display\sour\sprogress/) })
```
You should print the `rods` object outside the `else` block, after the comment.
```js
({ test: () => assert.match(code, /rods\[\s*source\s*\]\.append\s*\(\s*rods\[\s*target\s*\]\.pop\s*\(\s*\)\s*\)\s+#\s*display\sour\sprogress\s+print\s*\(\s*rods\s*\)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
forward = False
if not rods[target]:
forward = True
elif rods[source] and rods[source][-1] < rods[target][-1]:
forward = True
--fcc-editable-region--
if forward:
print(f'Moving disk {rods[source][-1]} from {source} to {target}')
rods[target].append(rods[source].pop())
else:
print(f'Moving disk {rods[target][-1]} from {target} to {source}')
rods[source].append(rods[target].pop())
--fcc-editable-region--
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,69 @@
---
id: 64ddd280d69fa818ca782d50
title: Step 29
challengeType: 20
dashedName: step-29
---
# --description--
As you can see, disk 1 is going back and forth every three moves. This happens because you still need to take care of movements between the other rods.
Instead of repeating the same code you wrote during the previous few steps and changing the rods, it would be better to move that code inside a function to call in each conditional statement. Declare an empty function named `make_allowed_move()` and don't forget the `pass` keyword.
# --hints--
You should declare an empty function named `make_allowed_move`. Remember to use the `pass` keyword inside the function body with the correct indentation.
```js
({ test: () => assert(__pyodide.runPython(`
import inspect
inspect.isfunction(__locals.get('make_allowed_move'))
`))
})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
forward = False
if not rods[target]:
forward = True
elif rods[source] and rods[source][-1] < rods[target][-1]:
forward = True
if forward:
print(f'Moving disk {rods[source][-1]} from {source} to {target}')
rods[target].append(rods[source].pop())
else:
print(f'Moving disk {rods[target][-1]} from {target} to {source}')
rods[source].append(rods[target].pop())
# display our progress
print(rods)
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,69 @@
---
id: 64ddd4fbb4b598199acf5ec5
title: Step 30
challengeType: 20
dashedName: step-30
---
# --description--
Add two parameters called `rod1` and `rod2` to your new function.
# --hints--
Your `make_allowed_move()` function should have two parameters named `rod1` and `rod2`.
```js
({ test: () => assert(__pyodide.runPython(`
import inspect
str(inspect.signature(__locals.get('make_allowed_move'))) == '(rod1, rod2)'
`))
})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def make_allowed_move():
pass
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
forward = False
if not rods[target]:
forward = True
elif rods[source] and rods[source][-1] < rods[target][-1]:
forward = True
if forward:
print(f'Moving disk {rods[source][-1]} from {source} to {target}')
rods[target].append(rods[source].pop())
else:
print(f'Moving disk {rods[target][-1]} from {target} to {source}')
rods[source].append(rods[target].pop())
# display our progress
print(rods)
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,69 @@
---
id: 64ddd65848a12919d7e1c7d0
title: Step 31
challengeType: 20
dashedName: step-31
---
# --description--
It's time to move some code from the `move()` function to the `make_allowed_move()` function. Move the code nested inside the first `if` statement (except the first `print()` call) to your new function. Pay close attention to the indentation.
Don't forget to remove the `pass` keyword.
# --hints--
You should move the code nested inside the first `if` statement (except the first `print()` call) to your new function.
```js
({ test: () => {
assert.match(code, /def\s+make_allowed_move\(rod1, rod2\):\s+forward\s+=\s+False\s+if\s+not\s+rods\[\s*target\s*\]\s*:\s+forward\s*=\s*True\s+elif\s+rods\[\s*source\s*\]\s+and\s+rods\[\s*source\s*\]\[-1\]\s<\srods\[\s*target\s*\]\[\s*-\s*1\s*\]:\s+forward\s*=\s*True\s+if\s+forward\s*:\s+print\(\s*f'Moving\sdisk\s\{\s*rods\[\s*source\s*\]\[\s*-\s*1\s*\]\s*\}\sfrom\s\{\s*source\s*\}\sto\s\{\s*target\s*\}'\s*\)\s+rods\[\s*target\s*\]\.append\(\s*rods\[\s*source\s*\]\.pop\(\s*\)\s*\)\s+else\s*:\s+print\(\s*f'Moving\sdisk\s\{\s*rods\[\s*target\s*\]\[\s*-\s*1\s*\]\}\sfrom\s\{\s*target\s*\}\sto\s\{\s*source\s*\}'\s*\)\s+rods\[\s*source\s*\].append\(\s*rods\[\s*target\s*\].pop\(\s*\)\s*\)\s+#\sdisplay\sour\sprogress\s+\print\(\s*rods\s*\)/);
}
})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def make_allowed_move(rod1, rod2):
pass
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
forward = False
if not rods[target]:
forward = True
elif rods[source] and rods[source][-1] < rods[target][-1]:
forward = True
if forward:
print(f'Moving disk {rods[source][-1]} from {source} to {target}')
rods[target].append(rods[source].pop())
else:
print(f'Moving disk {rods[target][-1]} from {target} to {source}')
rods[source].append(rods[target].pop())
# display our progress
print(rods)
--fcc-editable-region--
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,73 @@
---
id: 64de1469fccce61940f498c4
title: Step 2
challengeType: 20
dashedName: step-2
---
# --description--
The `rods` dictionary will represent the three rods with their disks. Give it the strings `'A'`, `'B'`, and `'C'` as keys and set each of them to an empty list.
# --hints--
Your `rods` dictionary should have an `'A'` key.
```js
({ test: () => assert(__pyodide.runPython(`'A' in __locals.get("rods")`)) })
```
`rods['A']` should be an empty list.
```js
({ test: () => assert(__pyodide.runPython(`
a = __locals.get("rods")
a['A'] == []
`))
})
```
Your `rods` dictionary should have a `'B'` key.
```js
({ test: () => assert(__pyodide.runPython(`'B' in __locals.get("rods")`)) })
```
`rods['B']` should be an empty list.
```js
({ test: () => assert(__pyodide.runPython(`
a = __locals.get("rods")
a['B'] == []
`))
})
```
Your `rods` dictionary should have a `'C'` key.
```js
({ test: () => assert(__pyodide.runPython(`'C' in __locals.get("rods")`)) })
```
`rods['C']` should be an empty list.
```js
({ test: () => assert(__pyodide.runPython(`
a = __locals.get("rods")
a['C'] == []
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
rods = {}
--fcc-editable-region--
```
@@ -0,0 +1,56 @@
---
id: 64de4bccf5becb208a48ca97
title: Step 13
challengeType: 20
dashedName: step-13
---
# --description--
In the Tower of Hanoi puzzle, you can identify the three rods according to their purpose:
- The first rod is the source, where all the disks are stacked on top of each other at the beginning of the game.
- The second rod is an auxiliary rod, and it helps in moving the disks to the target rod.
- The third rod is the target, where all the disks should be placed in order at the end of the game.
Currently, the `move()` function does not take any parameters. Change the function declaration to take 4 parameters: `n`, `source`, `auxiliary`, and `target`. Then, pass `NUMBER_OF_DISKS` and the strings `'A'`, `'B'`, and `'C'` as arguments to your function call. The order matters.
# --hints--
Your `move()` function should have `n`, `source`, `auxiliary`, and `target` as the parameters. The order matters.
```js
({ test: () => assert(__pyodide.runPython(`
import inspect
str(inspect.signature(__locals.get('move'))) == '(n, source, auxiliary, target)'
`))
})
```
You should pass `NUMBER_OF_DISKS` and the strings `'A'`, `'B'`, and `'C'` to `move()`. The order matters.
```js
({test: () => assert.match(code, /^move\(\s*NUMBER_OF_DISKS\s*,\s*('|")A\1\s*,\s*('|")B\2\s*,\s*('|")C\3\s*\)/m)
})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move():
print(rods)
move()
--fcc-editable-region--
```
@@ -0,0 +1,70 @@
---
id: 64de6c8a5305d8173a3a9e09
title: Step 32
challengeType: 20
dashedName: step-32
---
# --description--
`make_allowed_move()` takes in `rod1` and `rod2` as parameters. You need a little refactoring here. Change every occurrence of `source` into `rod1`.
# --hints--
You should change each occurrence of `source` inside the `make_allowed_move()` function into `rod1`.
```js
({ test: () => {
assert.match(code, /elif\s+rods\s*\[\s*rod1\s*\]\s+and\s+rods\s*\[\s*rod1\s*\]\[\s*-\s*1\s*\]\s*<\s*rods\s*\[\s*target\s*\]\[\s*-\s*1\s*\]\s*:/);
assert.match(code, /if\s+forward(\s*==\s*True)?\s*:\s+print\s*\(\s*f('|")Moving\sdisk\s\{\s*rods\[\s*rod1\s*\]\[\s*-\s*1\s*\]\s*\}\sfrom\s\{\s*rod1\s*\}\sto\s\{\s*target\s*\}\2\s*\)\s+rods\[\s*target\s*\]\.append\s*\(\s*rods\[\s*rod1\s*\]\.pop\s*\(\s*\)\s*\)/);
assert.match(code, /else\s*:\s+print\s*\(\s*f('|")Moving\sdisk\s\{\s*rods\[\s*target\s*\]\[\s*-\s*1\s*\]\s*\}\sfrom\s\{\s*target\s*\}\sto\s\{\s*rod1\s*\}\1\s*\)/);
assert.match(code, /rods\[\s*rod1\s*\]\.append\s*\(\s*rods\[\s*target\s*\]\.pop\s*\(\s*\)\s*\)/);
}
})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def make_allowed_move(rod1, rod2):
forward = False
if not rods[target]:
forward = True
elif rods[source] and rods[source][-1] < rods[target][-1]:
forward = True
if forward:
print(f'Moving disk {rods[source][-1]} from {source} to {target}')
rods[target].append(rods[source].pop())
else:
print(f'Moving disk {rods[target][-1]} from {target} to {source}')
rods[source].append(rods[target].pop())
# display our progress
print(rods)
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,71 @@
---
id: 64de7357cc75bd18bdced920
title: Step 33
challengeType: 20
dashedName: step-33
---
# --description--
Now change each occurrence of `target` into `rod2`.
# --hints--
You should change each occurrence of `target` inside `make_allowed_move` into `rod2`.
```js
({ test: () => {
assert.match(code, /if\s+not\s+rods\s*\[\s*rod2\s*\]\s*:/);
assert.match(code, /elif\s+rods\s*\[\s*rod1\s*\]\s+and\s+rods\s*\[\s*rod1\s*\]\[\s*-\s*1\s*\]\s*<\s*rods\s*\[\s*rod2\s*\]\[\s*-\s*1\s*\]\s*:/);
assert.match(code, /if\s+forward(\s*==\s*True)?\s*:\s+print\s*\(\s*f('|")Moving\sdisk\s\{\s*rods\[\s*rod1\s*\]\[\s*-\s*1\s*\]\s*\}\sfrom\s\{\s*rod1\s*\}\sto\s\{\s*rod2\s*\}\2\s*\)\s+rods\[\s*rod2\s*\]\.append\s*\(\s*rods\[\s*rod1\s*\]\.pop\s*\(\s*\)\s*\)/);
assert.match(code, /else\s*:\s+print\s*\(\s*f('|")Moving\sdisk\s\{\s*rods\[\s*rod2\s*\]\[\s*-\s*1\s*\]\s*\}\sfrom\s\{\s*rod2\s*\}\sto\s\{\s*rod1\s*\}\1\s*\)/);
assert.match(code, /rods\[\s*rod1\s*\]\.append\s*\(\s*rods\[\s*rod2\s*\]\.pop\s*\(\s*\)\s*\)/);
}
})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def make_allowed_move(rod1, rod2):
forward = False
if not rods[target]:
forward = True
elif rods[rod1] and rods[rod1][-1] < rods[target][-1]:
forward = True
if forward:
print(f'Moving disk {rods[rod1][-1]} from {rod1} to {target}')
rods[target].append(rods[rod1].pop())
else:
print(f'Moving disk {rods[target][-1]} from {target} to {rod1}')
rods[rod1].append(rods[target].pop())
# display our progress
print(rods)
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,66 @@
---
id: 64de73f6c2486518e3064fec
title: Step 34
challengeType: 20
dashedName: step-34
---
# --description--
Now call `make_allowed_move()` and pass in `source` and `target` as the arguments.
# --hints--
You should call `make_allowed_move()` passing `source` and `target` as the arguments.
```js
({ test: () => assert.match(code, /make_allowed_move\s*\(\s*source\s*,\s*target\s*\)(?=\s+elif\s+remainder\s*==\s*2\s*:)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def make_allowed_move(rod1, rod2):
forward = False
if not rods[rod2]:
forward = True
elif rods[rod1] and rods[rod1][-1] < rods[rod2][-1]:
forward = True
if forward:
print(f'Moving disk {rods[rod1][-1]} from {rod1} to {rod2}')
rods[rod2].append(rods[rod1].pop())
else:
print(f'Moving disk {rods[rod2][-1]} from {rod2} to {rod1}')
rods[rod1].append(rods[rod2].pop())
# display our progress
print(rods)
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
--fcc-editable-region--
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
--fcc-editable-region--
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,76 @@
---
id: 64de7662244db513d7b673ec
title: Step 35
challengeType: 20
dashedName: step-35
---
# --description--
Call the `make_allowed_move()` function again inside the two `elif` clauses, and pass in the correct arguments.
# --hints--
You should call `make_allowed_move(source, auxiliary)` inside the first `elif` clause.
```js
({ test: () => {
assert.match(code, /make_allowed_move\s*\(\s*source\s*,\s*auxiliary\s*\)(?=\s+elif\s+remainder\s*==\s*0\s*:)/);
}})
```
You should call `make_allowed_move(auxiliary, target)` inside the second `elif` clause.
```js
assert.match(code, /make_allowed_move\s*\(\s*auxiliary\s*,\s*target\s*\)(?=\s+#\s*initiate\scall)/);
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def make_allowed_move(rod1, rod2):
forward = False
if not rods[rod2]:
forward = True
elif rods[rod1] and rods[rod1][-1] < rods[rod2][-1]:
forward = True
if forward:
print(f'Moving disk {rods[rod1][-1]} from {rod1} to {rod2}')
rods[rod2].append(rods[rod1].pop())
else:
print(f'Moving disk {rods[rod2][-1]} from {rod2} to {rod1}')
rods[rod1].append(rods[rod2].pop())
# display our progress
print(rods)
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
--fcc-editable-region--
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
make_allowed_move(source, target)
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,68 @@
---
id: 64de773f81facd14653f49c8
title: Step 36
challengeType: 20
dashedName: step-36
---
# --description--
It looks like it's working! But the output is not very readable. Print a new line character after printing the rods to fix that.
# --hints--
You should pass a string with a new line character (`\n`) as the second argument for your two `print(rods)` calls.
```js
({ test: () => assert.match(code, /print\(\s*rods\s*,\s*('|")\\n\1\s*\)(?=.*print\(\s*rods\s*,\s*('|")\\n\2\s*\))/s) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def make_allowed_move(rod1, rod2):
forward = False
if not rods[rod2]:
forward = True
elif rods[rod1] and rods[rod1][-1] < rods[rod2][-1]:
forward = True
if forward:
print(f'Moving disk {rods[rod1][-1]} from {rod1} to {rod2}')
rods[rod2].append(rods[rod1].pop())
else:
print(f'Moving disk {rods[rod2][-1]} from {rod2} to {rod1}')
rods[rod1].append(rods[rod2].pop())
--fcc-editable-region--
# display our progress
print(rods)
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
--fcc-editable-region--
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
make_allowed_move(source, target)
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
make_allowed_move(source, auxiliary)
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
make_allowed_move(auxiliary, target)
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,69 @@
---
id: 64de79de2fac6b1536ebcfdd
title: Step 37
challengeType: 20
dashedName: step-37
---
# --description--
The iterative solution of Tower of Hanoi might seem complete, but change the number of disks to `4` and look at the output.
# --hints--
You should set `NUMBER_OF_DISKS` to 4.
```js
({ test: () => assert.equal(__userGlobals.get('NUMBER_OF_DISKS'), 4) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
NUMBER_OF_DISKS = 3
--fcc-editable-region--
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def make_allowed_move(rod1, rod2):
forward = False
if not rods[rod2]:
forward = True
elif rods[rod1] and rods[rod1][-1] < rods[rod2][-1]:
forward = True
if forward:
print(f'Moving disk {rods[rod1][-1]} from {rod1} to {rod2}')
rods[rod2].append(rods[rod1].pop())
else:
print(f'Moving disk {rods[rod2][-1]} from {rod2} to {rod1}')
rods[rod1].append(rods[rod2].pop())
# display our progress
print(rods, '\n')
def move(n, source, auxiliary, target):
# display starting configuration
print(rods, '\n')
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
make_allowed_move(source, target)
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
make_allowed_move(source, auxiliary)
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
make_allowed_move(auxiliary, target)
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,72 @@
---
id: 64de7be06eb689161dd63cf0
title: Step 38
challengeType: 20
dashedName: step-38
---
# --description--
The conditionals you wrote previously are only valid for odd numbers of disks.
Add a nested `if` to execute when `n` is odd, and add one indent level to your `print()` and `make_allowed_move()` calls.
# --hints--
You should nest an `if` statement to execute when `n` is odd. Don't forget to indent your `print()` and `make_allowed_move()` calls.
```js
({ test: () => assert.match(code, /if\s+n\s*%\s*2\s*(!=\s*0|==\s*1)\s*:\s+print.*\s+make_allowed_move\(.*\)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def make_allowed_move(rod1, rod2):
forward = False
if not rods[rod2]:
forward = True
elif rods[rod1] and rods[rod1][-1] < rods[rod2][-1]:
forward = True
if forward:
print(f'Moving disk {rods[rod1][-1]} from {rod1} to {rod2}')
rods[rod2].append(rods[rod1].pop())
else:
print(f'Moving disk {rods[rod2][-1]} from {rod2} to {rod1}')
rods[rod1].append(rods[rod2].pop())
# display our progress
print(rods, '\n')
def move(n, source, auxiliary, target):
# display starting configuration
print(rods, '\n')
for i in range(number_of_moves):
remainder = (i + 1) % 3
--fcc-editable-region--
if remainder == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
make_allowed_move(source, target)
--fcc-editable-region--
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
make_allowed_move(source, auxiliary)
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
make_allowed_move(auxiliary, target)
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,92 @@
---
id: 64df202aa1342114cd077920
title: Step 39
challengeType: 20
dashedName: step-39
---
# --description--
If the number of disks is even and the remainder equals `1`, the move is allowed between the source rod and the auxiliary rod. Add an `else` clause to print the allowed movement and call `make_allowed_move()` with the correct arguments.
If you look at the output, you can see that the execution stops at the third move because of an `IndexError`. This happens because the code is still incomplete and needs an `else` clause that you will be writing soon. To make it work, turn your `make_allowed_move()` call into a comment.
# --hints--
You should add an `else` clause.
```js
const tCode = code.replace(/\r/g, '');
const ifBlock = __helpers.python.getBlock(tCode, "if remainder == 1");
assert.match(ifBlock.block_body, /else:/);
```
You should have `print(f'Move {i + 1} allowed between {source} and {auxiliary}')` within the `else` clause.
```js
const tCode = code.replace(/\r/g, '');
const ifBlock = __helpers.python.getBlock(tCode, "if remainder == 1");
const elseBlock = __helpers.python.getBlock(ifBlock.block_body, "else");
assert.match(elseBlock.block_body, /print\(\s*f('|")Move\s\{\s*i\s*\+\s*1\s*\}\sallowed\sbetween\s\{\s*source\s*\}\sand\s\{\s*auxiliary\s*\}\1\s*\)/);
```
You should have `# make_allowed_move(source, auxiliary)` within the `else` clause.
```js
const tCode = code.replace(/\r/g, '');
const ifBlock = __helpers.python.getBlock(tCode, "if remainder == 1");
const elseBlock = __helpers.python.getBlock(ifBlock.block_body, "else");
assert.match(elseBlock.block_body, /# *make_allowed_move\(\s*source\s*,\s*auxiliary\s*\)/);
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def make_allowed_move(rod1, rod2):
forward = False
if not rods[rod2]:
forward = True
elif rods[rod1] and rods[rod1][-1] < rods[rod2][-1]:
forward = True
if forward:
print(f'Moving disk {rods[rod1][-1]} from {rod1} to {rod2}')
rods[rod2].append(rods[rod1].pop())
else:
print(f'Moving disk {rods[rod2][-1]} from {rod2} to {rod1}')
rods[rod1].append(rods[rod2].pop())
# display our progress
print(rods, '\n')
def move(n, source, auxiliary, target):
# display starting configuration
print(rods, '\n')
for i in range(number_of_moves):
remainder = (i + 1) % 3
--fcc-editable-region--
if remainder == 1:
if n % 2 != 0:
print(f'Move {i + 1} allowed between {source} and {target}')
make_allowed_move(source, target)
--fcc-editable-region--
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
make_allowed_move(source, auxiliary)
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
make_allowed_move(auxiliary, target)
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,77 @@
---
id: 64df332162988b13c35b7f7d
title: Step 40
challengeType: 20
dashedName: step-40
---
# --description--
Now you need to do the same with your `elif` statement: put the `print()` and `make_allowed_move()` calls inside an `if` statement to execute when `n` is odd.
Also, turn the `# make_allowed_move(source, auxiliary)` comment into code.
# --hints--
You should nest your `print()` and `make_allowed_move()` calls inside an `if` statement to execute when `n` is odd.
```js
({ test: () => assert.match(code, /if\s+n\s*%\s*2\s*(!=\s*0|==\s*1)\s*:\s+print\(\s*f('|")Move\s\{\s*i\s*\+\s*1\s*\}\sallowed\sbetween\s{\s*source\s*\}\sand\s\{\s*auxiliary\s*\}\2\s*\)\s+make_allowed_move\(\s*source\s*,\s*auxiliary\s*\)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def make_allowed_move(rod1, rod2):
forward = False
if not rods[rod2]:
forward = True
elif rods[rod1] and rods[rod1][-1] < rods[rod2][-1]:
forward = True
if forward:
print(f'Moving disk {rods[rod1][-1]} from {rod1} to {rod2}')
rods[rod2].append(rods[rod1].pop())
else:
print(f'Moving disk {rods[rod2][-1]} from {rod2} to {rod1}')
rods[rod1].append(rods[rod2].pop())
# display our progress
print(rods, '\n')
def move(n, source, auxiliary, target):
# display starting configuration
print(rods, '\n')
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
if n % 2 != 0:
print(f'Move {i + 1} allowed between {source} and {target}')
make_allowed_move(source, target)
--fcc-editable-region--
else:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
# make_allowed_move(source, auxiliary)
elif remainder == 2:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
make_allowed_move(source, auxiliary)
--fcc-editable-region--
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
make_allowed_move(auxiliary, target)
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,91 @@
---
id: 64df346f4c86461419974c1e
title: Step 41
challengeType: 20
dashedName: step-41
---
# --description--
Finally, add an `else` clause that prints the allowed move and call `make_allowed_move`. Try to figure out the correct arguments.
# --hints--
You should add an `else` clause.
```js
const tCode = code.replace(/\r/g, '');
const elifBlock = __helpers.python.getBlock(tCode, "elif remainder == 2");
assert.match(elifBlock.block_body, /else:/);
```
You should have `print(f'Move {i + 1} allowed between {source} and {target}')` within the `else` block.
```js
const tCode = code.replace(/\r/g, '');
const elifBlock = __helpers.python.getBlock(tCode, "elif remainder == 2");
assert.match(elifBlock.block_body, /print\(\s*f('|")Move\s\{\s*i\s*\+\s*1\s*\}\sallowed\sbetween\s{\s*source\s*\}\sand\s\{\s*target\s*\}\1\s*\)/);
```
You should have `make_allowed_move(source, target)` within the `else` block.
```js
const tCode = code.replace(/\r/g, '');
const elifBlock = __helpers.python.getBlock(tCode, "elif remainder == 2");
assert.match(elifBlock.block_body, /make_allowed_move\(\s*source\s*,\s*target\s*\)/);
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
number_of_moves = 2 ** NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def make_allowed_move(rod1, rod2):
forward = False
if not rods[rod2]:
forward = True
elif rods[rod1] and rods[rod1][-1] < rods[rod2][-1]:
forward = True
if forward:
print(f'Moving disk {rods[rod1][-1]} from {rod1} to {rod2}')
rods[rod2].append(rods[rod1].pop())
else:
print(f'Moving disk {rods[rod2][-1]} from {rod2} to {rod1}')
rods[rod1].append(rods[rod2].pop())
# display our progress
print(rods, '\n')
def move(n, source, auxiliary, target):
# display starting configuration
print(rods, '\n')
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
if n % 2 != 0:
print(f'Move {i + 1} allowed between {source} and {target}')
make_allowed_move(source, target)
else:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
make_allowed_move(source, auxiliary)
--fcc-editable-region--
elif remainder == 2:
if n % 2 != 0:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
make_allowed_move(source, auxiliary)
--fcc-editable-region--
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
make_allowed_move(auxiliary, target)
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,80 @@
---
id: 64df353d7ae6dc148fd64f53
title: Step 42
challengeType: 20
dashedName: step-42
---
# --description--
That's all for the iterative solution. From now on you are going to build a function that makes use of a recursive approach. Recursion is when a function calls itself.
In this case, you are going to use recursion to calculate smaller versions of the same problem.
Delete the whole body of the `move` function except for the comment and the first `print` call. Leave the function declaration as is.
# --hints--
You should delete the whole body of the `move` function except for the comment and the first `print` call.
```js
({ test: () => assert.match(code, /print\(\s*rods\s*,\s*('|")\\n\1\s*\)\s+(?=#\sinitiate)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
number_of_moves = 2 ** NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def make_allowed_move(rod1, rod2):
forward = False
if not rods[rod2]:
forward = True
elif rods[rod1] and rods[rod1][-1] < rods[rod2][-1]:
forward = True
if forward:
print(f'Moving disk {rods[rod1][-1]} from {rod1} to {rod2}')
rods[rod2].append(rods[rod1].pop())
else:
print(f'Moving disk {rods[rod2][-1]} from {rod2} to {rod1}')
rods[rod1].append(rods[rod2].pop())
# display our progress
print(rods, '\n')
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods, '\n')
for i in range(number_of_moves):
remainder = (i + 1) % 3
if remainder == 1:
if n % 2 != 0:
print(f'Move {i + 1} allowed between {source} and {target}')
make_allowed_move(source, target)
else:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
make_allowed_move(source, auxiliary)
elif remainder == 2:
if n % 2 != 0:
print(f'Move {i + 1} allowed between {source} and {auxiliary}')
make_allowed_move(source, auxiliary)
else:
print(f'Move {i + 1} allowed between {source} and {target}')
make_allowed_move(source, target)
elif remainder == 0:
print(f'Move {i + 1} allowed between {auxiliary} and {target}')
make_allowed_move(auxiliary, target)
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,63 @@
---
id: 64df3e2fac34d813d048f3f9
title: Step 43
challengeType: 20
dashedName: step-43
---
# --description--
You won't need `make_allowed_move` and `number_of_moves`, either. Delete the whole function and the variable.
# --hints--
You should delete the `number_of_moves` variable and its content.
```js
({ test: () => assert.match(code, /NUMBER_OF_DISKS\s*=\s*4\s+(?=rods\s*=\s*\{)/) })
```
You should delete the whole `make_allowed_move` function.
```js
({ test: () => assert.match(code, /\}\s+(?=def\s+move\(\s*n\s*,\s*source\s*,\s*auxiliary\s*,\s*target\s*\)\s*:)/) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
NUMBER_OF_DISKS = 4
number_of_moves = 2 ** NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def make_allowed_move(rod1, rod2):
forward = False
if not rods[rod2]:
forward = True
elif rods[rod1] and rods[rod1][-1] < rods[rod2][-1]:
forward = True
if forward:
print(f'Moving disk {rods[rod1][-1]} from {rod1} to {rod2}')
rods[rod2].append(rods[rod1].pop())
else:
print(f'Moving disk {rods[rod2][-1]} from {rod2} to {rod1}')
rods[rod1].append(rods[rod2].pop())
# display our progress
print(rods, '\n')
def move(n, source, auxiliary, target):
# display starting configuration
print(rods, '\n')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,63 @@
---
id: 64df3f1011888113fbd3d81b
title: Step 44
challengeType: 20
dashedName: step-44
---
# --description--
To solve the puzzle with recursion, the first thing to do is break the original problem down into smaller sub-problems.
The final configuration with `n` disks piled up to the third rod in decreasing order can be obtained by moving:
- `n - 1` disks from the source to the auxiliary rod
- the largest disk from the source to the target
- and then the `n - 1` disks from the auxiliary rod to the target.
So, the first thing the `move` function should do is calling itself with `n - 1` as the first parameter. But if you try to do so without defining a base case, you will get a `RecursionError`. This happens because the function keeps calling itself indefinitely.
Before your comment and your `print()` call, add the recursive function call with `n - 1` as the first parameter and make sure the function body executes only when `n` is greater than zero. For now, leave the others parameters in the same order.
# --hints--
The `move` function body should start with an `if` statement that is triggered when `n` is greater than zero.
```js
({ test: () => assert.match(code, /def\s+move\(\s*n\s*,\s*source\s*,\s*auxiliary\s*,\s*target\s*\)\s*:\s+if\s+n\s*>\s*0\s*:/) })
```
You should call `move(n - 1, source, auxiliary, target)` inside the `if` statement. Remember to indent your `print()` call.
```js
const transformedCode = "\n" + code.replace(/\r/g, '');
const move = __helpers.python.getDef(transformedCode, "move");
const { function_parameters, function_body } = move;
assert.match(function_parameters, /n\s*,\s*source\s*,\s*auxiliary\s*,\s*target/);
const functionBodyIndent = function_body.match(/ +/)[0];
const re = new RegExp(`${functionBodyIndent}if\\s+n\\s*>\\s*0\\s*:\\s+^${functionBodyIndent}( +)move\\(\\s*n\\s*-\\s*1\\s*,\\s*source\\s*,\\s*auxiliary\\s*,\\s*target\\s*\\).+?^${functionBodyIndent}\\1print\\s*\\(\\s*rods\\s*,\\s*("|')\\\\n\\2\\s*\\)`, "ms");
assert.match(function_body, re);
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods, '\n')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,46 @@
---
id: 64df45a3ad4f8719e5355244
title: Step 46
challengeType: 20
dashedName: step-46
---
# --description--
The steps of moving `n - 1` disks can be broken down further until only a single disk is considered. This will be the first move occurring. After the first move occurs, the following moves are generated by the unwinding of the recursive calls. Keep in mind that in each recursive step the role played by the rods changes between source, target, and auxiliary.
For now, each recursive call prints the `rods` dictionary without performing any changes to the lists. Before the `print()` call, remove the last element from the `rods[source]` list and append it to the `rods[target]` list.
# --hints--
You should remove the last element from the `rods[source]` list and append it to the `rods[target]` list before the `print` call.
```js
({ test: () => assert.match(code, /move\(\s*n\s*-\s*1\s*,\s*source\s*,\s*auxiliary\s*,\s*target\s*\)\s+rods\[\s*target\s*\].append\(\s*rods\[\s*source\s*\].pop\(\s*\)\s*\)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
if n > 0:
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, auxiliary, target)
# display starting configuration
print(rods, '\n')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,51 @@
---
id: 64df47b32b92301a815d5ef8
title: Step 49
challengeType: 20
dashedName: step-49
---
# --description--
At first, the recursive call you have just added deals with the sub-problem of moving `n - 1` disks to the second rod.
For that reason, the `target` parameter corresponds to your second rod, while the `auxiliary` parameter is the third rod. Keep in mind that those will keep swapping as the recursion proceeds.
Fix the parameters order exchanging `target` and `auxiliary` in your recursive call.
# --hints--
You should modify the order of the parameters in your `move(n - 1, source, auxiliary, target)` call.
```js
({ test: () => assert.match(code, /move\(\s*n\s*-\s*1\s*,\s*source\s*,\s*target\s*,\s*auxiliary\s*\)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
if n > 0:
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, auxiliary, target)
# move the nth disk from source to target
rods[target].append(rods[source].pop())
# display our progress
print(rods, '\n')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,51 @@
---
id: 64df496c6a8ddf1b38db1ed6
title: Step 50
challengeType: 20
dashedName: step-50
---
# --description--
In a previous step, you wrote the code to move the largest disk of the sub-problem to the target rod.
Now, all you need to do is add another recursive call to move the `n - 1` disks you have already displaced. Copy the first recursive call and paste it at the end of the `if` block.
Note that the function parameters are not in the right order. Try to figure out the correct order.
# --hints--
You should call `move(n - 1, auxiliary, source, target)` at the end of your `move` function.
```js
({ test: () => assert.match(code, /^\s{8}move\(\s*n\s*-\s*1\s*,\s*auxiliary\s*,\s*source\s*,\s*target\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
if n > 0:
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, target, auxiliary)
# move the nth disk from source to target
rods[target].append(rods[source].pop())
# display our progress
print(rods, '\n')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,39 @@
---
id: 64df72c6e0183a191fcd72dc
title: Step 14
challengeType: 20
dashedName: step-14
---
# --description--
Before your function call, write a comment saying `initiate call from source A to target C with auxiliary B`.
# --hints--
You should write the given comment before your function call.
```js
({test: () => assert.match(code, /#\s*initiate\scall\sfrom\ssource\sA\sto\starget\sC\swith\sauxiliary\sB/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move(n, source, auxiliary, target):
print(rods)
--fcc-editable-region--
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
--fcc-editable-region--
```
@@ -0,0 +1,44 @@
---
id: 64e337e3096b7c1739d934e6
title: Step 45
challengeType: 20
dashedName: step-45
---
# --description--
Before your recursive call, add a comment saying `move n - 1 disks from source to auxiliary, so they are out of the way`.
# --hints--
You should add a comment with the provided text before your recursive call.
```js
({ test: () => assert.match(code, /if\s+n\s*>\s*0\s*:\s+#\s*move\sn\s-\s1\sdisks\sfrom\ssource\sto\sauxiliary,\sso\sthey\sare\sout\sof\sthe\sway\s+(?=move\(\s*n\s*-\s*1\s*,\s*source\s*,\s*auxiliary\s*,\s*target\s*\))/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
if n > 0:
move(n - 1, source, auxiliary, target)
# display starting configuration
print(rods, '\n')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,46 @@
---
id: 64e3392996b41d17a1375643
title: Step 47
challengeType: 20
dashedName: step-47
---
# --description--
Before appending the last element to the target, add a comment saying `move the nth disk from source to target`.
# --hints--
You should add a comment with the provided text before `rods[target].append(rods[source].pop())`.
```js
({ test: () => assert.match(code, /move\(\s*n\s*-\s*1\s*,\s*source\s*,\s*auxiliary\s*,\s*target\s*\)\s+#\s*move\sthe\snth\sdisk\sfrom\ssource\sto\starget\s+(?=rods\[\s*target\s*\].append\(\s*rods\[\s*source\s*\].pop\(\s*\)\s*\))/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
if n > 0:
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, auxiliary, target)
rods[target].append(rods[source].pop())
# display starting configuration
print(rods, '\n')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,41 @@
---
id: 64e33a488b4a2717fa22ebf3
title: Step 15
challengeType: 20
dashedName: step-15
---
# --description--
Add another comment before your `print()` call saying `display starting configuration`.
# --hints--
You should use the provided text to add a comment before your `print()` call.
```js
({ test: () => assert.match(code, /#\s*display\sstarting\sconfiguration\s+print\s*\(\s*rods\s*\)/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
print(rods)
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,47 @@
---
id: 64e340302bd28513f3e73740
title: Step 48
challengeType: 20
dashedName: step-48
---
# --description--
Now, change the comment above the `print()` call into `display our progress`.
# --hints--
You should change `# display starting configuration` into `# display our progress`.
```js
({ test: () => assert.match(code, /\)\s+#\s*display\sour\sprogress\s+(?=print\()/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
if n > 0:
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, auxiliary, target)
# move the nth disk from source to target
rods[target].append(rods[source].pop())
# display starting configuration
print(rods, '\n')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,49 @@
---
id: 64e340ecee18af1430939018
title: Step 51
challengeType: 20
dashedName: step-51
---
# --description--
Above the second `move` call, add one last comment saying `move the n - 1 disks that we left on auxiliary onto target`.
# --hints--
You should add a comment using the provided text.
```js
({ test: () => assert.match(code, /#\s*move\sthe\sn\s-\s1\sdisks\sthat\swe\sleft\son\sauxiliary\sonto\starget\s+(?=move\(\s*n\s*-\s*1\s*,\s*auxiliary\s*,\s*source\s*,\s*target\s*\))/) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 4
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
if n > 0:
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, target, auxiliary)
# move the nth disk from source to target
rods[target].append(rods[source].pop())
# display our progress
print(rods, '\n')
move(n - 1, auxiliary, source, target)
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,50 @@
---
id: 64e34146860065146733883b
title: Step 53
challengeType: 20
dashedName: step-53
---
# --description--
Although recursion could sometimes be less easy to understand, it gives you the power to create more concise code. In this case, you don't even need to differentiate between even and odd numbers of disks.
Set `NUMBER_OF_DISKS` to `5` and check the output.
# --hints--
You should set the `NUMBER_OF_DISKS` variable to 5.
```js
({test: () => assert.equal(__userGlobals.get('NUMBER_OF_DISKS'), 5) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
NUMBER_OF_DISKS = 4
A = list(range(NUMBER_OF_DISKS, 0, -1))
B = []
C = []
def move(n, source, auxiliary, target):
if n > 0:
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, target, auxiliary)
# move the nth disk from source to target
target.append(source.pop())
# display our progress
print(A, B, C, '\n')
# move the n - 1 disks that we left on auxiliary onto target
move(n - 1, auxiliary, source, target)
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, A, B, C)
--fcc-editable-region--
```
@@ -0,0 +1,75 @@
---
id: 650c6082e5586f9e3acfcd3b
title: Step 52
challengeType: 20
dashedName: step-52
---
# --description--
Now, delete the `rods` dictionary and turn its keys into the variables `A`, `B`, and `C`, respectively, keeping their values. Refactor your code to reflect these changes. If you see the output on the terminal, you correctly did this step.
# --hints--
You should turn `rods['A']` into a variable named `A` keeping its value.
```js
assert.match(code, /A\s*=\s*list\s*\(\s*range\s*\(\s*NUMBER_OF_DISKS\s*,\s*0\s*,\s*-\s*1\s*\)\s*\)/);
```
You should turn `rods['B']` into a variable named `B` keeping its value.
```js
assert.match(code, /B\s*=\s*\[\s*\]/);
```
You should turn `rods['C']` into a variable named `C` keeping its value.
```js
assert.match(code, /C\s*=\s*\[\s*\]/);
```
You should modify your `print()` call to print `A`, `B`, `C`, instead of the `rods` object. Keep the newline character in the `print()` call.
```js
assert.match(code, /print\s*\(\s*A\s*,\s*B\s*,\s*C\s*,\s*('|")\\n\1\s*\)/);
```
You should have `target.append(source.pop())` in your code.
```js
assert.match(code, /target\.append\s*\(\s*source\.pop\s*\(\s*\)\s*\)/);
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
NUMBER_OF_DISKS = 4
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
def move(n, source, auxiliary, target):
if n > 0:
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, target, auxiliary)
# move the nth disk from source to target
rods[target].append(rods[source].pop())
# display our progress
print(rods, '\n')
# move the n - 1 disks that we left on auxiliary onto target
move(n - 1, auxiliary, source, target)
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
--fcc-editable-region--
```
@@ -0,0 +1,53 @@
---
id: 65782342850feb3b8d62f936
title: Step 18
challengeType: 20
dashedName: step-18
---
# --description--
Since you are going to use the expression `(i + 1) % 3` multiple times, it is convenient to store it in a variable.
Just above your `if` statement, declare a `remainder` variable and assign the value `(i + 1) % 3` to this variable.
# --hints--
You should declare a `remainder` variable just above the `if` block.
```js
({ test: () => assert.match(code, /^\s{8}remainder\s*=/m) })
```
The value of your `remainder` variable should be the expression `(i + 1) % 3`.
```js
({ test: () => assert.match(code, /^\s{8}remainder\s*=\s*\(\s*i\s*\+\s*1\s*\)\s*%\s*3/m) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
if (i + 1) % 3 == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,45 @@
---
id: 6578273de26b6e5965a9107d
title: Step 19
challengeType: 20
dashedName: step-19
---
# --description--
Now, replace the expression in the `if` condition with the `remainder` variable.
# --hints--
You should replace `(i+ 1) % 3` with the `remainder` variable.
```js
({ test: () => assert.match(code, /^\s{8}if\s+remainder\s*==\s*1\s*:/m) })
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 3
number_of_moves = 2**NUMBER_OF_DISKS - 1
rods = {
'A': list(range(NUMBER_OF_DISKS, 0, -1)),
'B': [],
'C': []
}
--fcc-editable-region--
def move(n, source, auxiliary, target):
# display starting configuration
print(rods)
for i in range(number_of_moves):
remainder = (i + 1) % 3
if (i + 1) % 3 == 1:
print(f'Move {i + 1} allowed between {source} and {target}')
--fcc-editable-region--
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, 'A', 'B', 'C')
```
@@ -0,0 +1,69 @@
---
id: 657b667a772ed53e82962c81
title: Step 54
challengeType: 20
dashedName: step-54
---
# --description--
There's still one thing you can do to improve the readability of your code.
Modify your `if` to execute when `n` is less than or equal to zero and add a `return` statement to stop the function execution.
# --hints--
You should modify your existing `if` to check if `n` is less than or equal to `0`.
```js
({ test: () =>
{
const hanoi = __helpers.python.getDef(code, "move");
const {function_body} = hanoi;
assert(function_body.match(/^\s{4}if\s+n\s*<=\s*0\s*:/m));
}
})
```
You should add a `return` statement to the first line of your `if` block.
```js
({ test: () =>
{
const hanoi = __helpers.python.getDef(code, "move");
const {function_body} = hanoi;
assert(function_body.match(/^(\s{4})if\s+n\s*<=\s*0\s*:\s*\1\1return/m));
}
})
```
# --seed--
## --seed-contents--
```py
NUMBER_OF_DISKS = 5
A = list(range(NUMBER_OF_DISKS, 0, -1))
B = []
C = []
--fcc-editable-region--
def move(n, source, auxiliary, target):
if n > 0:
--fcc-editable-region--
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, target, auxiliary)
# move the nth disk from source to target
target.append(source.pop())
# display our progress
print(A, B, C, '\n')
# move the n - 1 disks that we left on auxiliary onto target
move(n - 1, auxiliary, source, target)
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, A, B, C)
```
@@ -0,0 +1,84 @@
---
id: 657b6a4a49faec5c600287ba
title: Step 55
challengeType: 20
dashedName: step-55
---
# --description--
As a final step, reduce the indentation level of all the code after the `return` statement.
Well done. You have completed the Tower of Hanoi practice project.
# --hints--
You should reduce the indentation level of all the code after the `return` statement.
```js
({ test: () => assert(__pyodide.runPython(`
hanoi = __locals.get("move")
a, b, c = [3, 2, 1], [], []
hanoi(3, a, b, c)
a == [] and b == [] and c == [3, 2, 1]
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
NUMBER_OF_DISKS = 5
A = list(range(NUMBER_OF_DISKS, 0, -1))
B = []
C = []
def move(n, source, auxiliary, target):
if n <= 0:
return
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, target, auxiliary)
# move the nth disk from source to target
target.append(source.pop())
# display our progress
print(A, B, C, '\n')
# move the n - 1 disks that we left on auxiliary onto target
move(n - 1, auxiliary, source, target)
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, A, B, C)
--fcc-editable-region--
```
# --solutions--
```py
NUMBER_OF_DISKS = 5
A = list(range(NUMBER_OF_DISKS, 0, -1))
B = []
C = []
def move(n, source, auxiliary, target):
if n <= 0:
return
# move n - 1 disks from source to auxiliary, so they are out of the way
move(n - 1, source, target, auxiliary)
# move the nth disk from source to target
target.append(source.pop())
# display our progress
print(A, B, C, '\n')
# move the n - 1 disks that we left on auxiliary onto target
move(n - 1, auxiliary, source, target)
# initiate call from source A to target C with auxiliary B
move(NUMBER_OF_DISKS, A, B, C)
```