Advent of Code F# – Day 1

It’s Decem­ber again, which means two things have start­ed :

unfor­tu­nate­ly I was too busy (that is, until the swift and untime­ly death of Yubl…) to be involved with the F# Advent Cal­en­dar this year, but I’m cer­tain­ly not going to miss out on the Advent of Code.

ps. look out for all my oth­er solu­tions for Advent of Code chal­lenges here.


Day 1

See details of the chal­lenge here.

Start­ing off nice and easy, let’s cap­ture the domain and input data in a mod­ule, along with some helper func­tions.

Here, we have defined an Instruc­tion active pat­tern for pars­ing a string (eg. “L72”) into a tuple of:

  1. the direc­tion to turn — ie L or R
  2. the no. of blocks to move in that direc­tion

As we fol­low along the instruc­tions, we also need to track the direc­tion we’re mov­ing in — North, West, East or South. Hence why I added a turn func­tion that will return the new Direc­tion giv­en a tuple of:

  1. the cur­rent direc­tion we’re mov­ing in — N, W, E or S
  2. which way we’re turn­ing — L or R

Now we’re ready to answer Part 1 of the chal­lenge.

Most of this code should be straight for­ward, but there is one point I’d like to draw your atten­tion to. Many peo­ple don’t realise you can use active pat­terns out­side of match…with, but you can in fact use it inline where an argu­ment should be as I have done in the Array.fold.

Here, instead of accept­ing an instruc­tion as a string (eg, “L72”) I can use the afore­men­tioned Instruc­tion active pat­tern to parse it and cap­ture the angle to turn (L or R) and the no. of steps to move along the new direc­tion, all in one go.

Start­ing at (0, 0), we iter­a­tive­ly (well, using a fold) turn and move until we have exhaust­ed all the instruc­tions in the input data. To cal­cu­late how many blocks away the East­er Bun­ny HQ is we sim­ply have to add the final X and Y coor­diantes togeth­er, so long as we don’t for­get to Math.Abs first (which I total­ly did at my first attempt)!


Part 2

Then, you notice the instruc­tions con­tin­ue on the back of the Recruit­ing Doc­u­ment. East­er Bun­ny HQ is actu­al­ly at the first loca­tion you vis­it twice.

For exam­ple, if your instruc­tions are R8, R4, R4, R8, the first loca­tion you vis­it twice is 4 blocks away, due East.

How many blocks away is the first loca­tion you vis­it twice?

For part 2, we need to know more than just where we end­ed up at after each instruc­tion, we need to know all the posi­tions we vis­it­ed. We can mod­i­fy the move func­tion from part 1 to return all the posi­tions:

You can also achieve the same result with Seq.scan instead.

We can stick to the same struc­ture as part 1 and use a fold to iter­ate through all the instruc­tions and built up a trail of posi­tions we have vis­it­ed. How­ev­er, it felt a lit­tle con­vo­lut­ed, so I opt­ed for a slight­ly dif­fer­ent approach:

The fol­low func­tion turns a string list and returns all the posi­tions that are vis­it­ed as a sequence.

To find the first posi­tion that are vis­it­ed more than once, 2 approach­es jump to mind:

  1. group by the posi­tions, and return the first posi­tion with count > 1
  2. iter­ate through the posi­tions with a muta­ble Hash­Set, skip­While the posi­tions is added to the set (remem­ber, HashSet<T>.Add returns true if the item is added), then take the head of the result­ing sequence

Whilst it’s less effi­cient, I opt­ed for the first approach for its sim­plic­i­ty.

So, putting every­thing togeth­er and you have a solu­tion for part 2: