DEV Community

Caleb Weeks
Caleb Weeks

Posted on

Advent of Code #1 (in F#)

It's that time of the year again! Advent of Code is here! And although it is only 12 days instead of 25 this year, I am super excited as usual. Maybe I'll even be able to finish it this time!

This year, I'm going to try to solve the problems using F#. I have done a little bit of OCaml before, which is pretty similar, but have never used F#. Since I've been using a lot of C# at work, I figured I would try the functional language in the .NET world.

(To anyone who follows me because I used to write a lot about JavaScript, sorry to spam you with random languages... If you are interested in polyglot content with inconsistent publishing schedules and level of effort, then stay tuned :P)

Anyway, back to the problem at hand. I think day 1 was was a touch harder than previous years. It took me over an hour to finish both parts, but most of that was getting to know F# (and my new setup with MacOS + Zed + Arc).

I tried to use the strong points of F# to model the domain. Active Patterns will probably be useful for parsing later on, but they seemed redundant when I tried to use them today. I used a union type for the Left and Right rotations, but looking back, the problem would have been so much easier if I had just replaced all instances of "L" with "-1" and removed all instances of "R".

Part 1 was fairly straightforward with some modular arithmetic. Part 2 was a bit of a twist (there is a pun in there somewhere). I think my solution of multiplying the new_state with the old state to see if it crossed 0 was pretty clever, but maybe there is a better way I didn't think of.

Some quick thoughts on F# so far:

  • Objects like System.String are shared with other .NET languages, and therefore have the same methods. Collections are not shared and follow the pattern that most functional languages use with a module of helper functions. It is weird to see that mix of patterns in the code.
  • Compile errors have not been very friendly so far. Perhaps it is because of the nature of type inference. I have had similar issues with OCaml and Julia.
  • Documentation seems to be split between fsharp.org and learn.microsoft.com. It has been a little confusing to figure out where to look.
open System
open System.IO

let input_string = File.ReadAllLines("input")

type Rotation =
    | Left of int
    | Right of int

let parse_input (input: string array) =
    input
    |> Array.filter (fun line -> line.Length > 0)
    |> Array.map (fun line ->
        if line.StartsWith "L" then Left (int (line.Trim('L')))
        else Right (int (line.Trim('R'))))

let input = parse_input input_string

let part1 (input: Rotation array) =
    input |> Array.fold (fun (sum, state) rotation ->
        let new_state =
            match rotation with
            | Left l -> (state - l) % 100
            | Right r -> (state + r) % 100
        if new_state = 0 then (sum + 1, new_state) else (sum, new_state)
    ) (0, 50)
    |> fst

printfn "%A" (part1 input)

let part2 (input: Rotation array) =
    input |> Array.fold (fun (sum, state) rotation ->
        let new_state =
            match rotation with
            | Left l -> state - l
            | Right r -> state + r
        if new_state = 0 then (sum + 1, new_state)
        elif new_state * state < 0 then (Math.Abs(new_state) / 100 + 1 + sum, new_state % 100)
        else (Math.Abs(new_state) / 100 + sum, new_state % 100)
    ) (0, 50)
    |> fst

printfn "%A" (part2 input)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)