Now that we have our Graph data type, and that we have a function to create it from a textual description, it is time to create the traversal function.

(Note: in the preceding section, I split read_graph into three separate, logical chunks. I have decided not to split the bfs function in this section. I want to see how chunk vs. no-chunk affects clarity and comprehension; if you have comments about using chunks, do let me know.)

The bfs function has three logical phases.

Initialization

In this phase, we create two data structures: a queue that contains the nodes to visit (a BFS traversal uses a FIFO structure to determine the next node to visit while a DFS traversal uses a LIFO structure), and a map to keep track of the nodes visited so far and their parent. A parent is represented with an Option type. The start node has no parent and so we represent its lack of a parent in the map with the value None . The parent of the other nodes is Some((x, y)) . We begin the procedure, by putting the start node in the queue, and marking it as visited.

Traversal

Our next step is to traverse the whole graph. We iterate until the queue of nodes left to process is empty. We pop the oldest node from the queue and look at its neighbors. If a neighbor has already been visited, we skip it; if a neighbor has yet to be visited, we add it to the queue and record its parent in the map. We could terminate the traversal early by breaking from the loop when we hit the end node, but to keep the code shorter, we have omitted this small optimization.

Path finding

Once the parent map has been populated, finding the path from beginning to end is simple. Starting with the end node, we use the map to find its parent, and then find that parent’s parent, and so on, until we reach the start node. All the while, we push the coordinates of the nodes inside a vector. After the loop, the vector contains the coordinates, but from end to beginning; we reverse the path, and return it.