Outside Context Problem
Many-Worlds Interpretation

I have coffee and have refamiliarised myself with my own code. Let's continue...

So from phase 1 of the implementation, we now have a list of distances between the significant points on the map. I have these stored in a dictionary.

Wz: 5
Yd: 7
Pr: 7
@e: 18
fx: 20
ay: 22
JK: 16
...

Remember, uppercase letters are doors, lowercase letters are keys and the @ symbol is the starting point in the maze. There are 1378 entries in the dictionary, the number of combinations of 2 of 53 (a-z, A-Z, @) characters. The number is simply the number of steps between the items in any orthogonal direction (up, down, left, right). So, in the above example, there are 5 steps between the door W and the key z.

I also have a dictionary of doors between points:

Bz: W
iu: N
Zm: K, J
Hr: P, V, I
...

So, in the above example, to get to door H from key r (or vice-versa), you need to pass through doors P, V and I.

I pass both of these into a Graph class which, unsurprisingly, constructs a graph of connected nodes from the first dictionary. At this point it just stores a reference to the doors dictionary, it is not included in the node graph.

This Graph class is passed into a GraphSolver class. This class maintains a queue of INodeWalkers which, like the bots in the discovery phase, start at a point and split when reaching a junction. In this case though, we start with one at the maze start point. The queue is a PriorityQueue so nodes can be added with their distance as the priority, and shorter ones will be evaluated first.

You may have noticed, this is essentially Dijkstra's Algorithm again to some extent. The reason for the extra layers of abstraction will become apparent when I cover part 2 of this puzzle.

Given that the priority queue evaluates shortest distances first, as soon as an INodeWalker has visited every key, we can return the number of steps required and answer the puzzle. My implementation also returns the path taken, to make the visualisation possible.

For part 1 of this puzzle, the INodeWalker implementation is (drum-roll) NodeWalker. As mentioned, this is essentially like the discovery bots from phase 1, but walks the graph constructed by the Graph class. It contains some additional logic to check whether a door that hasn't been unlocked exists between two nodes, but that's pretty much it.

I hope I've explained my approach in an understandable manner. I think the code is quite clean and class responsibilities are well delineated. Feel free to provide any constructive feedback.