Problem description

The goal is to find a password that opens a safe, by following a list of instructions. The safe has a circular dial, with 100 positions, from 0 to 99. The instructions are rotations to the left (toward lower numbers, beginning with a L), or to the right (toward higher numbers, beginning with a R).

Part 1

In this part, the password is the number of times the dial points to 0 after a rotation.

I was a bit slow to solve this part, as I had not completely finished my preparation when the problem was unlocked.

Instruction parsing was very simple, thanks to pattern matching.

The “mathematical” part was a bit tricky, as the modulo (%) operator in Scala returns a negative number when the first argument is negative (this can happen for rotations to the left). For example, -25 % 100 returns -25 in Scala, and 75 in Python. I could have used the method Math.floorMod, but I was not aware of its existence, and I knew another way to get the desired result.

  def solvePart1(input: List[String]): Int =
    var dial = 50
    var count = 0

    for instruction <- input do
      instruction match
        case s"L$clicks" => dial = (dial - clicks.toInt + 100) % 100
        case s"R$clicks" => dial = (dial + clicks.toInt) % 100
      if dial == 0 then count += 1

    count

Part 2

In this second part, the password is the number of times any click causes the dial to point at zero.

Fortunately, I understood the warning (“Be careful: if the dial were pointing at 50, a single rotation like R1000 would cause the dial to point at 0 ten times before returning back to 50!”) in the problem description.

My code was a bit more complex in the “left rotation” case, as I had to keep in memory the starting position, to avoid double counting.

  def solvePart2(input: List[String]): Int =
    var dial = 50
    var count = 0

    for instruction <- input do
      instruction match
        case s"L$clicks" =>
          var nextDial = dial
          nextDial -= clicks.toInt
          if nextDial <= 0 then count -= (nextDial / 100 - 1)
          if dial == 0 then count -= 1
          dial = nextDial % 100
          if dial < 0 then dial += 100
        case s"R$clicks" =>
          dial += clicks.toInt
          count += dial / 100
          dial %= 100

    count

About the unit test.

I used the test case given in the problem description to build my unit test. But, in this test case, the number of clicks to the left or to the right is always inferior to 100. This is insufficient to prove the correction of the Part 2 code.

In a professional context, I would have added another test case, with clicks numbers greater than 100.

Scala leaderboard

This year, as there is no global leaderboard, so I decided to enter a private Scala leaderboard. After this first day, I am ranked fifth, out of 19 solvers.