Thuta Learning
ProjectsProgrammingintermediate

Project: Pathfinding Over a Lesson Graph

What you'll walk away with

  • Explain the core ideas behind Project: Pathfinding Over a Lesson Graph
  • Run the sample Python code and verify its output
  • Apply the technique correctly to the Tutorial Platform and production scenarios

Build the mental model

Related-lessons data naturally forms a graph: each lesson is a node, and an edge to another lesson means 'this leads naturally to that,' possibly weighted by difficulty jump or estimated time. Finding the best route from a learner's current lesson to a goal is a shortest-path problem, and Dijkstra's algorithm is the right tool because edge weights are non-negative (you never have a 'negative cost' lesson transition) and you need the true minimum-cost path, not just any path. The naive alternative — plain breadth-first search — only works if every edge has equal weight; the moment lessons differ in cost (a big conceptual leap costs more than a small refinement), BFS's 'fewest edges' answer can be wrong, picking a path with fewer hops but a higher real cost. Dijkstra fixes this by always expanding the frontier node with the lowest known total cost first, using a priority queue (`heapq`) so it never has to re-examine settled nodes, giving the mathematically shortest weighted path in O((V+E) log V).

Connect it to a real scenario

This models a real 'recommended learning path' feature: a learner finishing an intro Python lesson wants to reach 'Building REST APIs,' and the platform needs to suggest the shortest sequence of prerequisite lessons connecting the two, rather than dumping the entire catalog on them or listing lessons in an arbitrary order. Each edge weight could represent estimated minutes-to-complete or a difficulty delta, so the shortest path is genuinely the fastest reasonable route through the curriculum, not just the fewest clicks. The same graph could also drive the 'Related Lessons' sidebar already implied by the site's per-tutorial chapter structure, ranking suggestions by path cost from the lesson currently open.

Try the working example

python
import heapq

def shortest_path(graph, start, goal):
    """graph: {node: [(neighbor, cost), ...]}. Returns (path, total_cost)."""
    # (cumulative_cost, node, path_so_far)
    frontier = [(0, start, [start])]
    visited = set()

    while frontier:
        cost, node, path = heapq.heappop(frontier)  # always pop lowest cost
        if node == goal:
            return path, cost
        if node in visited:
            continue
        visited.add(node)

        for neighbor, edge_cost in graph.get(node, []):
            if neighbor not in visited:
                heapq.heappush(frontier, (cost + edge_cost, neighbor, path + [neighbor]))

    return None, float("inf")  # goal unreachable

# Small "related lessons" graph: edge weight = estimated extra minutes needed
lesson_graph = {
    "python-basics": [("python-functions", 10), ("python-oop", 25)],
    "python-functions": [("python-oop", 12), ("python-decorators", 15)],
    "python-oop": [("rest-apis", 20)],
    "python-decorators": [("rest-apis", 8)],
    "rest-apis": [],
}

path, cost = shortest_path(lesson_graph, "python-basics", "rest-apis")
print("Path:", " -> ".join(path))
print("Total estimated minutes:", cost)
You should see
Prints `Path: python-basics -> python-functions -> python-decorators -> rest-apis` and `Total estimated minutes: 33`, the cheaper route versus going through `python-oop`.

5-minute try-it

Extend the graph so edge costs adjust dynamically based on a learner's mastery of prerequisite topics — lower the cost of an edge if the learner already knows that topic.

One important caution

Checking `visited` only when popping instead of also skipping already-visited neighbors before pushing lets many stale, higher-cost entries pile up in the heap — doesn't break correctness but wastes memory and time on large graphs

Adding a negative edge weight (e.g. a '-5 minute skip credit' for a review lesson) silently violates Dijkstra's non-negative-weight assumption and can produce a wrong shortest path with no error raised — negative weights require Bellman-Ford instead

Wikipedia — A* search algorithmData Structures & Algorithms

Easy traps

  • Checking `visited` only when popping instead of also skipping already-visited neighbors before pushing lets many stale, higher-cost entries pile up in the heap — doesn't break correctness but wastes memory and time on large graphs
  • Adding a negative edge weight (e.g. a '-5 minute skip credit' for a review lesson) silently violates Dijkstra's non-negative-weight assumption and can produce a wrong shortest path with no error raised — negative weights require Bellman-Ford instead
  • Validate sample code in a local or test environment before applying it to a production system.

Exercise

Extend the graph so edge costs adjust dynamically based on a learner's mastery of prerequisite topics — lower the cost of an edge if the learner already knows that topic.

You'll know it worked when: Prints `Path: python-basics -> python-functions -> python-decorators -> rest-apis` and `Total estimated minutes: 33`, the cheaper route versus going through `python-oop`.

Project: Pathfinding Over a Lesson Graph | Thuta Learning