I treated day 2 as an excercise of parsing the the input data to helpful data structures, which I then used to solve the puzzle. Each line of the puzzle has a password and a rule for validation and each rule has a character and a range of the expected amount of times that character should be present.
With this outline I modeled the types I would need. I tend to prefer stronger typing than weaker, so that I always know what I’m dealing with. I even went so far as to make a specific Password type that was in retrospect probably not truly required for today’s challenge.
In general though, I find compilations issues easier to reason about if there’s a type in them, and these can occur often while chaining many operations on Sequence. It’s just a lot simpler to know that you have a Password at any given point rather than a String, where it may be the password or the current line or even the puzzle input!
My parsing code is a simple series of inits which take the input (1-3 a: abcde) and split it down to the four component parts. Splitting on the colon to find the password, the space to find the character and the dash to find the lower and upper bounds of the range.
Once parsed into this data structure it’s simple to understand the solution using an extension to Rule to provide the validation.
Throughout the day, I was niggled by the thought that my simple string-splitting parsing might be improved upon using fancier techniques, specifically regular expressions.
I’ve dabbled in using regular expressions (regex) to check the validity of strings over the years, where by dabbled I mean I’ve searched the web endlessly for the correct mystical incantations. It’s safe to say: I am a regex noob and find them ever so slightly intimidating.
Paul Hudson’s really quick guide to pattern matching gave me my first clues and showed me that regex can be used to capture values and not just validate inputs. Inside a regular expression, you can use parentheses () to create a capturing group, which can be used to extract information from the input string.
Foundation has NSRegularExpression, which has the power to do all of this work, but the API is slightly cumbersome. Wrapping it using a custom type made it a little nicer and succint to work with. The following is the total solution for part 1 using regular expressions.
The defined regular expression (\d+)-(\d+) (.): (.+) captures four groups:
1 or more numbers
1 or more numbers
Any single character
1 or more characters
These translate into our lower and upper bounds, the search character and the passord respecively. Once we have these pieces of information, we can simply see if the number of occurances of the character in the password is within the range that is required.
I now have two solutions to compare. A more verbose, typed solution which explicity and clearly states what the code is doing and a more succinct piece which you can read and reason about in just a couple of lines. Performance-wise, the two are indistinguishable from one another, which is unsurprising given the lengths of the strings involved and that both are doing similar amounts of work.
This regular expression variant does have a singular benefit that because I didn’t have types set up, I could rename my variables for the second part. For instance the two numeric values are no longer the lower and upper bound of a range but two indices in the password – in my original solution, I just brutalised the use of the closed range to get at the values after the data had been parsed.
I’m going to keep the regular expression code in as they may prove useful to have in my toolbelt for other puzzles.