Today’s problem has guards walking around a lab we need to investigate.
The guards (automata) walk around following tight training (rules).
It is our job to study and predict their paths.
Today’s part 2 kept me up til 4a.m. so I hope you like the read.
As per use you can find the solutions discussed below on my github.
Part A
The Problem
As mentioned above, the is a lab with a guard in it.
This guard follows a predictable set of rules:
If facing a wall, turn clockwise 90 degrees
else step forward one space in the currently faced direction.
This will be followed until the guard leaves the map.
With this established, it would useful to know all the uniquely occupied squares of the guard.
The Solution
The plan
Today’s problem is one with a few moving parts.
The map and it’s walls along with the guards position and direction need to be maintained just to represent the world state.
Further accommodations are required to track cycles and such.
This will give us excuse to use some of Rust’s cool features.
Our plan here is similar to the other days:
Parse in the map
run the automata until it is done
count up the visited squares
return
This article is written after I’ve done both parts, so I can’t walk through all the predesign decisions in the sincerity of ignorance.
But an addition I made to part A that wasn’t necessary was cycle detection.
This was done to catch test cases where the guard doesn’t leave the lab (which turned out to never happen).
As an aside, it’s worth thinking of how this problem could grow or change.
Since it is an automata, the rules could easily change, so keeping them loosely coupled and extendible is a must have.
Similarly, adding more guards is always a possibility, so it is good to keep your logic for handling the automata cordoned into reproducible units.
The processing of the walked path could change; so keeping a record of where you’ve been will always be useful.
I digress.
Rust is cool
I said above that this problem leverages some cool parts of Rust.
Rust is one of my favorite languages right now.
Far and away from the usual safety concerns that the pundit hock, Rust is full of idioms that I find myself needing in other languages.
If I could take one thing from Rust into any other language, it would be the monadic return types that shoe horn railway programming.
But if I could take two things; Rust enums would get to come too.
Enums in the C-likes are really just sugar over integer values.
In Rust, they are rich, fast, zero-cost abstractions for representing state.
In our problem here, we need to represent direction.
So then what better way to do that then with the 4 cardinal directions.
That is nice and better than using a char, but it’s not cool enough to be a sub sub heading in a blog post that 2 people might read.
It gets cool when you tie functionality to the enum.
Rust’s data model is based on data and associated functionality.
C++/C# envelope the functionality in the data with classes.
However, C classically doesn’t do this, however C sucks to type out.
Rust, like C, maintains this separation with an ergonomic boost.
The data in Rust is a struct, enum, tuple; the functionality is shown in impl blocks.
Ever close to digression, here is the example for our enum above.
So, this saves us abunch of ugly if statements of direction_to_delta() functions.
The implementation
The main work for this was in the function that advanced the automaton.
The basic flow is:
get current position and direction
try to advance
on fail turn
on success move
exit on loop or exit of map
goto top
How this shook out, you can see in the code below.
Cycle detection will be discussed more in part B.
The Full Solution
This is the entire solution.
The boiler plate for this one was a bit more chunky that the others of late.
But once it was processed in, it wasn’t too bad.
Part B
The Twist
Finding where the guard will be isn’t enough; we want to make them loop for eternity.
The goal of part B is to find all unique locations where you can place a new wall s.t. the guard will loop indefinitely.
You’ll note that this wasn’t called out above as a design consideration; that’s because it wasn’t.
However, this isn’t that invasive of a request.
It can be treated as a middle processing step.
How we Adapt
So, one insights from the jump: we only need to check for obstacles on the guards path.
This means we can run the automata like normal and check at each step if an inserted wall would cause a cycle.
That is this was solved.
At every step in the journey: a wall was inserted, the guard checked for a cycle, success is tracked.This worked like a charm for the sample input… and every test case I could come up with.
However, for the “real” input, it was no use.
The thing about the problem statement that I didn’t realize was that the obstacle is placed at t[0].
So, it will be there from the beginning of the guards journey.
This means, if you place a new wall at a place and you create a cycle but internal to the new structure it doesn’t count.
For example,
Here, the left is the initial map and the right is the false positive cycle.
The new wall is denoted with the 0.
As you can see, to the guard within the new structure a cycle is obvious.
However to the guard that will hit it from the outside, there will be no such cycle.
Here, the fix is simple, we check all obstacles from the initial state.
With that, we are done!