From c7a4138c181c8526d764ac03cb4aca6dd04c6932 Mon Sep 17 00:00:00 2001 From: Hillary Nyakundi <63947040+larymak@users.noreply.github.com> Date: Thu, 6 Nov 2025 15:25:59 +0300 Subject: [PATCH] feat(curriculum): add shortest path algorithm workshop (#61147) Co-authored-by: Ilenia <26656284+ilenia-magoni@users.noreply.github.com> Co-authored-by: Dario <105294544+Dario-DC@users.noreply.github.com> Co-authored-by: Ilenia M --- client/i18n/locales/english/intro.json | 6 +- .../workshop-shortest-path-algorithm/index.md | 8 + .../6853e58d52ec64349a5760ca.md | 50 ++++++ .../686251fb49ebc489ba60ccb5.md | 58 +++++++ .../686253f1b051998ad4904e3e.md | 62 ++++++++ .../68625ad34aa3f58d7f3b9b07.md | 50 ++++++ .../686b73007fc582db0132c361.md | 65 ++++++++ .../686b7848fbaa41e0c2a7efef.md | 51 ++++++ .../686b78bc13d518e1267448e4.md | 102 ++++++++++++ .../686b82af323098e7a5203bf4.md | 67 ++++++++ .../68760464eb9c2e79cc913fac.md | 117 ++++++++++++++ .../68760671d950767b055bf2f9.md | 78 +++++++++ .../6876089f7a500b7c782e5b83.md | 81 ++++++++++ .../68760b53e250f982473e1808.md | 75 +++++++++ .../68760c4c2648e7832c5eef16.md | 76 +++++++++ .../687774e17e0972345de2527a.md | 67 ++++++++ .../687775bed85144353d298665.md | 115 ++++++++++++++ .../68777816e95d4936f129819f.md | 101 ++++++++++++ .../687b3926419dec576850b814.md | 85 ++++++++++ .../687b3c7a3af0df59b363712e.md | 75 +++++++++ .../687b3ef0c2fe185b2abc4654.md | 119 ++++++++++++++ .../687b426c3dabfe5cf80fad80.md | 76 +++++++++ .../687b43c0e20d695e0b8cca5b.md | 118 ++++++++++++++ .../687b45e16f38ad5f4e4bd799.md | 148 ++++++++++++++++++ .../687b48ccb46c5e6126d30bc6.md | 94 +++++++++++ .../687b499774fc9061b32e13cd.md | 93 +++++++++++ .../687b4bd2e4c7c962ec08310c.md | 136 ++++++++++++++++ .../68e4c0ff9edcc92771d7549a.md | 53 +++++++ .../69046179fba35c03dec84fb6.md | 103 ++++++++++++ .../workshop-shortest-path-algorithm.json | 39 +++++ .../superblocks/full-stack-developer.json | 1 + 31 files changed, 2367 insertions(+), 2 deletions(-) create mode 100644 client/src/pages/learn/full-stack-developer/workshop-shortest-path-algorithm/index.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/6853e58d52ec64349a5760ca.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686251fb49ebc489ba60ccb5.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686253f1b051998ad4904e3e.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68625ad34aa3f58d7f3b9b07.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b73007fc582db0132c361.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b7848fbaa41e0c2a7efef.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b78bc13d518e1267448e4.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b82af323098e7a5203bf4.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760464eb9c2e79cc913fac.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760671d950767b055bf2f9.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/6876089f7a500b7c782e5b83.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760b53e250f982473e1808.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760c4c2648e7832c5eef16.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687774e17e0972345de2527a.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687775bed85144353d298665.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68777816e95d4936f129819f.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3926419dec576850b814.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3c7a3af0df59b363712e.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3ef0c2fe185b2abc4654.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b426c3dabfe5cf80fad80.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b43c0e20d695e0b8cca5b.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b45e16f38ad5f4e4bd799.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b48ccb46c5e6126d30bc6.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b499774fc9061b32e13cd.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b4bd2e4c7c962ec08310c.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68e4c0ff9edcc92771d7549a.md create mode 100644 curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/69046179fba35c03dec84fb6.md create mode 100644 curriculum/structure/blocks/workshop-shortest-path-algorithm.json diff --git a/client/i18n/locales/english/intro.json b/client/i18n/locales/english/intro.json index 1b8b99cad9c..ffe2d34c2d6 100644 --- a/client/i18n/locales/english/intro.json +++ b/client/i18n/locales/english/intro.json @@ -4787,8 +4787,10 @@ ] }, "workshop-shortest-path-algorithm": { - "title": "Build a Shortest Path Algorithm", - "intro": [""] + "title": "Implement the Shortest Path Algorithm", + "intro": [ + "In this workshop you will implement the shortest path algorithm to find the shortest path between two nodes in a graph." + ] }, "lab-adjacency-list-to-matrix-converter": { "title": "Build an Adjacency List to Matrix Converter", diff --git a/client/src/pages/learn/full-stack-developer/workshop-shortest-path-algorithm/index.md b/client/src/pages/learn/full-stack-developer/workshop-shortest-path-algorithm/index.md new file mode 100644 index 00000000000..e011f21f315 --- /dev/null +++ b/client/src/pages/learn/full-stack-developer/workshop-shortest-path-algorithm/index.md @@ -0,0 +1,8 @@ +--- +title: Introduction to the Implement the Shortest Path Algorithm +block: workshop-shortest-path-algorithm +--- + +## Introduction to the Implement the Shortest Path Algorithm + +In this workshop, you will learn how to implement the shortest path algorithm using Dijkstra's algorithm to find the shortest path between two nodes in a graph. diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/6853e58d52ec64349a5760ca.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/6853e58d52ec64349a5760ca.md new file mode 100644 index 00000000000..f4b0bcfd100 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/6853e58d52ec64349a5760ca.md @@ -0,0 +1,50 @@ +--- +id: 6853e58d52ec64349a5760ca +title: Step 1 +challengeType: 20 +dashedName: step-1 +--- + +# --description-- + +In this workshop, you will implement the shortest path algorithm. You will write a Python function that computes the shortest path between the nodes in a graph, and also returns the path taken. + +For example, given a graph where cities are connected by roads with different distances, the algorithm will find the shortest route from one city to another. If you want to travel from City A to City D, the algorithm might find that going A ⇨ B ⇨ C ⇨ D (total: 15km) is shorter than going directly A ⇨ D (20km). + +To get started, define a variable named `INF` and assign it the value `float('inf')`, which represents positive infinity. Later, you'll use it to indicate an infinite distance between two nodes. + +# --hints-- + +You should define the variable `INF` at the top of your file. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).has_variable("INF")`)); + } +}); +``` + +You should assign `float('inf')` to the variable `INF`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_variable("INF").is_equivalent("INF = float('inf')")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686251fb49ebc489ba60ccb5.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686251fb49ebc489ba60ccb5.md new file mode 100644 index 00000000000..7bf3fbb8d63 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686251fb49ebc489ba60ccb5.md @@ -0,0 +1,58 @@ +--- +id: 686251fb49ebc489ba60ccb5 +title: Step 2 +challengeType: 20 +dashedName: step-2 +--- + +# --description-- + +You will need to create a 2D list to represent the adjacency matrix of the graph. This matrix will be used to represent the weights of the edges between nodes in the graph. + +Create a variable named `adj_matrix` and assign it a 2D list representing the graph with the following weights: + +```py +[0, 5, 3, INF, 11, INF], +[5, 0, 1, INF, INF, 2], +[3, 1, 0, 1, 5, INF], +[INF, INF, 1, 0, 9, 3], +[11, INF, 5, 9, 0, INF], +[INF, 2, INF, 3, INF, 0] +``` + +# --hints-- + +You should create a variable named `adj_matrix`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).has_variable("adj_matrix")`)); + } +}); +``` + +`adj_matrix` should be a 2D list containing the provided values. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_variable("adj_matrix").is_equivalent("adj_matrix = [[0, 5, 3, INF, 11, INF], [5, 0, 1, INF, INF, 2], [3, 1, 0, 1, 5, INF], [INF, INF, 1, 0, 9, 3], [11, INF, 5, 9, 0, INF], [INF, 2, INF, 3, INF, 0]]")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686253f1b051998ad4904e3e.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686253f1b051998ad4904e3e.md new file mode 100644 index 00000000000..9c92917778f --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686253f1b051998ad4904e3e.md @@ -0,0 +1,62 @@ +--- +id: 686253f1b051998ad4904e3e +title: Step 3 +challengeType: 20 +dashedName: step-3 +--- + +# --description-- + +You will now create the main function that accepts three parameters: the adjacency matrix, the starting node, and an optional target node. + +Create a function named `shortest_path` that takes in three parameters: `matrix`, `start_node`, and `target_node`. Assign `None` as the default value for `target_node`. + +The `target_node` parameter is set to `None` by default, indicating that if no target node is specified, the function should compute the shortest paths from the starting node to all other nodes in the graph. + +Add a `pass` statement inside the function body for now. + +# --hints-- + +You should create a function named `shortest_path`. + +```js +({ + test: () => { + assert(runPython(`_Node(_code).has_function("shortest_path")`)); + } +}); +``` + +The function should take in three parameters: `matrix`, `start_node`, and `target_node` with `target_node` having a default value of `None`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").has_args("matrix, start_node, target_node=None")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68625ad34aa3f58d7f3b9b07.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68625ad34aa3f58d7f3b9b07.md new file mode 100644 index 00000000000..0337b59edeb --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68625ad34aa3f58d7f3b9b07.md @@ -0,0 +1,50 @@ +--- +id: 68625ad34aa3f58d7f3b9b07 +title: Step 4 +challengeType: 20 +dashedName: step-4 +--- + +# --description-- + +You need to store the number of nodes in the graph. For this you will need a variable that matches the length of the adjacency matrix. + +Inside the `shortest_path` function, create a variable `n` and set it to the length of the `matrix`. + +# --hints-- + +You should initialize `n` to the length of the `matrix`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_variable("n").is_equivalent("n = len(matrix)")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +--fcc-editable-region-- +def shortest_path(matrix, start_node, target_node=None): + pass + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b73007fc582db0132c361.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b73007fc582db0132c361.md new file mode 100644 index 00000000000..82b97c662f3 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b73007fc582db0132c361.md @@ -0,0 +1,65 @@ +--- +id: 686b73007fc582db0132c361 +title: Step 5 +challengeType: 20 +dashedName: step-5 +--- + +# --description-- + +You need to keep track of the shortest known distance from the start node to every other node in the graph. + +To do this, create a variable named `distances` and initialize it as a list containing a single element: `INF`. + + +# --hints-- + +You should create a variable named `distances`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").has_variable("distances")` + ) + ); + } +}); +``` + +The `distances` list should be initialized to `[INF]`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_variable("distances").is_equivalent("distances = [INF]")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +--fcc-editable-region-- +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b7848fbaa41e0c2a7efef.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b7848fbaa41e0c2a7efef.md new file mode 100644 index 00000000000..7eef7f976c8 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b7848fbaa41e0c2a7efef.md @@ -0,0 +1,51 @@ +--- +id: 686b7848fbaa41e0c2a7efef +title: Step 7 +challengeType: 20 +dashedName: step-7 +--- + +# --description-- + +Now that you have your `distances` list initialized, you need to update the distance for the starting node. + +Since the distance from the starting node to itself is always `0`, set the value at the `start_node` index in the `distances` list to `0`. + +# --hints-- + +You should set `distances[start_node]` to `0`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").has_stmt("distances[start_node] = 0")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +--fcc-editable-region-- +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b78bc13d518e1267448e4.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b78bc13d518e1267448e4.md new file mode 100644 index 00000000000..9f90f52fc4d --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b78bc13d518e1267448e4.md @@ -0,0 +1,102 @@ +--- +id: 686b78bc13d518e1267448e4 +title: Step 8 +challengeType: 20 +dashedName: step-8 +--- + +# --description-- + +In addition to tracking distances, you also need to keep track of the actual paths taken to reach each node. + +You'll create a list where each entry stores the path taken to reach that node. Initially, each node's path will just contain itself. + +List comprehensions provide a concise way to create lists. For example: + +```py +[x * 2 for x in range(3)] +``` + +Create a variable named `paths` and initialize it using a list comprehension that creates a list containing `[node_no]` for each `node_no` in `range(n)`. + +# --hints-- + +You should have a variable called `paths`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").has_variable("paths")` + ) + ); + } +}); +``` + +You should initialize `paths` using a list comprehension. + +```js +({ + test: () => { + runPython(` +import ast +assert isinstance(_Node(_code).find_function("shortest_path").find_variable("paths").tree.value, ast.ListComp) + `); + } +}); +``` + +Your list comprehension should use `node_no` to iterate over `range(n)`. + +```js +({ + test: () => { + runPython(` +v = _Node(_code).find_function("shortest_path").find_variable("paths") +assert len(v.find_comp_targets()) == 1 +assert v.find_comp_targets()[0].is_equivalent("node_no") +assert len(v.find_comp_iters()) == 1 +assert v.find_comp_iters()[0].is_equivalent("range(n)") + `); + } +}); +``` + +Your list comprehension should evaluate `[node_no]` for each `node_no` in `range(n)`. + +```js +({ + test: () => { + runPython(` +v = _Node(_code).find_function("shortest_path").find_variable("paths") +assert v.find_comp_expr().is_equivalent("[node_no]") + `); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +--fcc-editable-region-- +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b82af323098e7a5203bf4.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b82af323098e7a5203bf4.md new file mode 100644 index 00000000000..2eab0cf4ed4 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/686b82af323098e7a5203bf4.md @@ -0,0 +1,67 @@ +--- +id: 686b82af323098e7a5203bf4 +title: Step 9 +challengeType: 20 +dashedName: step-9 +--- + +# --description-- + +As the algorithm runs, you need to keep track of which nodes you've already visited, so you don't process them more than once. + +To do this, create a list named `visited` and initialize it with `False` for every node. + +# --hints-- + +You should have a list named `visited` inside the `shortest_path` function. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").has_variable("visited")` + ) + ); + } +}); +``` + +You should initialize the `visited` list with `False` for every node using `[False] * n`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_variable("visited").is_equivalent("visited = [False] * n")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +--fcc-editable-region-- +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760464eb9c2e79cc913fac.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760464eb9c2e79cc913fac.md new file mode 100644 index 00000000000..5b6245fe1e7 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760464eb9c2e79cc913fac.md @@ -0,0 +1,117 @@ +--- +id: 68760464eb9c2e79cc913fac +title: Step 10 +challengeType: 20 +dashedName: step-10 +--- + +# --description-- + +In this step, you will add a loop that will run once for each node in the graph. This loop will allow the algorithm to update distances and paths over multiple passes. + +Create a `for` loop that runs `n` times. Use `_` as the loop variable since you don't need to use the iteration value. + +Inside the loop, you need to prepare for selecting the next node to process by creating two variables: + +- one to hold the smallest distance found so far in the current iteration +- and another to store the index of the node that has this smallest distance. + +Create variables `min_distance` and `current`, and set them to `INF` and `-1`, respectively. + +# --hints-- + +You should have a `for` loop within the `shortest_path` function. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0]` + ) + ); + } +}); +``` + +Your `for` loop should iterate over `range(n)`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0].find_for_iter().is_equivalent("range(n)")` + ) + ); + } +}); +``` + +Your for loop should use `_` as the iteration variable. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0].find_for_vars().is_equivalent("_")` + ) + ); + } +}); +``` + +You should create a variable named `min_distance` and set it to `INF` inside the for loop. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].has_stmt("min_distance = INF")` + ) + ); + } +}); +``` + +You should create a variable named `current` and set it to `-1` inside the for loop. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].has_stmt("current = -1")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760671d950767b055bf2f9.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760671d950767b055bf2f9.md new file mode 100644 index 00000000000..e006b293019 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760671d950767b055bf2f9.md @@ -0,0 +1,78 @@ +--- +id: 68760671d950767b055bf2f9 +title: Step 11 +challengeType: 20 +dashedName: step-11 +--- + +# --description-- + +Now you need to check every node to find the one with the smallest known distance that has not been visited yet. + +To do this, add a `for` loop inside the main loop. + +The loop should iterate through each `node_no` in `range(n)`, where `n` is the number of nodes in the graph. Add `pass` as the body of the loop for now. + +# --hints-- + +You should have a second `for` loop within the `shortest_path` function. + +```js +({ + test: () => { + assert( + runPython(` +func = _Node(_code).find_function("shortest_path") +outer_loop = func.find_for_loops()[0] +len(outer_loop.find_bodies()[0].find_for_loops()) >= 1 + `) + ); + } +}); +``` + +Your `for` loop should iterate over `range(n)` with `node_no` as the iteration variable. + +```js +({ + test: () => { + runPython(` +func = _Node(_code).find_function("shortest_path") +outer_loop = func.find_for_loops()[0] +inner_loop = outer_loop.find_bodies()[0].find_for_loops()[0] +assert inner_loop.find_for_vars().is_equivalent("node_no") +assert inner_loop.find_for_iter().is_equivalent("range(n)") + `); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + +--fcc-editable-region-- + for _ in range(n): + min_distance = INF + current = -1 + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/6876089f7a500b7c782e5b83.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/6876089f7a500b7c782e5b83.md new file mode 100644 index 00000000000..bf3ad72ef3a --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/6876089f7a500b7c782e5b83.md @@ -0,0 +1,81 @@ +--- +id: 6876089f7a500b7c782e5b83 +title: Step 12 +challengeType: 20 +dashedName: step-12 +--- + +# --description-- + +You need to decide whether the current node is a better choice than the one you've already found (if any). To do this, you'll add a conditional statement inside the loop. + +The condition should do two things: + +- Check if the node has not been visited yet. +- Compare the known distance to the current `min_distance`. + +Inside the inner `for` loop, add an `if` statement that checks whether `node_no` has not been visited **and** whether `distances[node_no]` is less than `min_distance`. + +Add `pass` as a placeholder inside the conditional block. + +# --hints-- + +You should add an `if` statement inside your inner `for` loop. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[0].find_bodies()[0].find_ifs()[0]` + ) + ); + } +}); +``` + +Your `if` statement should check if the node is unvisited (`visited[node_no]` is falsy) and `distances[node_no]` is less than `min_distance`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[0].find_bodies()[0].find_ifs()[0].find_conditions()[0].is_equivalent("not visited[node_no] and distances[node_no] < min_distance")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + +--fcc-editable-region-- + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + pass + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760b53e250f982473e1808.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760b53e250f982473e1808.md new file mode 100644 index 00000000000..684f80ee08d --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760b53e250f982473e1808.md @@ -0,0 +1,75 @@ +--- +id: 68760b53e250f982473e1808 +title: Step 13 +challengeType: 20 +dashedName: step-13 +--- + +# --description-- + +If the conditional you just added is true, that means the current node is the best unvisited option you've found so far and you need to update your variables to reflect that. + +Inside the `if` block, update `min_distance` to `distances[node_no]` and set `current` to `node_no`. + +# --hints-- + +You should update the `min_distance` variable to `distances[node_no]`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[0].find_bodies()[0].find_ifs()[0].find_bodies()[0].has_stmt("min_distance = distances[node_no]")` + ) + ); + } +}); +``` + +You should set `current` to `node_no`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[0].find_bodies()[0].find_ifs()[0].find_bodies()[0].has_stmt("current = node_no")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + +--fcc-editable-region-- + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + pass + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760c4c2648e7832c5eef16.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760c4c2648e7832c5eef16.md new file mode 100644 index 00000000000..b8b5499a66f --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68760c4c2648e7832c5eef16.md @@ -0,0 +1,76 @@ +--- +id: 68760c4c2648e7832c5eef16 +title: Step 14 +challengeType: 20 +dashedName: step-14 +--- + +# --description-- + +After the loop that finds the nearest unvisited node, you need to check whether a valid node was actually found. + +If no such node exists, that means the remaining nodes are unreachable from the start node, and the algorithm should stop early. + +On the same level as the nested `for` loop, add an `if` statement that checks if `current == -1` and breaks out of the loop if true. + +# --hints-- + +You should have an `if` statement that checks if `current` is still `-1` after the inner `for` loop. + +```js +({ + test: () => { + runPython(` + cond = _Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_ifs()[0].find_conditions()[0] + assert cond.is_equivalent("current == -1") or cond.is_equivalent("-1 == current") + `); + } +}); +``` + +You should use a `break` statement to break out of the loop. + +```js +({ + test: () => { + runPython(` + assert _Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_ifs()[0].find_bodies()[0].has_stmt("break") + `); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687774e17e0972345de2527a.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687774e17e0972345de2527a.md new file mode 100644 index 00000000000..0a26fca66e4 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687774e17e0972345de2527a.md @@ -0,0 +1,67 @@ +--- +id: 687774e17e0972345de2527a +title: Step 15 +challengeType: 20 +dashedName: step-15 +--- + +# --description-- + +If a valid node was found in the pass, you need to mark it as visited so it won't be considered again in future iterations. + +After the `if` statement you added in the previous step, set `visited[current]` to `True`. + +# --hints-- + +You should set `visited[current]` to `True` after your last `if` statement. + +```js +({ + test: () => { + runPython(` + block = _Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0] + if_stmt = """if current == -1: + break""" + target_stmt = "visited[current] = True" + assert block.is_ordered(if_stmt, target_stmt) + `); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + +--fcc-editable-region-- + if current == -1: + break + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687775bed85144353d298665.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687775bed85144353d298665.md new file mode 100644 index 00000000000..90c071ebcb9 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687775bed85144353d298665.md @@ -0,0 +1,115 @@ +--- +id: 687775bed85144353d298665 +title: Step 16 +challengeType: 20 +dashedName: step-16 +--- + +# --description-- + +Now that you've selected and marked a node as visited, it's time to look at all of its neighbors to see if you can find shorter paths to them. + +After the line `visited[current] = True`, add a `for` loop that iterates through `node_no` in `range(n)`. + +Inside this loop, create a variable `distance` and set it to `matrix[current][node_no]`. This will give you the distance from the current node to the neighbor node. + +# --hints-- + +You should add a `for` loop after marking the current node as visited. + +```js +({ + test: () => { + assert( + runPython(` +func = _Node(_code).find_function("shortest_path") +outer_loop = func.find_for_loops()[0] +len(outer_loop.find_bodies()[0].find_for_loops()) >= 2 + `) + ); + } +}); +``` + +The loop should iterate through `node_no` in `range(n)`. + +```js +({ + test: () => { + runPython(` +loop = _Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[1] +assert loop.find_for_vars().is_equivalent("node_no") +assert loop.find_for_iter().is_equivalent("range(n)") +`); + } +}); +``` + +You should create a variable `distance` inside the loop. + +```js +({ + test: () => { + assert( + runPython(` +func = _Node(_code).find_function("shortest_path") +outer_loop = func.find_for_loops()[0] +neighbor_loop = outer_loop.find_bodies()[0].find_for_loops()[1] +neighbor_loop.find_bodies()[0].has_variable("distance") + `) + ); + } +}); +``` + +The variable `distance` should be set to `matrix[current][node_no]`. + +```js +({ + test: () => { + assert( + runPython(`_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[1].find_bodies()[0].is_equivalent("distance = matrix[current][node_no]") + `) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68777816e95d4936f129819f.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68777816e95d4936f129819f.md new file mode 100644 index 00000000000..2d63a60d070 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68777816e95d4936f129819f.md @@ -0,0 +1,101 @@ +--- +id: 68777816e95d4936f129819f +title: Step 17 +challengeType: 20 +dashedName: step-17 +--- + +# --description-- + +Before trying to update the distance to a neighbor, you need to verify that the neighbor is both reachable and unvisited. Then you'll calculate what the total distance would be to reach that neighbor through the current node. + +Inside the `for` loop, add an `if` statement that checks: + +- The `distance` is not equal to `INF` (meaning there's an edge between the nodes) +- The neighbor `node_no` has not been visited yet + +Inside the `if` block, create a variable named `new_distance` and assign it the sum of `distances[current]` (the shortest distance to the current node) and `distance` (the distance from the current node to the neighbor). + +# --hints-- + +You should have an `if` statement that checks if `distance` is not equal to `INF` and if the neighbor node has not been visited. + +```js +({ + test: () => { + assert( + runPython(`_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[1].find_bodies()[0].find_ifs()[0].find_conditions()[0].is_equivalent("distance != INF and not visited[node_no]") + `) + ); + } +}); +``` + +You should create a variable named `new_distance` inside the `if` block. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[1].find_bodies()[0].find_ifs()[0].find_bodies()[0].has_variable("new_distance")` + ) + ); + } +}); +``` + +You should assign `new_distance` the sum of `distances[current]` and `distance`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[1].find_bodies()[0].find_ifs()[0].find_bodies()[0].find_variable("new_distance").is_equivalent("new_distance = distances[current] + distance")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + +--fcc-editable-region-- + for node_no in range(n): + distance = matrix[current][node_no] + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3926419dec576850b814.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3926419dec576850b814.md new file mode 100644 index 00000000000..ab9f4dd1f2a --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3926419dec576850b814.md @@ -0,0 +1,85 @@ +--- +id: 687b3926419dec576850b814 +title: Step 18 +challengeType: 20 +dashedName: step-18 +--- + +# --description-- + +Now that you've calculated the new possible distance to the neighbor, check if it's better than the one currently stored in the `distances` list. If it is, update the distance. + +Inside the existing `if` block, add an `if` statement that checks if `new_distance` is less than `distances[node_no]`. + +Inside this new conditional block, update `distances[node_no]` to store the `new_distance`. + +# --hints-- + +You should have a nested `if` statement that checks if `new_distance` is less than `distances[node_no]`. + +```js +({ + test: () => { + runPython(` + cond = _Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[1].find_bodies()[0].find_ifs()[0].find_bodies()[0].find_ifs()[0].find_conditions()[0] + assert cond.is_equivalent("new_distance < distances[node_no]") or cond.is_equivalent("distances[node_no] > new_distance") + `); + } +}); +``` + +You should update `distances[node_no]` to store the `new_distance`. + +```js +({ + test: () => { + assert( + runPython(`_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[1].find_bodies()[0].find_ifs()[0].find_bodies()[0].find_ifs()[0].find_bodies()[0].has_stmt("distances[node_no] = new_distance") + `) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + +--fcc-editable-region-- + for node_no in range(n): + distance = matrix[current][node_no] + if distance != INF and not visited[node_no]: + new_distance = distances[current] + distance + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3c7a3af0df59b363712e.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3c7a3af0df59b363712e.md new file mode 100644 index 00000000000..d1581e995f5 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3c7a3af0df59b363712e.md @@ -0,0 +1,75 @@ +--- +id: 687b3c7a3af0df59b363712e +title: Step 19 +challengeType: 20 +dashedName: step-19 +--- + +# --description-- + +When you find a shorter path to a node, you also need to update the actual path taken to reach it. + +Inside the same conditional block, update the `paths` list at the neighbor's index to reflect the new, shorter path. + +You should assign `paths[node_no]` to be the current path to the `current` node, with the `node_no` (the neighbor) added at the end. + +# --hints-- + +You should update `paths[node_no]` to be the current path plus the neighbor node. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_for_loops()[0].find_bodies()[0].find_for_loops()[1].find_bodies()[0].find_ifs()[0].find_bodies()[0].find_ifs()[0].find_bodies()[0].has_stmt("paths[node_no] = paths[current] + [node_no]")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + +--fcc-editable-region-- + for node_no in range(n): + distance = matrix[current][node_no] + if distance != INF and not visited[node_no]: + new_distance = distances[current] + distance + if new_distance < distances[node_no]: + distances[node_no] = new_distance + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3ef0c2fe185b2abc4654.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3ef0c2fe185b2abc4654.md new file mode 100644 index 00000000000..fe3af92929b --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b3ef0c2fe185b2abc4654.md @@ -0,0 +1,119 @@ +--- +id: 687b3ef0c2fe185b2abc4654 +title: Step 20 +challengeType: 20 +dashedName: step-20 +--- + +# --description-- + +Once the algorithm has finished running, you need to decide which node(s) to display results for. + +If a specific `target_node` was provided, you'll only show the distance and path to that node. Otherwise, you'll show results for all nodes. + +After the outer `for _ in range(n):` loop ends, create a variable named `targets`. Use a conditional expression to assign it `[target_node]` if `target_node` is not `None`, otherwise assign it `list(range(n))`. + +# --hints-- + +You should create a variable named `targets`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").has_variable("targets")` + ) + ); + } +}); +``` + +Your `targets` variable should contain a conditional expression. + +```js +({ + test: () => { + runPython(` + import ast + val =_Node(_code).find_function("shortest_path").find_variable("targets").tree.value + assert isinstance(val, ast.IfExp) + `); + } +}); +``` + +The conditional expression should check if `target_node is not None`. + +```js +({ + test: () => { + runPython(` + compare = _Node(_Node(_code).find_function("shortest_path").find_variable("targets").tree.value.test) + assert compare.is_equivalent("target_node is not None") + `); + } +}); +``` + +You should assign `[target_node]` when a target is provided and `range(n)` otherwise. + +```js +({ + test: () => { + runPython(` + import ast + val = _Node(_code).find_function("shortest_path").find_variable("targets").tree.value + assert _Node(val.body).is_equivalent("[target_node]") + assert _Node(val.orelse).is_equivalent("range(n)") + `); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + + for node_no in range(n): + distance = matrix[current][node_no] + if distance != INF and not visited[node_no]: + new_distance = distances[current] + distance + if new_distance < distances[node_no]: + distances[node_no] = new_distance + paths[node_no] = paths[current] + [node_no] + +--fcc-editable-region-- + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b426c3dabfe5cf80fad80.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b426c3dabfe5cf80fad80.md new file mode 100644 index 00000000000..fb6de21120e --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b426c3dabfe5cf80fad80.md @@ -0,0 +1,76 @@ +--- +id: 687b426c3dabfe5cf80fad80 +title: Step 21 +challengeType: 20 +dashedName: step-21 +--- + +# --description-- + +Now that you've defined which nodes you want to display results for, you need to loop through them. + +Add a `for` loop that iterates through each `node_no` in the `targets` list. Add `pass` as a placeholder inside the loop body. + +# --hints-- + +You should create a `for` loop that uses `node_no` to iterate over `targets`. + +```js +({ + test: () => { + runPython(` + loop = _Node(_code).find_function("shortest_path").find_for_loops()[1] + assert loop.find_for_iter().is_equivalent("targets") + assert loop.find_for_vars().is_equivalent("node_no") + `); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + + for node_no in range(n): + distance = matrix[current][node_no] + if distance != INF and not visited[node_no]: + new_distance = distances[current] + distance + if new_distance < distances[node_no]: + distances[node_no] = new_distance + paths[node_no] = paths[current] + [node_no] + +--fcc-editable-region-- + targets = [target_node] if target_node is not None else range(n) + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b43c0e20d695e0b8cca5b.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b43c0e20d695e0b8cca5b.md new file mode 100644 index 00000000000..9cdc011944c --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b43c0e20d695e0b8cca5b.md @@ -0,0 +1,118 @@ +--- +id: 687b43c0e20d695e0b8cca5b +title: Step 22 +challengeType: 20 +dashedName: step-22 +--- + +# --description-- + +At this point, you only want to display results for nodes that are not the start node and are reachable from it. + +Add a conditional that checks if `node_no` equals `start_node` **or** if `distances[node_no]` equals `INF`. + +If either condition is true, use `continue` to skip to the next iteration of the loop. + +# --hints-- + +You should add a conditional statement inside the loop. + +```js +({ + test: () => { + runPython(` +func = _Node(_code).find_function("shortest_path") +last_loop = func.find_for_loops()[-1] +assert last_loop.find_bodies()[0].find_ifs()[0] + `); + } +}); +``` + +Your `if` statement should check if `node_no` is equal to `start_node` *or* if `distances[node_no]` is equal to `INF`. + +```js +({ + test: () => { + runPython(` +func = _Node(_code).find_function("shortest_path") +if_cond = func.find_for_loops()[-1].find_bodies()[0].find_ifs()[0].find_conditions()[0] +conditions = [ + 'node_no == start_node or distances[node_no] == INF', + 'start_node == node_no or distances[node_no] == INF', + 'start_node == node_no or INF == distances[node_no]', + 'node_no == start_node or INF == distances[node_no]', + 'distances[node_no] == INF or node_no == start_node', + 'distances[node_no] == INF or start_node == node_no', + 'INF == distances[node_no] or start_node == node_no', + 'INF == distances[node_no] or node_no == start_node', +] +assert any(if_cond.is_equivalent(condition) for condition in conditions) + `); + } +}); +``` + +You should use `continue` to skip to the next iteration of the loop. + +```js +({ + test: () => { + runPython(` +func = _Node(_code).find_function("shortest_path") +if_stmt = func.find_for_loops()[-1].find_bodies()[0].find_ifs()[0].find_bodies()[0] +assert if_stmt.has_stmt("continue") + `); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + + for node_no in range(n): + distance = matrix[current][node_no] + if distance != INF and not visited[node_no]: + new_distance = distances[current] + distance + if new_distance < distances[node_no]: + distances[node_no] = new_distance + paths[node_no] = paths[current] + [node_no] + +--fcc-editable-region-- + targets = [target_node] if target_node is not None else range(n) + for node_no in targets: + pass + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b45e16f38ad5f4e4bd799.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b45e16f38ad5f4e4bd799.md new file mode 100644 index 00000000000..d6665a90713 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b45e16f38ad5f4e4bd799.md @@ -0,0 +1,148 @@ +--- +id: 687b45e16f38ad5f4e4bd799 +title: Step 23 +challengeType: 20 +dashedName: step-23 +--- + +# --description-- + +Now that you've determined a node should be displayed, you need to format its path so it can be printed clearly. For this you will use a generator expression. + +A generator expression is similar to a list comprehension, but instead of creating a list, it generates each value one at a time. It uses parentheses `()` instead of square brackets `[]`. For example: + +```py +numbers = [1, 2, 3] +squared = (x**2 for x in numbers) # Generator expression +``` + +Inside the loop after the `if` statement, create a variable called `string_path`. Assign it a generator expression that converts each node number in `paths[node_no]` to a string using `str()`. + +The generator expression should iterate over each node number `n` in `paths[node_no]`. + +# --hints-- + +You should create a variable called `string_path`. + +```js +({ + test: () => { + assert( + runPython(` +func = _Node(_code).find_function("shortest_path") +for_loop = func.find_for_loops()[-1] +for_loop.find_bodies()[0].has_variable("string_path") + `) + ); + } +}); +``` + +You should assign a generator expression to `string_path`. + +```js +({ + test: () => { + runPython(` + import ast + assert isinstance(_Node(_code).find_function("shortest_path").find_for_loops()[1].find_bodies()[0].find_variable("string_path").tree.value, ast.GeneratorExp) + `) + } +}) +``` + +Your generator expression should iterate over `paths[node_no]`. + +```js +({ + test: () => { + runPython(` +func = _Node(_code).find_function("shortest_path") +for_loop = func.find_for_loops()[-1] +path_var = for_loop.find_bodies()[0].find_variable("string_path") +assert path_var.find_comp_iters()[0].is_equivalent("paths[node_no]") + `); + } +}); +``` + +Your generator expression should use `n` as the iteration variable to iterate over `paths[node_no]`. + +```js +({ + test: () => { + runPython(` +func = _Node(_code).find_function("shortest_path") +for_loop = func.find_for_loops()[-1] +string_path_var = for_loop.find_bodies()[0].find_variable("string_path") +assert string_path_var.find_comp_targets()[0].is_equivalent("n") + `); + } +}); +``` + +Your generator expression should evaluate `str(n)` for each `n` in `paths[node_no]`. + +```js +({ + test: () => { + runPython(` +func = _Node(_code).find_function("shortest_path") +for_loop = func.find_for_loops()[-1] +string_path_var = for_loop.find_bodies()[0].find_variable("string_path") +assert string_path_var.find_comp_expr().is_equivalent("str(n)") + `); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + + for node_no in range(n): + distance = matrix[current][node_no] + if distance != INF and not visited[node_no]: + new_distance = distances[current] + distance + if new_distance < distances[node_no]: + distances[node_no] = new_distance + paths[node_no] = paths[current] + [node_no] + +--fcc-editable-region-- + targets = [target_node] if target_node is not None else range(n) + for node_no in targets: + if node_no == start_node or distances[node_no] == INF: + continue + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b48ccb46c5e6126d30bc6.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b48ccb46c5e6126d30bc6.md new file mode 100644 index 00000000000..6882692380b --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b48ccb46c5e6126d30bc6.md @@ -0,0 +1,94 @@ +--- +id: 687b48ccb46c5e6126d30bc6 +title: Step 25 +challengeType: 20 +dashedName: step-25 +--- + +# --description-- + +Finally, print the results for the current node. + +Your output should show two things, the distance from the `start_node` to `node_no` and the path taken to get there. + +Use an `f-string` to format the output to show both the distance and the full path using the format: + +```md +\n[starting node]-[node number] distance: [distance]\nPath: [path] +``` + +Where: + +- `[starting node]` is the `start_node` +- `[node number]` is the `node_no` +- `[distance]` is the distance from the `start_node` to `node_no` +- `[path]` is the path taken to get to `node_no` + +# --hints-- + +You should print the correct output using `print(f'\n{start_node}-{node_no} distance: {distances[node_no]}\nPath: {path}')`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function('shortest_path').find_for_loops()[1].has_call('print(f"\\\\n{start_node}-{node_no} distance: {distances[node_no]}\\\\nPath: {path}")')` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + + for node_no in range(n): + distance = matrix[current][node_no] + if distance != INF and not visited[node_no]: + new_distance = distances[current] + distance + if new_distance < distances[node_no]: + distances[node_no] = new_distance + paths[node_no] = paths[current] + [node_no] + +--fcc-editable-region-- + targets = [target_node] if target_node is not None else range(n) + for node_no in targets: + if node_no == start_node or distances[node_no] == INF: + continue + string_path = (str(n) for n in paths[node_no]) + path = ' -> '.join(string_path) + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b499774fc9061b32e13cd.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b499774fc9061b32e13cd.md new file mode 100644 index 00000000000..8c72fef45c2 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b499774fc9061b32e13cd.md @@ -0,0 +1,93 @@ +--- +id: 687b499774fc9061b32e13cd +title: Step 26 +challengeType: 20 +dashedName: step-26 +--- + +# --description-- + +At the end of your function, return the data you've computed so it can be used outside the function. + +Return both the list of shortest `distances` from the `start_node` to all other nodes and the list of `paths` that lead to each node + +# --hints-- + +You should have a `return` statement at the end of your function. + +```js +({ + test: () => { + runPython(`assert not _Node(_code).find_function("shortest_path").find_return().is_empty()` + ); + } +}); +``` + +You should return both `distances` and `paths`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_return().is_equivalent("return distances, paths")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + + for node_no in range(n): + distance = matrix[current][node_no] + if distance != INF and not visited[node_no]: + new_distance = distances[current] + distance + if new_distance < distances[node_no]: + distances[node_no] = new_distance + paths[node_no] = paths[current] + [node_no] + +--fcc-editable-region-- + targets = [target_node] if target_node is not None else range(n) + for node_no in targets: + if node_no == start_node or distances[node_no] == INF: + continue + string_path = (str(n) for n in paths[node_no]) + path = ' -> '.join(string_path) + print(f'\n{start_node}-{node_no} distance: {distances[node_no]}\nPath: {path}') + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b4bd2e4c7c962ec08310c.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b4bd2e4c7c962ec08310c.md new file mode 100644 index 00000000000..b565e2320ff --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/687b4bd2e4c7c962ec08310c.md @@ -0,0 +1,136 @@ +--- +id: 687b4bd2e4c7c962ec08310c +title: Step 27 +challengeType: 20 +dashedName: step-27 +--- + +# --description-- + +Now you will demonstrate that your function works. At the end of your program, call your function `shortest_path` with the `adj_matrix` and a `start_node` of `0` and a `target_node` of `5` as arguments. + +With that, the shortest path algorithm is complete! + +# --hints-- + +You should call the `shortest_path` function with three arguments: `adj_matrix`, `0`, and `5`. + +```js +({ + test: () => { + assert( + runPython(`_Node(_code).has_call('shortest_path(adj_matrix, 0, 5)')`) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + + for node_no in range(n): + distance = matrix[current][node_no] + if distance != INF and not visited[node_no]: + new_distance = distances[current] + distance + if new_distance < distances[node_no]: + distances[node_no] = new_distance + paths[node_no] = paths[current] + [node_no] + +--fcc-editable-region-- + targets = [target_node] if target_node is not None else range(n) + for node_no in targets: + if node_no == start_node or distances[node_no] == INF: + continue + string_path = (str(n) for n in paths[node_no]) + path = ' -> '.join(string_path) + print(f'\n{start_node}-{node_no} distance: {distances[node_no]}\nPath: {path}') + + return distances, paths + + +--fcc-editable-region-- +``` + +# --solutions-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + + for node_no in range(n): + distance = matrix[current][node_no] + if distance != INF and not visited[node_no]: + new_distance = distances[current] + distance + if new_distance < distances[node_no]: + distances[node_no] = new_distance + paths[node_no] = paths[current] + [node_no] + + targets = [target_node] if target_node is not None else range(n) + for node_no in targets: + if node_no == start_node or distances[node_no] == INF: + continue + string_path = (str(n) for n in paths[node_no]) + path = ' -> '.join(string_path) + print(f'\n{start_node}-{node_no} distance: {distances[node_no]}\nPath: {path}') + + return distances, paths + +shortest_path(adj_matrix, 0, 5) +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68e4c0ff9edcc92771d7549a.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68e4c0ff9edcc92771d7549a.md new file mode 100644 index 00000000000..7ccbfa12d03 --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/68e4c0ff9edcc92771d7549a.md @@ -0,0 +1,53 @@ +--- +id: 68e4c0ff9edcc92771d7549a +title: Step 6 +challengeType: 20 +dashedName: step-6 +--- + +# --description-- + +Right now, the `distances` list only has one element. However, you need one distance value for each node in the graph. + +In Python, you can multiply a list by an integer to repeat it. For example, `[0] * 3` creates `[0, 0, 0]`. + +Update the `distances` list to contain `n` copies of `INF` by multiplying `[INF]` by `n`. + +# --hints-- + +You should update `distances` to contain `n` copies of `INF`. + +```js +({ + test: () => { + assert( + runPython( + `_Node(_code).find_function("shortest_path").find_variable("distances").is_equivalent("distances = [INF] * n")` + ) + ); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +--fcc-editable-region-- +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] + +--fcc-editable-region-- +``` diff --git a/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/69046179fba35c03dec84fb6.md b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/69046179fba35c03dec84fb6.md new file mode 100644 index 00000000000..0a6a406671a --- /dev/null +++ b/curriculum/challenges/english/blocks/workshop-shortest-path-algorithm/69046179fba35c03dec84fb6.md @@ -0,0 +1,103 @@ +--- +id: 69046179fba35c03dec84fb6 +title: Step 24 +challengeType: 20 +dashedName: step-24 +--- + +# --description-- + +Now you'll use the `join()` method to combine the string representations of the node numbers into a single readable path. + +The `join()` method takes an iterable (like your generator expression) and combines all elements into one string, placing the separator between each element. For example: + +```py +numbers = ['1', '2', '3'] +route = ' -> '.join(numbers) # route will be '1 -> 2 -> 3' +``` + +Create a variable called `path` and assign it the result of joining `string_path` using `' -> '` as the separator. + +# --hints-- + +You should create a variable called `path`. + +```js +({ +test: () => { + assert( + runPython(` +func = _Node(_code).find_function("shortest_path") +for_loop = func.find_for_loops()[-1] +for_loop.find_bodies()[0].has_variable("path") + `) +); + } +}); +``` + +You use `join` to join the items in `string_path` with `' -> '` as the separator and assign the result to `path`. + +```js +({ + test: () => { + runPython(` +func = _Node(_code).find_function("shortest_path") +for_loop = func.find_for_loops()[-1] +assert for_loop.find_bodies()[0].find_variable("path").is_equivalent('path = " -> ".join(string_path)') + `); + } +}); +``` + +# --seed-- + +## --seed-contents-- + +```py +INF = float('inf') +adj_matrix = [ + [0, 5, 3, INF, 11, INF], + [5, 0, 1, INF, INF, 2], + [3, 1, 0, 1, 5, INF], + [INF, INF, 1, 0, 9, 3], + [11, INF, 5, 9, 0, INF], + [INF, 2, INF, 3, INF, 0], +] + +def shortest_path(matrix, start_node, target_node=None): + n = len(matrix) + distances = [INF] * n + distances[start_node] = 0 + paths = [[node_no] for node_no in range(n)] + visited = [False] * n + + for _ in range(n): + min_distance = INF + current = -1 + for node_no in range(n): + if not visited[node_no] and distances[node_no] < min_distance: + min_distance = distances[node_no] + current = node_no + + if current == -1: + break + visited[current] = True + + for node_no in range(n): + distance = matrix[current][node_no] + if distance != INF and not visited[node_no]: + new_distance = distances[current] + distance + if new_distance < distances[node_no]: + distances[node_no] = new_distance + paths[node_no] = paths[current] + [node_no] + +--fcc-editable-region-- + targets = [target_node] if target_node is not None else range(n) + for node_no in targets: + if node_no == start_node or distances[node_no] == INF: + continue + string_path = (str(n) for n in paths[node_no]) + +--fcc-editable-region-- +``` diff --git a/curriculum/structure/blocks/workshop-shortest-path-algorithm.json b/curriculum/structure/blocks/workshop-shortest-path-algorithm.json new file mode 100644 index 00000000000..d3301cf7bb1 --- /dev/null +++ b/curriculum/structure/blocks/workshop-shortest-path-algorithm.json @@ -0,0 +1,39 @@ +{ + "name": "Implement the Shortest Path Algorithm", + "blockLayout": "challenge-grid", + "blockLabel": "workshop", + "isUpcomingChange": true, + "dashedName": "workshop-shortest-path-algorithm", + "helpCategory": "Python", + "usesMultifileEditor": true, + "hasEditableBoundaries": true, + "challengeOrder": [ + { "id": "6853e58d52ec64349a5760ca", "title": "Step 1" }, + { "id": "686251fb49ebc489ba60ccb5", "title": "Step 2" }, + { "id": "686253f1b051998ad4904e3e", "title": "Step 3" }, + { "id": "68625ad34aa3f58d7f3b9b07", "title": "Step 4" }, + { "id": "686b73007fc582db0132c361", "title": "Step 5" }, + { "id": "68e4c0ff9edcc92771d7549a", "title": "Step 6" }, + { "id": "686b7848fbaa41e0c2a7efef", "title": "Step 7" }, + { "id": "686b78bc13d518e1267448e4", "title": "Step 8" }, + { "id": "686b82af323098e7a5203bf4", "title": "Step 9" }, + { "id": "68760464eb9c2e79cc913fac", "title": "Step 10" }, + { "id": "68760671d950767b055bf2f9", "title": "Step 11" }, + { "id": "6876089f7a500b7c782e5b83", "title": "Step 12" }, + { "id": "68760b53e250f982473e1808", "title": "Step 13" }, + { "id": "68760c4c2648e7832c5eef16", "title": "Step 14" }, + { "id": "687774e17e0972345de2527a", "title": "Step 15" }, + { "id": "687775bed85144353d298665", "title": "Step 16" }, + { "id": "68777816e95d4936f129819f", "title": "Step 17" }, + { "id": "687b3926419dec576850b814", "title": "Step 18" }, + { "id": "687b3c7a3af0df59b363712e", "title": "Step 19" }, + { "id": "687b3ef0c2fe185b2abc4654", "title": "Step 20" }, + { "id": "687b426c3dabfe5cf80fad80", "title": "Step 21" }, + { "id": "687b43c0e20d695e0b8cca5b", "title": "Step 22" }, + { "id": "687b45e16f38ad5f4e4bd799", "title": "Step 23" }, + { "id": "69046179fba35c03dec84fb6", "title": "Step 24" }, + { "id": "687b48ccb46c5e6126d30bc6", "title": "Step 25" }, + { "id": "687b499774fc9061b32e13cd", "title": "Step 26" }, + { "id": "687b4bd2e4c7c962ec08310c", "title": "Step 27" } + ] +} diff --git a/curriculum/structure/superblocks/full-stack-developer.json b/curriculum/structure/superblocks/full-stack-developer.json index 2c6ac82a434..172cd3f3f2f 100644 --- a/curriculum/structure/superblocks/full-stack-developer.json +++ b/curriculum/structure/superblocks/full-stack-developer.json @@ -784,6 +784,7 @@ "comingSoon": true, "blocks": [ "lecture-understanding-graphs-and-trees", + "workshop-shortest-path-algorithm", "lab-adjacency-list-to-matrix-converter", "workshop-breadth-first-search", "lab-depth-first-search",