Advent of Code F# – Day 1 to 6

In case you missed it, a lot of the good folks in the F# community has been doing the Advent of Code the past 2 weeks. I’m a bit late to the game, but have done a lot of catching up this weekend, and here’s my solutions for the day 1-6.

p.s. you can find all my solution on github, but the input is different for everyone.

 

Day 1

image

Starting off nice and simple, we can solve this problem with a fold.

image

The only caveat I encountered with this problem was in fact, trying to capture the input into a string value:

image

which for some reason seems to upset F# interactive:

image

Which is why I defined the input in a source file (day01.fs) whilst keeping the actual solution in the script (day01.fsx).

After you solve the question, the second part of the challenge is revealed (the same theme runs throughout all the challenges).

 

Day 1 (Part 2)

image

This requires a slight twist to our approach to part 1 – we need the intermediate steps of the fold instead of the final value.

Fortunately Seq.scan does just that!

image

Another thing to note is that, since Seq.scan returns the initial value (0 in this case) as part of the returned sequence, it means we didn’t have to shift our index system from 0-based to 1-based.

 

Day 2

image

First, bear in mind that the input looks like this:

20x3x11
15x27x5
6x29x7
30x15x9
19x29x21
10x4x15

so let’s add a helper to make splitting strings easier (and prettier):

image

image

 

Day 2 (Part 2)

image

To find the shortest distance around the smallest side, we just have to sort the dimensions then pick the first 2. So our solution only needs a small tweak from part 1:

image

 

Day 3

image

Starting with an implied position of (0, 0), in order to find the number of houses that received at least one present we need to find the number of distinct positions that Santa has traversed.

Again, fold to the rescue!

image

 

Day 3 (Part 2)

image

First, let’s extract our fold logic out from part 1:

image

Now, we need to massage the input (e.g. ^v^v^v^v^v) into two sequences, i.e.:

input : seq { ‘^’; ‘v’; ‘^’; ‘v’; ‘^’; ‘v’; ‘^’; ‘v’; ‘^’; ‘v’ }

we want :

seq {

seq { ‘^’; ‘^’; ‘^’; ‘^’; ‘^’ }  // for Santa

seq { ‘v’; ‘v’; ‘v’; ‘v’; ‘v’ }   // for Robot-Santa

}

which is what the first 4 LOC does:

image

so, to explain, we start with “^v^v^v^v” which is really just a sequence of chars:

seq { ‘^’; ‘v’; ‘^’; ‘v’; ‘^’; ‘v’; ‘^’; ‘v’; ‘^’; ‘v’ }

=> seq { (0, ‘^’); (1, ‘v’); (0, ‘^’); (1, ‘v’); … }

=> seq {

(0, seq { (0, ‘^’); (0, ‘^’); (0, ‘^’); … })

(1, seq { (1, ‘v’); (1, ‘v’); (1, ‘v’); … }) }

=> seq {

seq { ‘^’; ‘^’; ‘^’; … }

seq { ‘v’; ‘v’; ‘v’; … } }

after we’ve done that, we

  1. apply the same fold logic we did previously against each of the inner sequences;
  2. collect the result (i.e. the positions traversed) into a unified sequence
  3. count the distinct positions traversed by both Santa and Robot-Santa

and job done!

 

Day 4

image

In .Net you can use System.Security.Cryptography.MD5 to create a MD5 hash (as a byte[]) for some input byte[].

You can then use BitConverter.ToString to get back a string representation of the hex-decimal value for the hash.

Or, you can take a shortcut, and take advantage of binary-to-hex conversion where every 4 bits = 1 hex value:

image

 

Day 4 (Part 2)

image

Because of the approach we took for part 1, the change here becomes as trivial as checking if the 3rd byte is also 0uy!

image

 

Day 5

image

First, let’s capture the three requirements for a nice string.

At least 3 vowels, check.

image

At least 1 letter that appear twice in a row, check.

image

Does not contain certain strings, check (in a more generic way).

image

Then, let’s create a helper to make it easier for us to compose these three requirements later:

image

and finally let’s put everything together:

image

Voila!

aside : I think the <&&> operator makes the code easier to read here. I’m usually not a fan of special operators, I find they often gets in the way of discoverability and raises the barrier to entry for a library if there are too many special operators one has to remember. I think it’s ok in this case given that the scope is really small.

 

Day 5 (Part 2)

image

Again, let’s capture the 2 new requirements as functions.

At least one pair of letters that appear twice without overlap, check.

image

Here, I’m taking advantage of the fact that all the strings contains only alphabet characters and replacing the pair of characters currently being considered with ‘-’. However, it still feels as though I’m taking the scenic route to express the requirement here, if you have a better way, please let me know in the comments.

At least one letter that repeats with exactly one letter in between, check.

image

Like before, we just need to compose the two together to solve this challenge.

image

 

Day 6

image

First, let’s look at the input for this challenge.

turn on 489,959 through 759,964
turn off 820,516 through 871,914
turn off 427,423 through 929,502
toggle 756,965 through 812,992

so, we have coordinates – int, int – and three different actions – turn on, turn off, toggle – that we need to deal with:

image

For each line, the format is:

{action} {start_coordinate} through {end_coordinate}

so we can write a simple function to parse it:

image

After the first split, we’ll end up an array consisting of the two coordinates, e.g.

[| “ 489,959 ”; “ 759,964 ” |]

splitting each by ‘,’ again gives us an array of arrays, e.g.:

[|  [| “489”; “959” |];  [| “759”; “964” |]  |]

which is why we needed to further massage the inner arrays in to int tuples.

[| (489, 959); (759, 964) |]

 

Next, let’s have some lights!

image

And we also need a function to perform the parsed Action against the corresponding coordinate rages:

image

and then hook everything together:

image

 

Finally, let’s count how many of the lights are turned on after performing all the actions. Unfortunately, there’s no built-in sum, or sumBy functions in the Array2D module.. so for simplicity sake let’s just resort to using mutable state again.

image

 

Day 6 (Part 2)

image

We can reuse most of what we had done for part 1 of the challenge, the main thing that has to change is the perform function:

image

and also how we calculate the final result:

image

and that’s it!

 

From Day 7, the challenges start to get more challenging, so watch this space for more solutions to come.

Subscribe to my newsletter and get new contents delivered straight to your inbox :-)