feat(curriculum): add shortest path algorithm python project (#52634)

Co-authored-by: Dario-DC <dicillodario@gmail.com>
Co-authored-by: Dario-DC <105294544+Dario-DC@users.noreply.github.com>
Co-authored-by: Zaira <33151350+zairahira@users.noreply.github.com>
Co-authored-by: Shaun Hamilton <shauhami020@gmail.com>
Co-authored-by: Ihechikara Vincent Abba <ihechikara.dev@gmail.com>
This commit is contained in:
Tom
2023-12-19 23:54:43 -06:00
committed by GitHub
parent 05f7c63ecc
commit 142ec00591
56 changed files with 3373 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
---
title: Introduction to the Learn Algorithm Design by Building the Shortest Path Algorithm
block: learn-algorithm-design-by-building-the-shortest-path-algorithm
superBlock: upcoming-python
isBeta: true
---
## Introduction to the Learn Algorithm Design by Building the Shortest Path Algorithm
This is a test for the new project-based curriculum.

View File

@@ -0,0 +1,232 @@
{
"name": "Learn Algorithm Design by Building the Shortest Path Algorithm",
"isUpcomingChange": false,
"usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-algorithm-design-by-building-the-shortest-path-algorithm",
"order": 5,
"time": "5 hours",
"template": "",
"required": [],
"superBlock": "scientific-computing-with-python",
"isBeta": true,
"challengeOrder": [
{
"id": "65789506b30453080f77470c",
"title": "Step 1"
},
{
"id": "65576ff7888f9e96f52a4be1",
"title": "Step 2"
},
{
"id": "6578b13757611e2825beb8a5",
"title": "Step 3"
},
{
"id": "6578b57361f2f132a02e2a18",
"title": "Step 4"
},
{
"id": "65796fac81f983127558f3f4",
"title": "Step 5"
},
{
"id": "6579717f0920131304286804",
"title": "Step 6"
},
{
"id": "65797670e0c0d016f17e7660",
"title": "Step 7"
},
{
"id": "6579ca0923cfa7162089d2f0",
"title": "Step 8"
},
{
"id": "6579cbab9825b8170974c69a",
"title": "Step 9"
},
{
"id": "6579cd5f6dd62c189e53ddbb",
"title": "Step 10"
},
{
"id": "6579dd49fa8a8e1fd06b85a9",
"title": "Step 11"
},
{
"id": "6557709b0aee699a6a00528c",
"title": "Step 12"
},
{
"id": "6557712d77ce2d9bd7e63afd",
"title": "Step 13"
},
{
"id": "6557716aadbd2d9c42c0e69a",
"title": "Step 14"
},
{
"id": "655771d889132f9ccd341060",
"title": "Step 15"
},
{
"id": "6566195b0a021bb660b2b4b1",
"title": "Step 16"
},
{
"id": "65661b72d6745ebec6a96923",
"title": "Step 17"
},
{
"id": "65577236b056379d5dbc7000",
"title": "Step 18"
},
{
"id": "655773b0591c5f9f4045883e",
"title": "Step 19"
},
{
"id": "655773f8b8b5db9fc6d0ae76",
"title": "Step 20"
},
{
"id": "6557743527cb92a06417ea97",
"title": "Step 21"
},
{
"id": "6557746aad2844a0cd864e12",
"title": "Step 22"
},
{
"id": "655774955b097ea14897db12",
"title": "Step 23"
},
{
"id": "655774d01daeeaa1978b99d5",
"title": "Step 24"
},
{
"id": "655775221059f5a20493d5d7",
"title": "Step 25"
},
{
"id": "655776db1eeae0a620e42a0d",
"title": "Step 26"
},
{
"id": "655777060d8ddea6741be1b1",
"title": "Step 27"
},
{
"id": "65577739f57ecca6c39bb4e9",
"title": "Step 28"
},
{
"id": "65577791ad8c26a7705e2919",
"title": "Step 29"
},
{
"id": "65577a17564ce8a8e06c1460",
"title": "Step 30"
},
{
"id": "65578c17d54dfab65cd54b95",
"title": "Step 31"
},
{
"id": "65578c74607d40b6d8c4757f",
"title": "Step 32"
},
{
"id": "65578cb031cd93b77a285db2",
"title": "Step 33"
},
{
"id": "65578cee7f2cb8b80127cce2",
"title": "Step 34"
},
{
"id": "65578d0f6c78a0b868a43b9c",
"title": "Step 35"
},
{
"id": "65578d4fc3afc3b8f554c882",
"title": "Step 36"
},
{
"id": "65578f895f2a65ba7a916804",
"title": "Step 37"
},
{
"id": "65578fcf00322dbad5dee05b",
"title": "Step 38"
},
{
"id": "657891ab9c1903f4e55433ba",
"title": "Step 39"
},
{
"id": "655790d113d14dbb727eaf41",
"title": "Step 40"
},
{
"id": "6557910b0ebaeebc18209e90",
"title": "Step 41"
},
{
"id": "6557913b8fe5c0bc834c9f4f",
"title": "Step 42"
},
{
"id": "655791847db8a9bd0b685f40",
"title": "Step 43"
},
{
"id": "655791ae44c182bd92f31caa",
"title": "Step 44"
},
{
"id": "655791e6cf5e03be3de73451",
"title": "Step 45"
},
{
"id": "6567722f53ad97d7ea6bb082",
"title": "Step 46"
},
{
"id": "65579228c669fcbebffd01d5",
"title": "Step 47"
},
{
"id": "6557924d47c325bf27afbe51",
"title": "Step 48"
},
{
"id": "6559d70c5161b16ff1d6530d",
"title": "Step 49"
},
{
"id": "6557927ad11e58bf8c794b25",
"title": "Step 50"
},
{
"id": "6559d86fe1b8947954b9178d",
"title": "Step 51"
},
{
"id": "6559da1b7d75f088f5e6b89f",
"title": "Step 52"
},
{
"id": "65774ae7c3eee66fe79b9459",
"title": "Step 53"
},
{
"id": "6559da93115de78dbbdc7ba3",
"title": "Step 54"
}
],
"helpCategory": "Python"
}

View File

@@ -0,0 +1,51 @@
---
id: 65576ff7888f9e96f52a4be1
title: Step 2
challengeType: 20
dashedName: step-2
---
# --description--
Dictionaries store data in the form of *key*-*value* pairs. A key is separated from the correspondent value by a colon. And each key-value pair is separated from the following pair by a comma:
```py
my_dict = {
'name': 'Michael',
'occupation': 'Lumberjack'
}
```
Add a new key-value pair to your dictionary. Use the string `species` as the key, and the string `guinea pig` as the value.
# --hints--
You should add a new key-value pair to your `copper` dictionary.
```js
({ test: () => assert(__pyodide.runPython(`
copper = __locals.get("copper")
len(copper) == 1
`))
})
```
You should have a `species` key with the value `guinea pig` inside your `copper` dictionary.
```js
({ test: () => assert(__pyodide.runPython(`
copper = __locals.get("copper")
copper == {"species": "guinea pig"}
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
copper = {}
--fcc-editable-region--
```

View File

@@ -0,0 +1,75 @@
---
id: 6557709b0aee699a6a00528c
title: Step 12
challengeType: 20
dashedName: step-12
---
# --description--
Now, replace the existent keys with the strings `A` and `B` — one for each node. Then, replace each value with the string representing the node connected to the key.
# --hints--
Your dictionary should have an `A` key.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
"A" in graph
`))
})
```
Your `A` key should have `B` as the value.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
graph["A"] == "B"
`))
})
```
Your dictionary should have an `B` key.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
"B" in graph
`))
})
```
Your `B` key should have `A` as the value.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
graph["B"] == "A"
`))
})
```
Your dictionary should have two keys.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
len(graph) == 2
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
my_graph = {
'species': 'guinea pig',
'age': 2
}
--fcc-editable-region--
```

View File

@@ -0,0 +1,78 @@
---
id: 6557712d77ce2d9bd7e63afd
title: Step 13
challengeType: 20
dashedName: step-13
---
# --description--
Add another node connected to `B` to your graph and call it `C`.
Modify your existing dictionary to represent this arrangement. Use a list to represent the multiple connections of your `B` node.
# --hints--
Your dictionary should have 3 keys — `A`, `B`, and `C`.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
key_list = ["A", "B", "C"]
len(graph) == 3 and all(key in graph for key in key_list)
`))
})
```
`my_graph["A"]` should have the `B` node as the value.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
graph["A"] == "B"
`))
})
```
`my_graph["B"]` should be a list.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
type(graph["B"]) is list
`))
})
```
The value of `my_graph["B"]` should be a list containing the other two nodes.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
len(graph["B"]) == 2 and "A" in graph["B"] and "C" in graph["B"]
`))
})
```
The value of `my_graph["C"]` should be the connected node.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
graph["C"] == "B"
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
my_graph = {
'A': 'B',
'B': 'A'
}
--fcc-editable-region--
```

View File

@@ -0,0 +1,117 @@
---
id: 6557716aadbd2d9c42c0e69a
title: Step 14
challengeType: 20
dashedName: step-14
---
# --description--
Add one last node, `D`, which is connected with `A` and `C`. Modify your dictionary to represent this structure. Again, use a list to represent multiple connections.
# --hints--
Your dictionary should have 4 keys called `A`, `B`, `C`, and `D`.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
key_list = ["A", "B", "C", "D"]
len(graph) == 4 and all(key in graph for key in key_list)
`))
})
```
`my_graph["A"]` should be a list.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
type(graph["A"]) is list
`))
})
```
`my_graph["A"]` should be a list containing `B` and `D`.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
len(graph["A"]) == 2 and "B" in graph["A"] and "D" in graph["A"]
`))
})
```
`my_graph["B"]` should be a list.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
type(graph["B"]) is list
`))
})
```
`my_graph["B"]` should be a list containing `A` and `C`.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
len(graph["B"]) == 2 and "A" in graph["B"] and "C" in graph["B"]
`))
})
```
`my_graph["C"]` should be a list.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
type(graph["C"]) is list
`))
})
```
`my_graph["C"]` should be a list containing `B` and `D`.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
len(graph["C"]) == 2 and "B" in graph["C"] and "D" in graph["C"]
`))
})
```
`my_graph["D"]` should be a list.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
type(graph["D"]) is list
`))
})
```
`my_graph["D"]` should be a list containing `A` and `C`.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
len(graph["D"]) == 2 and "A" in graph["D"] and "C" in graph["D"]
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
my_graph = {
'A': 'B',
'B': ['A', 'C'],
'C': 'B'
}
--fcc-editable-region--
```

View File

@@ -0,0 +1,74 @@
---
id: 655771d889132f9ccd341060
title: Step 15
challengeType: 20
dashedName: step-15
---
# --description--
A graph is called a *weighted* graph when its edges are associated with weights, representing a distance, time or other.
In your case, these weights will be the distances between each node, or point in space.
To represent a weighted graph you can modify your dictionary, using a list of tuples for each value.
The first element in the tuple will be the connected node, and the second element will be an integer number indicating the distance.
Modify `my_graph["A"]` into a list of tuples, considering the following distances:
| Edge | Weight |
|------|--------|
| A-B | 3 |
| B-C | 4 |
| C-D | 7 |
| D-A | 1 |
# --hints--
Your dictionary should have 4 keys called `A`, `B`, `C`, and `D`.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
key_list = ["A", "B", "C", "D"]
len(graph) == 4 and all(key in graph for key in key_list)
`))
})
```
`my_graph["A"]` should be a list of tuples.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
type(graph["A"]) is list and all(type(i) is tuple for i in graph["A"])
`))
})
```
`my_graph["A"]` should be a list of tuples where the first item in the tuple is the connected node and the second item is the distance.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
tuples = [("B", 3), ("D", 1)]
len(graph["A"]) == 2 and all(t in graph["A"] for t in tuples)
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
my_graph = {
'A': ['B', 'D'],
--fcc-editable-region--
'B': ['A', 'C'],
'C': ['B', 'D'],
'D': ['A', 'C']
}
```

View File

@@ -0,0 +1,42 @@
---
id: 65577236b056379d5dbc7000
title: Step 18
challengeType: 20
dashedName: step-18
---
# --description--
Now you are going to start developing the algorithm to calculate the shortest path between each node in your new graph.
Declare an empty function called `shortest_path` and don't forget the `pass` keyword.
# --hints--
You should have a `shortest_path` function.
```js
({ test: () => assert(__pyodide.runPython(`
import inspect
f = __locals.get("shortest_path")
inspect.isfunction(f)
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
```

View File

@@ -0,0 +1,44 @@
---
id: 655773b0591c5f9f4045883e
title: Step 19
challengeType: 20
dashedName: step-19
---
# --description--
The algorithm will start at a specified node. Then it will explore the graph to find the shortest path between the starting node, or *source*, and all the other nodes.
For that your function needs two parameters: `graph`, and `start`. Add them to your function declaration.
# --hints--
Your function should accept `graph` and `start` as the parameters, in this order.
```js
({ test: () => assert(__pyodide.runPython(`
import inspect
f = __locals.get("shortest_path")
sig = str(inspect.signature(f))
sig == '(graph, start)'
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path():
pass
--fcc-editable-region--
```

View File

@@ -0,0 +1,56 @@
---
id: 655773f8b8b5db9fc6d0ae76
title: Step 20
challengeType: 20
dashedName: step-20
---
# --description--
To keep track of the visited nodes, you need a list of all the nodes in the graph. Once a node is visited, it will be removed from that list.
Now, replace the `pass` keyword with a variable named `unvisited` and assign it an empty list.
# --hints--
You should have a variable called `unvisited`.
```js
({ test: () =>
{
const shortest_path = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest_path;
assert(function_body.match(/unvisited\s*=/));
}
})
```
Your `unvisited` variable should be an empty list.
```js
({ test: () =>
{
const shortest_path = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest_path;
assert(function_body.match(/unvisited\s*=\s*\[\s*\]/));
}
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
pass
--fcc-editable-region--
```

View File

@@ -0,0 +1,52 @@
---
id: 6557743527cb92a06417ea97
title: Step 21
challengeType: 20
dashedName: step-21
---
# --description--
Create a `for` loop to iterate over your graph, and append each node to the `unvisited` list.
# --hints--
You should create a `for` loop to iterate over `graph` inside the `shortest_path` function.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)for\s+(\w+)\s+in\s+graph\s*:/m));
}
})
```
You should append each node to `unvisited` inside your `for` loop.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)for\s+(\w+)\s+in\s+graph\s*:\s*^\1\1unvisited\.append\s*\(\s*\2\s*\)/m));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = []
--fcc-editable-region--
```

View File

@@ -0,0 +1,56 @@
---
id: 6557746aad2844a0cd864e12
title: Step 22
challengeType: 20
dashedName: step-22
---
# --description--
While the algorithm explores the graph, it should keep track of the currently known shortest distance between the starting node and the other nodes.
Before your `for` loop, create a new variable named `distances` and assign it an empty dictionary.
# --hints--
You should have a `distances` variable.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)distances\s*=.*(?=^\1for.*:)/ms));
}
})
```
Your `distances` variable should be an empty dictionary.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)distances\s*=\s*\{\s*\}.*(?=^\1for.*:)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = []
for node in graph:
unvisited.append(node)
--fcc-editable-region--
```

View File

@@ -0,0 +1,57 @@
---
id: 655774955b097ea14897db12
title: Step 23
challengeType: 20
dashedName: step-23
---
# --description--
The distance from the starting node is zero, because the algorithm begins its assessment right from there.
After appending `node` to `unvisited` in your loop, create an `if` statement that triggers if the node is equal to the starting node. Then assign `0` to that node inside the `distances` dictionary.
# --hints--
You should create an `if` statement that executes when `node` is equal to `start`.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)for.*:.*^\1\1if\s+node\s*==\s*start\s*:/ms));
}
})
```
Inside your new `if` statement you should assign `0` to the node in the `distances` dictionary.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)for.*:.*^\1\1if\s+node\s*==\s*start\s*:\s*^\1\1\1distances\s*\[\s*node\s*\]\s*=\s*0/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = []
distances = {}
for node in graph:
unvisited.append(node)
--fcc-editable-region--
```

View File

@@ -0,0 +1,59 @@
---
id: 655774d01daeeaa1978b99d5
title: Step 24
challengeType: 20
dashedName: step-24
---
# --description--
At the beginning, all the other nodes in the graph are considered to be at infinite distance from the source node, because the distance has not been determined yet.
Create an `else` clause and assign an infinite value to the node in the `distances` dictionary. For that, use the `float()` function with the string `inf` as argument to generate a floating point number representing the positive infinity.
# --hints--
You should have an `else` clause.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)if.*:.*^\1else\s*:/ms));
}
})
```
You should assign `float('inf')` to `distances[node]` inside your new `else` clause.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)if.*:.*^\1else\s*:\s*^\1\s{4}distances\s*\[\s*node\s*\]\s*=\s*float\s*\(\s*("|')inf\2\s*\)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = []
distances = {}
for node in graph:
unvisited.append(node)
if node == start:
distances[node] = 0
--fcc-editable-region--
```

View File

@@ -0,0 +1,48 @@
---
id: 655775221059f5a20493d5d7
title: Step 25
challengeType: 20
dashedName: step-25
---
# --description--
After your `for` loop, add a `print()` call and pass in the following string to see the values of the variables you have created: `f'Unvisited: {unvisited}\nDistances: {distances}'`.
# --hints--
You should print `f'Unvisited: {unvisited}\nDistances: {distances}'` after your `for` loop.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/(^\s*)for.*:.*^\1print\s*\(\s*f("|')Unvisited:\s*\{\s*unvisited\s*\}\\nDistances:\s\{\s*distances\s*\}\2\s*\)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = []
distances = {}
for node in graph:
unvisited.append(node)
if node == start:
distances[node] = 0
else:
distances[node] = float('inf')
--fcc-editable-region--
```

View File

@@ -0,0 +1,44 @@
---
id: 655776db1eeae0a620e42a0d
title: Step 26
challengeType: 20
dashedName: step-26
---
# --description--
Now, call your function passing `my_graph` and `'A'` as the arguments.
# --hints--
You should call `shortest_path` passing `my_graph` and `'A'` as the arguments.
```js
({ test: () => assert.match(code, /^shortest_path\s*\(\s*my_graph\s*,\s*("|')A\1\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = []
distances = {}
for node in graph:
unvisited.append(node)
if node == start:
distances[node] = 0
else:
distances[node] = float('inf')
print(f'Unvisited: {unvisited}\nDistances: {distances}')
--fcc-editable-region--
```

View File

@@ -0,0 +1,53 @@
---
id: 655777060d8ddea6741be1b1
title: Step 27
challengeType: 20
dashedName: step-27
---
# --description--
All the distances in `distances` are set to infinite, except for the starting node. The `unvisited` list contains all the nodes in your graph. But actually, you don't need that `for` loop to achieve this result.
Remove your `for` loop with its entire body.
# --hints--
You should remove your `for` loop and all the nested code.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/(^\s*)distances\s*=\s*\{\s*\}\s*\1print\s*\(\s*f("|')Unvisited:\s*\{\s*unvisited\s*\}\\nDistances:\s\{\s*distances\s*\}\2\s*\)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = []
distances = {}
for node in graph:
unvisited.append(node)
if node == start:
distances[node] = 0
else:
distances[node] = float('inf')
print(f'Unvisited: {unvisited}\nDistances: {distances}')
shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,59 @@
---
id: 65577739f57ecca6c39bb4e9
title: Step 28
challengeType: 20
dashedName: step-28
---
# --description--
The `list()` type constructor enables you to build a list from an iterable.
Modify the assignment of your `unvisited` variable to use `list()`, and pass `graph` as the iterable.
# --hints--
You should use `list()` to generate a list from the `graph` dictionary.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/list\s*\(\s*graph\s*\)/));
}
})
```
You should assign `list(graph)` to `unvisited`.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/unvisited\s*=\s*list\s*\(\s*graph\s*\)/));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = []
distances = {}
print(f'Unvisited: {unvisited}\nDistances: {distances}')
shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,65 @@
---
id: 65577791ad8c26a7705e2919
title: Step 29
challengeType: 20
dashedName: step-29
---
# --description--
With a dictionary comprehension, you can create a dictionary starting from an existing dictionary:
```py
{key: val for key in dict}
```
You want to keep track of the paths between the starting node and each other node.
After the `distances` variable, create a `paths` variable and assign it a dictionary with all the keys from `graph`. Assign an empty list to each key and use a dictionary comprehension to build your dictionary.
# --hints--
You should have a `paths` variable.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^\s{4}paths\s*\=/m));
}
})
```
Your `paths` variable should use the dictionary comprehension syntax to assign an empty list to each node in graph.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^\s{4}paths\s*\=\s*\{\s*(\w+)\s*:\s*\[\s*\]\s+for\s+\1\s+in\s+graph\s*\}/m));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = list(graph)
distances = {}
print(f'Unvisited: {unvisited}\nDistances: {distances}')
shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,52 @@
---
id: 65577a17564ce8a8e06c1460
title: Step 30
challengeType: 20
dashedName: step-30
---
# --description--
Dictionary comprehensions support conditional `if`/`else` syntax too:
```py
{key: val_1 if condition else val_2 for key in dict}
```
Use a dictionary comprehension to create a dictionary based in `graph` and assign it to the `distances` variable. Give the key a value of zero if the node is equal to the starting node, and infinite otherwise. Use `float('inf')` to achieve the latter.
# --hints--
You should use the dictionary comprehension syntax to give a value to your `distances` variable.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^\s{4}distances\s*=\s*\{\s*(\w+)\s*:\s*0\s+if\s+\1\s*==\s*start\s+else\s+float\s*\(\s*("|')inf\2\s*\)\s+for\s+\1\s+in\s+graph\s*\}/m));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = list(graph)
distances = {}
paths = {node: [] for node in graph}
print(f'Unvisited: {unvisited}\nDistances: {distances}')
shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,49 @@
---
id: 65578c17d54dfab65cd54b95
title: Step 31
challengeType: 20
dashedName: step-31
---
# --description--
Since the algorithm begins its assessment from the starting node, after creating the `paths` dictionary, you need to add the starting node to its own list in the `paths` dictionary.
Use the `append()` method to append `start` to the `paths[start]` list.
# --hints--
You should use the `append()` method to append `start` to `paths[start]`.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^\s{4}paths\s*\[\s*start\s*\]\.append\s*\(\s*start\s*\)/m));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
print(f'Unvisited: {unvisited}\nDistances: {distances}')
shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,47 @@
---
id: 65578c74607d40b6d8c4757f
title: Step 32
challengeType: 20
dashedName: step-32
---
# --description--
Add `\nPaths: {paths}` at the end of the f-string passed to the `print` call, so that it prints the `paths` variable, too.
# --hints--
You should modify your existing `print` call by adding `\nPaths: {paths}` at the end of the f-string.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^\s{4}print\s*\(\s*f("|')Unvisited:\s*\{\s*unvisited\s*\}\\nDistances:\s\{\s*distances\s*\}\\nPaths:\s\{\s*paths\s*\}\1\s*\)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
print(f'Unvisited: {unvisited}\nDistances: {distances}')
shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,46 @@
---
id: 65578cb031cd93b77a285db2
title: Step 33
challengeType: 20
dashedName: step-33
---
# --description--
Your function is going to explore all the nodes connected to the starting node. It will calculate the shortest paths for all of them. Then, it will remove the starting node from the unvisited nodes.
Next, the closest neighbor node will be visited and the process will be repeated until all the nodes are visited.
From now on, you are going to work on the main loop that explores the nodes in the graph. To avoid issues with running an infinite loop during the algorithm development, turn your function call into a comment.
# --hints--
You should turn your function call into a comment.
```js
({ test: () => assert.match(code, /#\s*shortest_path\s*\(\s*my_graph\s*,\s*("|')A\1\s*\)/) })
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,48 @@
---
id: 65578cee7f2cb8b80127cce2
title: Step 34
challengeType: 20
dashedName: step-34
---
# --description--
Before the `print` call, create a `while` loop that runs while `unvisited` is not empty. Use the `pass` keyword to fill the loop body.
# --hints--
You should have a `while` loop that executes while `unvisited` is not empty. Don't forget the `pass` keyword.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s{4})while\s+unvisited\s*:\s*^\1\1pass(?=\s*^\1print)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,63 @@
---
id: 65578d0f6c78a0b868a43b9c
title: Step 35
challengeType: 20
dashedName: step-35
---
# --description--
Inside the `while` loop, the first thing to do is define the current node to visit. For that you can use the `min()` function. It returns the smallest item from the iterable passed as the argument.
Remove `pass`, then create a variable called `current` and assign it `min(unvisited)`.
# --hints--
You should create a `current` variable in your `while` loop.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s{4})while\s+unvisited\s*:\s*^\1\1current\s*=/ms));
}
})
```
You should assign `min(unvisited)` to your `current` variable.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s{4})while\s+unvisited\s*:\s*^\1\1current\s*=\s*min\s*\(\s*unvisited\s*\)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
--fcc-editable-region--
while unvisited:
pass
--fcc-editable-region--
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,54 @@
---
id: 65578d4fc3afc3b8f554c882
title: Step 36
challengeType: 20
dashedName: step-36
---
# --description--
`min()` takes also a keyword-only argument. Passing a function as an additional argument to `min()`, you can modify the way the list items are compared.
The result of the line you've just written in the previous step is the node that comes first in alphabetical order. Instead you want to select the unvisited node having the smallest distance from the starting node.
Pass `key=distances.get` as the second argument to your `min()` call. In this way, the comparison will take place depending on the value each `unvisited` list item has inside the `distances` dictionary.
# --hints--
You should pass `key=distances.get` as the second argument to your `min()` call.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s{4})while\s+unvisited\s*:\s*^\1\1current\s*=\s*min\s*\(\s*unvisited\s*,\s*key\s*=\s*distances\.get\s*\)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
--fcc-editable-region--
while unvisited:
current = min(unvisited)
--fcc-editable-region--
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,50 @@
---
id: 65578f895f2a65ba7a916804
title: Step 37
challengeType: 20
dashedName: step-37
---
# --description--
After the `current` variable assignment, create a `for` loop to iterate over the tuples in the `graph[current]` list. You will need two iterating variables for that.
# --hints--
You should create a `for` loop to iterate over the tuples items in the `graph[current]` list. Use two iterating variables and don't forget to add the `pass` keyword.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)while\s+unvisited\s*:.*^\1\1for\s+\w+\s*,\s*\w+\s+in\s+graph\s*\[\s*current\s*\]\s*:\s*^\1\1\1pass/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
--fcc-editable-region--
while unvisited:
current = min(unvisited, key=distances.get)
--fcc-editable-region--
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,55 @@
---
id: 65578fcf00322dbad5dee05b
title: Step 38
challengeType: 20
dashedName: step-38
---
# --description--
Create an `if` statement to check if the distance of the neighbor node (the second item in the processed tuple) plus the distance of `current` is less than the currently known distance of the neighbor node (the first item in the processed tuple).
Use the `pass` keyword to temporarily fill the body of the `if`.
# --hints--
You should have an `if` statement to check if `distance + distances[current]` is less than `distances[node]`.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)for\s+\w+\s*,\s*\w+\s+in\s+graph\s*\[\s*current\s*\]\s*:\s*^\1(\s{4})if\s+distance\s*\+\s*distances\s*\[\s*current\s*\]\s*<\s*distances\s*\[\s*node\s*\]\s*:/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
--fcc-editable-region--
for node, distance in graph[current]:
pass
--fcc-editable-region--
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,54 @@
---
id: 655790d113d14dbb727eaf41
title: Step 40
challengeType: 20
dashedName: step-40
---
# --description--
Once the distance to a node is set inside the `distances` dictionary, you need to keep track of the path to that node, too. If the distance for the node in the processed tuple has been updated, the last item in its path is the node itself.
Inside your conditional, nest another `if` statement that triggers when the last element of `paths[node]` is equal to `node`.
# --hints--
You should create a nested `if` statements that checks if `paths[node][-1]` is equal to `node`. Don't forget to use `pass`.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)if.*:.*^\1(\s{4})if\s+paths\s*\[\s*node\s*\]\s*\[\s*-\s*1\s*\]\s*==\s*node\s*:\s*^\1\2\2pass/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
--fcc-editable-region--
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
--fcc-editable-region--
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,55 @@
---
id: 6557910b0ebaeebc18209e90
title: Step 41
challengeType: 20
dashedName: step-41
---
# --description--
Now remove `pass` and assign `paths[current]` to `paths[node]`.
# --hints--
You should delete `pass` and assign `paths[current]` to `paths[node]`.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)if.*:.*^\1(\s{4})if\s+paths\s*\[\s*node\s*\]\s*\[\s*-\s*1\s*\]\s*==\s*node\s*:\s*^\1\2\2paths\s*\[\s*node\s*\]\s*=\s*paths\s*\[\s*current\s*\]/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
--fcc-editable-region--
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
if paths[node][-1] == node:
pass
--fcc-editable-region--
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,67 @@
---
id: 6557913b8fe5c0bc834c9f4f
title: Step 42
challengeType: 20
dashedName: step-42
---
# --description--
Next, create an `else` statement and use the `extend()` function to add the current node path to the neighbor node path.
# --hints--
You should create an `else` statement after your nested `if`.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)if\s+paths\s*\[\s*node\s*\]\s*\[\s*-\s*1\s*\]\s*==\s*node\s*:\s*^\1(\s{4})paths\s*\[\s*node\s*\]\s*=\s*paths\s*\[\s*current\s*\]\s*^\1else\s*:/ms));
}
})
```
You should call `extend()` on the neighbour node path passing the current node path as the argument.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)if\s+paths\s*\[\s*node\s*\]\s*\[\s*-\s*1\s*\]\s*==\s*node\s*:\s*^\1(\s{4})paths\s*\[\s*node\s*\]\s*=\s*paths\s*\[\s*current\s*\]\s*^\1else\s*:\s*^\1\2paths\s*\[\s*node\s*\]\.extend\s*\(\s*paths\s*\[\s*current\s*\]\s*\)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
--fcc-editable-region--
if paths[node][-1] == node:
paths[node] = paths[current]
--fcc-editable-region--
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,57 @@
---
id: 655791847db8a9bd0b685f40
title: Step 43
challengeType: 20
dashedName: step-43
---
# --description--
Finally, outside the nested conditionals, append the neighbor node to its path.
# --hints--
You should append `node` to `paths[node]` just after your `else` statement.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)else\s*:\s*^\1\s{4}paths\s*\[\s*node\s*\]\.extend\s*\(\s*paths\s*\[\s*current\s*\]\s*\)\s*^\1paths\s*\[\s*node\s*\]\.append\s*\(\s*node\s*\)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
--fcc-editable-region--
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
if paths[node][-1] == node:
paths[node] = paths[current]
else:
paths[node].extend(paths[current])
--fcc-editable-region--
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,58 @@
---
id: 655791ae44c182bd92f31caa
title: Step 44
challengeType: 20
dashedName: step-44
---
# --description--
Terminate the `while` loop by removing the current node from the `unvisited` list. Pay attention to the indentation.
# --hints--
You should use the `remove()` function to remove the current node from `unvisited` after your `for` loop.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)for.*:.*^\1unvisited\.remove\s*\(\s*current\s*\)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
--fcc-editable-region--
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
if paths[node][-1] == node:
paths[node] = paths[current]
else:
paths[node].extend(paths[current])
paths[node].append(node)
--fcc-editable-region--
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,63 @@
---
id: 655791e6cf5e03be3de73451
title: Step 45
challengeType: 20
dashedName: step-45
---
# --description--
If you try to uncomment your function call, it won't work. You have a couple of bugs to fix. The first one happens because in the nested `if` you are trying to access that might not exist in your `paths` dictionary. So, you need to be sure that `paths[node]` is not empty before accessing `paths[node][-1]`.
Add an additional condition to your nested `if` statement to ensure that `paths[node]` is non-empty before accessing `paths[node][-1]`.
# --hints--
You should add `paths[node]` as the first condition to your nested `if` statement. Use the `and` operator to combine your conditions.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/if\s+paths\s*\[\s*node\s*\]\s+and\s+paths\s*\[\s*node\s*\]\s*\[\s*-\s*1\s*\]\s*==\s*node\s*:/));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
--fcc-editable-region--
if paths[node][-1] == node:
paths[node] = paths[current]
--fcc-editable-region--
else:
paths[node].extend(paths[current])
paths[node].append(node)
unvisited.remove(current)
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,60 @@
---
id: 65579228c669fcbebffd01d5
title: Step 47
challengeType: 20
dashedName: step-47
---
# --description--
The other bug is subtle. When a shorter distance is found for a neighbor node, `paths[current]` gets assigned to the neighbor node path, `paths[node]`.
This means both variables point to the same list. Since lists are mutable, when you append the neighbor node to its path, both `paths[node]` and `paths[current]` are modified because they are the same list. This results in wrong paths, although the distances are correct.
Fix that bug by assigning a copy of `paths[current]` to the neighbor node path. Modify the existing assignment inside your `if` block.
# --hints--
You should use the slice syntax to assign a copy of `paths[current]` to the neighbor node path.
```js
({ test: () => assert.match(code, /^(\s*)if\s+paths\s*\[\s*node\s*\]\s+and\s+paths\s*\[\s*node\s*\]\s*\[\s*-\s*1\s*\]\s*==\s*node\s*:\s*^\1\s{4}paths\s*\[\s*node\s*\]\s*=\s*paths\s*\[\s*current\s*\]\s*\[\s*::?\s*\]/ms) })
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
--fcc-editable-region--
if paths[node] and paths[node][-1] == node:
paths[node] = paths[current]
--fcc-editable-region--
else:
paths[node].extend(paths[current])
paths[node].append(node)
unvisited.remove(current)
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,69 @@
---
id: 6557924d47c325bf27afbe51
title: Step 48
challengeType: 20
dashedName: step-48
---
# --description--
The algorithm is complete but you can improve the output. Also, you can provide the function with an additional argument to return only the path between two nodes.
Add `target` as the third parameter to your function declaration and give it the default value of an empty string.
# --hints--
Your function should take three parameters:`graph`, `start`, and `target`. The order matters.
```js
({ test: () => assert.match(code, /^def\s+shortest_path\s*\(\s*graph\s*,\s*start\s*,\s*target\s*=?\s*.*\s*\)\s*:/m) })
```
The `target` parameter should have the default value of an empty string.
```js
({ test: () => assert(__pyodide.runPython(`
import inspect
foo = __locals.get("shortest_path")
sig = str(inspect.signature(foo))
sig == "(graph, start, target='')"
`))
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start):
--fcc-editable-region--
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
if paths[node] and paths[node][-1] == node:
paths[node] = paths[current][:]
else:
paths[node].extend(paths[current])
paths[node].append(node)
unvisited.remove(current)
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,72 @@
---
id: 6557927ad11e58bf8c794b25
title: Step 50
challengeType: 20
dashedName: step-50
---
# --description--
Create a `for` loop to iterate over `targets_to_print` and print the following f-string: `f'\n{start}-{node} distance: {distances[node]}\nPath: {" -> ".join(paths[node])}'`.
# --hints--
You should create a `for` loop to iterate over `targets_to_print`. Use `node` as iteration variable.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s{4})for\s+node\s+in\s+targets_to_print\s*:/m));
}
})
```
You should print the provided string inside your new `for` loop.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s{4})for\s+node\s+in\s+targets_to_print\s*:\s*^\1\1print\s*\(f("|')\\n\{\s*start\s*\}-\{\s*node\s*\}\sdistance:\s\{\s*distances\s*\[\s*node\s*\]\s*\}\\nPath:\s\{\s*(?=[^\1])("|')\s->\s\3\.join\s*\(\s*paths\s*\[\s*node\s*\]\s*\)\s*\}\2\s*\)/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start, target = ''):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
if paths[node] and paths[node][-1] == node:
paths[node] = paths[current][:]
else:
paths[node].extend(paths[current])
paths[node].append(node)
unvisited.remove(current)
--fcc-editable-region--
targets_to_print = [target] if target else graph
shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,85 @@
---
id: 6559d70c5161b16ff1d6530d
title: Step 49
challengeType: 20
dashedName: step-49
---
# --description--
Python provides a concise way to write `if`/`else` conditionals by using the ternary syntax:
```py
val_1 if condition else val_2
```
The expression above evaluates to `val_1` if `condition` is true, otherwise to `val_2`.
Delete your `print` call and create a variable called `targets_to_print` after your `while` loop. Use the ternary syntax to assign it `[target]` when `target` is truthy, and `graph` otherwise.
# --hints--
You should delete your `print` call.
```js
({ test: () => assert.isFalse( /print\s*\(\s*f("|')Unvisited:\s*\{\s*unvisited\s*\}\\nDistances:\s\{\s*distances\s*\}\\nPaths:\s\{\s*paths\s*\}\1\s*\)/.test(code)) })
```
You should create a variable called `targets_to_print` after your `while` loop.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/unvisited\.remove\(current\).*^\s{4}targets_to_print\s*=/ms));
}
})
```
You should the ternary syntax to assign `[target]` when `target` is truthy, and `graph` otherwise to your `targets_to_print` variable.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/unvisited\.remove\(current\).*^\s{4}targets_to_print\s*=\s*\[\s*target\s*\]\s+if\s+target\s+else\s+graph/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start, target = ''):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
if paths[node] and paths[node][-1] == node:
paths[node] = paths[current][:]
else:
paths[node].extend(paths[current])
paths[node].append(node)
unvisited.remove(current)
--fcc-editable-region--
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,75 @@
---
id: 6559d86fe1b8947954b9178d
title: Step 51
challengeType: 20
dashedName: step-51
---
# --description--
Now it's better but you don't want to print the details about the starting node.
Before the `print` call, add an `if` statement to execute when `node` is equal to `start` and use the `continue` keyword to go to the next loop iteration.
# --hints--
You should nest an `if` statement to check that `node` is equal to `start` inside your `for` loop.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s{4})for\s+node\s+in\s+targets_to_print\s*:\s*^\1\1if\s+(node\s*==\s*start|start\s*==\s*node)\s*:/m));
}
})
```
You should use the `continue` keyword to go to the next iteration inside your new `if` statement.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s{4})for\s+node\s+in\s+targets_to_print\s*:\s*^\1\1if\s+(node\s*==\s*start|start\s*==\s*node)\s*:\s*^\1\1\1continue/m));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start, target = ''):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
if paths[node] and paths[node][-1] == node:
paths[node] = paths[current][:]
else:
paths[node].extend(paths[current])
paths[node].append(node)
unvisited.remove(current)
--fcc-editable-region--
targets_to_print = [target] if target else graph
for node in targets_to_print:
print(f'\n{start}-{node} distance: {distances[node]}\nPath: {" -> ".join(paths[node])}')
shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,64 @@
---
id: 6559da1b7d75f088f5e6b89f
title: Step 52
challengeType: 20
dashedName: step-52
---
# --description--
Finally, at the very end of your function, return `distances` and `paths`.
# --hints--
You should return `distances` and `paths` at the bottom of your function.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^\s{4}return\s+distances\s*,\s*paths/m));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start, target = ''):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
if paths[node] and paths[node][-1] == node:
paths[node] = paths[current][:]
else:
paths[node].extend(paths[current])
paths[node].append(node)
unvisited.remove(current)
--fcc-editable-region--
targets_to_print = [target] if target else graph
for node in targets_to_print:
if node == start:
continue
print(f'\n{start}-{node} distance: {distances[node]}\nPath: {" -> ".join(paths[node])}')
shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,107 @@
---
id: 6559da93115de78dbbdc7ba3
title: Step 54
challengeType: 20
dashedName: step-54
---
# --description--
As a final step, modify your function call passing `F` as the third argument and check the output.
With that, the shortest path algorithm is complete.
# --hints--
You should call `shortest_path` passing `my_graph`, `'A'` and `'F'` as the arguments.
```js
({ test: () => assert.match(code, /^shortest_path\s*\(\s*my_graph\s*,\s*("|')A\1\s*,\s*("|')F\2\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
my_graph = {
'A': [('B', 5), ('C', 3), ('E', 11)],
'B': [('A', 5), ('C', 1), ('F', 2)],
'C': [('A', 3), ('B', 1), ('D', 1), ('E', 5)],
'D': [('C',1 ), ('E', 9), ('F', 3)],
'E': [('A', 11), ('C', 5), ('D', 9)],
'F': [('B', 2), ('D', 3)]
}
def shortest_path(graph, start, target = ''):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
if paths[node] and paths[node][-1] == node:
paths[node] = paths[current][:]
else:
paths[node].extend(paths[current])
paths[node].append(node)
unvisited.remove(current)
targets_to_print = [target] if target else graph
for node in targets_to_print:
if node == start:
continue
print(f'\n{start}-{node} distance: {distances[node]}\nPath: {" -> ".join(paths[node])}')
return distances, paths
shortest_path(my_graph, 'A')
--fcc-editable-region--
```
# --solutions--
```py
my_graph = {
'A': [('B', 5), ('C', 3), ('E', 11)],
'B': [('A', 5), ('C', 1), ('F', 2)],
'C': [('A', 3), ('B', 1), ('D', 1), ('E', 5)],
'D': [('C',1 ), ('E', 9), ('F', 3)],
'E': [('A', 11), ('C', 5), ('D', 9)],
'F': [('B', 2), ('D', 3)]
}
def shortest_path(graph, start, target = ''):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
if paths[node] and paths[node][-1] == node:
paths[node] = paths[current][:]
else:
paths[node].extend(paths[current])
paths[node].append(node)
unvisited.remove(current)
targets_to_print = [target] if target else graph
for node in targets_to_print:
if node == start:
continue
print(f'\n{start}-{node} distance: {distances[node]}\nPath: {" -> ".join(paths[node])}')
return distances, paths
shortest_path(my_graph, 'A', 'F')
```

View File

@@ -0,0 +1,49 @@
---
id: 6566195b0a021bb660b2b4b1
title: Step 16
challengeType: 20
dashedName: step-16
---
# --description--
Now modify `my_graph["B"]` into a list of tuples. The `B-C` distance is `4`.
# --hints--
`my_graph["B"]` should be a list of tuples.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
type(graph["B"]) is list and all(type(i) is tuple for i in graph["B"])
`))
})
```
`my_graph["B"]` should be a list of tuples where the first item in the tuple is the connected node and the second item is the distance.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
tuples = [("A", 3), ("C", 4)]
len(graph["B"]) == 2 and all(t in graph["B"] for t in tuples)
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': ['A', 'C'],
--fcc-editable-region--
'C': ['B', 'D'],
'D': ['A', 'C']
}
```

View File

@@ -0,0 +1,69 @@
---
id: 65661b72d6745ebec6a96923
title: Step 17
challengeType: 20
dashedName: step-17
---
# --description--
In the same way, modify the remaining two lists considering that the `C-D` distance is `7`.
# --hints--
`my_graph["C"]` should be a list of tuples.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
type(graph["C"]) is list and all(type(i) is tuple for i in graph["C"])
`))
})
```
`my_graph["C"]` should be a list of tuples where the first item in the tuple is the connected node and the second item is the distance.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
tuples = [("B", 4), ("D", 7)]
len(graph["C"]) == 2 and all(t in graph["C"] for t in tuples)
`))
})
```
`my_graph["D"]` should be a list of tuples.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
type(graph["D"]) is list and all(type(i) is tuple for i in graph["D"])
`))
})
```
`my_graph["D"]` should be a list of tuples where the first item in the tuple is the connected node and the second item is the distance.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
tuples = [("A", 1), ("C", 7)]
len(graph["D"]) == 2 and all(t in graph["D"] for t in tuples)
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': ['B', 'D'],
'D': ['A', 'C']
}
--fcc-editable-region--
```

View File

@@ -0,0 +1,56 @@
---
id: 6567722f53ad97d7ea6bb082
title: Step 46
challengeType: 20
dashedName: step-46
---
# --description--
Now uncomment your function call.
# --hints--
You should restore your `shortest_path(my_graph, 'A')` call.
```js
({ test: () => assert.match(code, /^shortest_path\s*\(\s*my_graph\s*,\s*("|')A\1\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
--fcc-editable-region--
if paths[node] and paths[node][-1] == node:
paths[node] = paths[current]
else:
paths[node].extend(paths[current])
paths[node].append(node)
unvisited.remove(current)
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
--fcc-editable-region--
```

View File

@@ -0,0 +1,84 @@
---
id: 65774ae7c3eee66fe79b9459
title: Step 53
challengeType: 20
dashedName: step-53
---
# --description--
Now, you are going to test your function with another graph. Change `my_graph` into the following graph:
```py
{
'A': [('B', 5), ('C', 3), ('E', 11)],
'B': [('A', 5), ('C', 1), ('F', 2)],
'C': [('A', 3), ('B', 1), ('D', 1), ('E', 5)],
'D': [('C', 1), ('E', 9), ('F', 3)],
'E': [('A', 11), ('C', 5), ('D', 9)],
'F': [('B', 2), ('D', 3)]
}
```
# --hints--
You should modify `my_graph` into the provided graph.
```js
({ test: () => assert(__pyodide.runPython(`
graph = __locals.get("my_graph")
g = {
'A': [('B', 5), ('C', 3), ('E', 11)],
'B': [('A', 5), ('C', 1), ('F', 2)],
'C': [('A', 3), ('B', 1), ('D', 1), ('E', 5)],
'D': [('C',1 ), ('E', 9), ('F', 3)],
'E': [('A', 11), ('C', 5), ('D', 9)],
'F': [('B', 2), ('D', 3)]
}
graph == g
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
--fcc-editable-region--
def shortest_path(graph, start, target = ''):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
if distance + distances[current] < distances[node]:
distances[node] = distance + distances[current]
if paths[node] and paths[node][-1] == node:
paths[node] = paths[current][:]
else:
paths[node].extend(paths[current])
paths[node].append(node)
unvisited.remove(current)
targets_to_print = [target] if target else graph
for node in targets_to_print:
if node == start:
continue
print(f'\n{start}-{node} distance: {distances[node]}\nPath: {" -> ".join(paths[node])}')
return distances, paths
shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,54 @@
---
id: 657891ab9c1903f4e55433ba
title: Step 39
challengeType: 20
dashedName: step-39
---
# --description--
Inside your new `if` block, delete `pass` and reassign the neighbor node distance to the sum of the neighbor node distance plus the distance of `current`.
# --hints--
You should assign `distance + distances[current]` to the neighbor node distance inside your new `if`.
```js
({ test: () => {
const shortest = __helpers.python.getDef(code, "shortest_path");
const {function_body} = shortest;
assert(function_body.match(/^(\s*)if\s+distance\s*\+\s*distances\s*\[\s*current\s*\]\s*<\s*distances\s*\[\s*node\s*\]\s*:\s*^\1\s{4}distances\s*\[\s*node\s*\]\s*=\s*distance\s*\+\s*distances\s*\[\s*current\s*\]/ms));
}
})
```
# --seed--
## --seed-contents--
```py
my_graph = {
'A': [('B', 3), ('D', 1)],
'B': [('A', 3), ('C', 4)],
'C': [('B', 4), ('D', 7)],
'D': [('A', 1), ('C', 7)]
}
def shortest_path(graph, start):
unvisited = list(graph)
distances = {node: 0 if node == start else float('inf') for node in graph}
paths = {node: [] for node in graph}
paths[start].append(start)
while unvisited:
current = min(unvisited, key=distances.get)
for node, distance in graph[current]:
--fcc-editable-region--
if distance + distances[current] < distances[node]:
pass
--fcc-editable-region--
print(f'Unvisited: {unvisited}\nDistances: {distances}\nPaths: {paths}')
#shortest_path(my_graph, 'A')
```

View File

@@ -0,0 +1,45 @@
---
id: 65789506b30453080f77470c
title: Step 1
challengeType: 20
dashedName: step-1
---
# --description--
So far, you have already met different data types:
- Immutable data types, such as integers, strings, tuples, and Booleans.
- Mutable data types, such as lists.
A dictionary is a mutable data type and it is identified by a pair of curly braces, `{}`.
Start by creating a variable called `copper` and assign it an empty dictionary using a pair of curly braces, in the same way you would create an empty list with a pair of square brackets.
# --hints--
You should have a variable called `copper`.
```js
({ test: () => assert(__userGlobals.has("copper")) })
```
Your `copper` variable should have the value of an empty dictionary. Use a pair of curly braces for that.
```js
({ test: () => assert(__pyodide.runPython(`
copper = __locals.get("copper")
copper == {}
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
--fcc-editable-region--
```

View File

@@ -0,0 +1,44 @@
---
id: 6578b13757611e2825beb8a5
title: Step 3
challengeType: 20
dashedName: step-3
---
# --description--
Keys must be unique whitin a dictionary and they can be only immutable data types. This means you cannot use a list or another dictionary as keys.
Add another key `age` to your dictionary and give it the integer number `2` as value.
# --hints--
You should add a new key-value pair to your `copper` dictionary.
```js
({ test: () => assert(__pyodide.runPython(`
copper = __locals.get("copper")
len(copper) == 2
`))
})
```
You should have an `age` key with the value `2` just after `'species': 'guinea pig'`, inside your `copper` dictionary. Don't forget the comma.
```js
({ test: () => assert(__pyodide.runPython(`
copper = __locals.get("copper")
copper == {"species": "guinea pig", "age": 2}
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
copper = {'species': 'guinea pig'}
--fcc-editable-region--
```

View File

@@ -0,0 +1,59 @@
---
id: 6578b57361f2f132a02e2a18
title: Step 4
challengeType: 20
dashedName: step-4
---
# --description--
You can access the data stored in a dictionary through its keys:
```py
my_dict = {
'name': 'Michael',
'occupation': 'Lumberjack`
}
my_dict['name'] # 'Michael'
```
After your dictionary, follow the example above to access the `species` key of `copper` and print the result.
# --hints--
You should not modify your dictionary.
```js
({ test: () => assert(__pyodide.runPython(`
copper = __locals.get("copper")
copper == {"species": "guinea pig", "age": 2}
`))
})
```
You should use `copper['species']` to access the value of the `species` key.
```js
({ test: () => assert.match(code, /copper\s*\[\s*("|')species\1\s*\]/) })
```
You should call `print()` passing `copper['species']` as argument.
```js
({ test: () => assert.match(code, /^print\s*\(\s*copper\s*\[\s*("|')species\1\s*\]\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
copper = {
'species': 'guinea pig',
'age': 2
}
--fcc-editable-region--
```

View File

@@ -0,0 +1,39 @@
---
id: 65796fac81f983127558f3f4
title: Step 5
challengeType: 20
dashedName: step-5
---
# --description--
Now, modify your existing `print()` call to print the value of the `age` key.
# --hints--
You should use `copper['age']` to access the value of the `age` key.
```js
({ test: () => assert.match(code, /copper\s*\[\s*("|')age\1\s*\]/) })
```
You should call `print()` passing `copper['age']` as argument.
```js
({ test: () => assert.match(code, /^print\(\s*copper\s*\[\s*("|')age\1\s*\]\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
copper = {
'species': 'guinea pig',
'age': 2
}
print(copper['species'])
--fcc-editable-region--
```

View File

@@ -0,0 +1,55 @@
---
id: 6579717f0920131304286804
title: Step 6
challengeType: 20
dashedName: step-6
---
# --description--
To add a new key-value pair after declaring a dictionary, you can indicate the key in the same way you would access an existing key, and set the value of the new key by using the assignment operator:
```py
my_dict = {
'name': 'Michael',
'occupation': 'Lumberjack`
}
my_dict['country'] = 'Canada'
```
Delete your `print()` call. Then, after declaring `copper`, add the key `food` to your dictionary and set its value to `hay`.
# --hints--
You should not have `print(copper['age'])` in your code.
```js
({ test: () => assert.notMatch(code, /^print\(\s*copper\s*\[\s*("|')age\1\s*\]\s*\)/m) })
```
You should add the key `food` to `copper` after declaring the dictionary.
```js
({ test: () => assert.match(code, /copper\s*\[\s*("|')food\1\s*\]/) })
```
You should set `copper['food']` to `hay` after declaring the dictionary.
```js
({ test: () => assert.match(code, /^copper\s*\[\s*("|')food\1\s*\]\s*=\s*("|')hay\2/m) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
copper = {
'species': 'guinea pig',
'age': 2
}
print(copper['age'])
--fcc-editable-region--
```

View File

@@ -0,0 +1,32 @@
---
id: 65797670e0c0d016f17e7660
title: Step 7
challengeType: 20
dashedName: step-7
---
# --description--
Now, at the bottom of your code, print `copper`.
# --hints--
You should print `copper` at the bottom of your code.
```js
({ test: () => assert.match(code, /(?<!\}\s*)^print\s*\(\s*copper\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
copper = {
'species': 'guinea pig',
'age': 2
}
copper['food'] = 'hay'
--fcc-editable-region--
```

View File

@@ -0,0 +1,36 @@
---
id: 6579ca0923cfa7162089d2f0
title: Step 8
challengeType: 20
dashedName: step-8
---
# --description--
The same syntax can be used to change the value of an existing key.
Just before the `print()` call, access the `species` key and reassign its value to `Cavia porcellus`.
# --hints--
You should reassign the `copper['species']` to the string `Cavia porcellus` before the `print()` call.
```js
({ test: () => assert.match(code, /^copper\s*\[\s*("|')species\1\s*\]\s*=\s*("|')Cavia porcellus\2.*^print\s*\(\s*copper\s*\)/ms) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
copper = {
'species': 'guinea pig',
'age': 2
}
copper['food'] = 'hay'
print(copper)
--fcc-editable-region--
```

View File

@@ -0,0 +1,46 @@
---
id: 6579cbab9825b8170974c69a
title: Step 9
challengeType: 20
dashedName: step-9
---
# --description--
You can remove a key-value pair from a dictionary by using the `del` keyword. The syntax is the following:
```py
my_dict = {
'name': 'Michael',
'occupation': 'Lumberjack`
}
del my_dict['occupation']
```
Just before your `print()` call, use the `del` keyword to delete the `age` key and its value from `copper`.
# --hints--
You should use the `del` keyword to delete `copper['age']` before the `print()` call.
```js
({ test: () => assert.match(code, /^del\s+copper\[\s*("|')age\1\s*\].*^print\s*\(\s*copper\s*\)/ms) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
copper = {
'species': 'guinea pig',
'age': 2
}
copper['food'] = 'hay'
copper['species'] = 'Cavia porcellus'
print(copper)
--fcc-editable-region--
```

View File

@@ -0,0 +1,37 @@
---
id: 6579cd5f6dd62c189e53ddbb
title: Step 10
challengeType: 20
dashedName: step-10
---
# --description--
Now that you got the basic aspects of dictionaries, you can proceed to build the shortest path algorithm.
Delete every line of code after the declaration of the `copper` dictionary.
# --hints--
You should delete the lines after the declaration of your dictionary.
```js
({ test: () => assert.isFalse( /copper\s*\[.*?\]|del|print\s*\(.*?\)/ms.test(code)) })
```
# --seed--
## --seed-contents--
```py
copper = {
'species': 'guinea pig',
'age': 2
}
--fcc-editable-region--
copper['food'] = 'hay'
copper['species'] = 'Cavia porcellus'
del copper['age']
print(copper)
--fcc-editable-region--
```

View File

@@ -0,0 +1,46 @@
---
id: 6579dd49fa8a8e1fd06b85a9
title: Step 11
challengeType: 20
dashedName: step-11
---
# --description--
Graphs are data structures representing relations between pairs of elements.
These elements, called *nodes*, can be real-life objects, entities, points in space or others. The connections between the nodes are called the *edges*.
For example, a graph can be used to represent two points in the space, `A` and `B`, connected by a path. A graph like this will be made of two nodes connected by an edge.
Rename the `copper` dictionary into `my_graph`. This will represent the graph to test your algorithm.
# --hints--
You should rename your `copper` dictionary into `my_graph`.
```js
({ test: () => assert(__userGlobals.has("my_graph")) })
```
Your `my_graph` variable should be a dictionary.
```js
({ test: () => assert(__pyodide.runPython(`
my_graph = __locals.get("my_graph")
type(my_graph) is dict
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
copper = {
'species': 'guinea pig',
'age': 2
}
--fcc-editable-region--
```