Problem statement on Advent of Code website.
Code for this post can be found on GitHub.
On Day 3, we are walking through a design department and see a list values specifying various triangles. Our job is to be a good citizen and help the design department by identifying the triangles which are "possible".
The algorithm which determines whether a triangle is "possible" is stated as follows:
In a valid triangle, the sum of any two sides must be larger than the remaining side.
I rephrased this for myself as follows:
In a valid triangle, the sum of the two smaller sides must be larger than the largest side.
The steps of the solution are as follows:
Some choices I made up-front:
For Step 1, I started by creating an input file.
I wrote a small function to determine whether a triangle is valid or not.
let possibleTriangle ptr = ptr |> Seq.sort |> fun x -> (Seq.item 0 x) + (Seq.item 1 x) > (Seq.item 2 x)
The largest part of the solution was to write a function to:
let possibleTrianglesCount triangleEvaluator = File.ReadLines @"./puzzle_input" |> Seq.map ((fun line -> Regex.Matches (line, @"[0-9]+")) >> Seq.cast >> Seq.map (fun (x : System.Text.RegularExpressions.Match) -> x.Value |> int) ) |> Seq.filter triangleEvaluator
I like the fact that the 3 steps needed to perform this part of the job are so obviously mapped to the source code:
The last step was to connect
let day3part1 = possibleTrianglesCount possibleTriangle |> Seq.length
I deliberately kept the summarization step separate from the filtering action on the off-chance that the problem for Part 2 would ask me to do something different with the list of valid triangles (oh, how wrong I was!).
This function, when executed on the input, gives a value of
982, which is the correct answer.
Part 2 threw in a twist that I did not expect. It changed the way that the program has to interpret the input file:
For example, if this is the input file with just 9 numbers:
101 301 501 102 302 502 103 303 503
Then the three triangles are:
My goal for the solution to Part 2 was to try and re-use as much code as possible. The first, obvious candidate for re-use was the
possibleTriangle function. Unfortunately, I could not re-use the parsing code, since that code was embedded in the
To parse and filter the list of triangles, I chose the following steps:
ints into 1 longer column
seqs of 3
let possibleVerticalTrianglesCount triangleEvaluator = (* Turn 3 columns of ints into 1 column *) let colOfInts ints = ints |> Seq.map (Seq.item 0) |> Seq.append (ints |> Seq.map (Seq.item 1)) |> Seq.append (ints |> Seq.map (Seq.item 2)) (* Read the data, serialize it, re-group it, then filter using evaluation function *) File.ReadLines @"./puzzle_input" |> Seq.map ((fun line -> Regex.Matches (line, @"[0-9]+")) >> Seq.cast >> Seq.map (fun (x : System.Text.RegularExpressions.Match) -> x.Value |> int) ) |> colOfInts |> Seq.chunkBySize 3 |> Seq.filter triangleEvaluator
Thanks to F#'s OOTB functions in the
Seq module, the only real change compared to the previous function was adding the part to change columns of 3 integers into a single column of integers.
The last step was to invoke the new function.
let day3part2 = possibleVerticalTrianglesCount possibleTriangle |> Seq.length
This function produces a value of
1826, which is the correct answer.
From Day 3, I picked up a few more lessons learned.
See you next time!