Advent of Code 2016 - Day 8: Two-Factor Authentication

Adam Boucek profile

Adam Boucek

|

June 26th 2023

KotlinAOC

Overview of the problem

My second AOC task is the day 8 from 2016. I try to display a specific word on a 50x6px display. The display shows two types of characters, # and .. I try to move the # in the way it displays a word. Like so:

.#..#.#
#.#....
.#.....

At the front desk is cheatsheet how to find out the password.

  • rect AxB turns on all of the pixels in a rectangle at the top-left of the screen which is A wide and B tall.
  • rotate row y=A by B shifts all of the pixels in row A (0 is the top row) right by B pixels. Pixels that would fall off the right end appear at the left end of the row.
  • rotate column x=A by B shifts all of the pixels in column A (0 is the left column) down by B pixels. Pixels that would fall off the bottom appear at the top of the column.

The document might look like this.

rect 9x1
rotate column x=7 by 1
rotate row y=1 by 3

The instructions tell us to shift the symbols in a way it creates a word.

Solution

The solution for the first and second part is very similar, so I combined them into one section.

Firstly, I created the needed grid based on the provided dimensions of 50x6. Every cell contains .. I used the idiomatic approach to initialize a nested list.

val lines = List(50) { '.' } val grid = List(6) { lines }

Secondly, I iterate through the instruction text file line by line. I prepare the instruction check if there are no extra spaces that might mess up the split function.

val line = it.trim() val words = line.split(" ")

Then I take the first word from the instruction array and decide if the program will to turn on the light in provided dimensions or shift the symbols. I used Kotlin's native feature .first(), which takes the first element of the list.

The first instruction with adding # was pretty simple. I only needed to iterate the nested array by provided dimensions. However, I tried to use another idiomatic syntax like var (rangeY, rangeX) = words[1].split("x") like destructing a list, and for (y in 0 until rangeY.toInt() ) looping in a specific range. I am not sure whether this is the most elegant solution.

if (words.first() == "rect") { val (rangeY, rangeX) = words[1].split("x") // destructing dimension for (y in 0 until rangeY.toInt()) { for (x in 0 until rangeX.toInt()) { grid[x][y] = '#' } } }

The second part was significantly more challenging. It started similarly by destructing the direction of shifting and the number of the rows/columns. Again, the first part used .last() for destructing the last instruction of the line for variable by. The most crucial part was to figure out how to shift in a particular direction. For this part, I felt a bit hopeless and had to google how to shift the whole array and persist its length. Looking at it now, I see it was pretty simple. We store the column or row I want to shift and loop its length. I re-assign the lists' values with the old list by taking its index, deducting how many points I want to shift it, adding the length and module the length grid[x][line] = previous[(x - by + 6) % 6]. It shifts [#,#,.,.,.,.,.,.] by 4 to [.,.,.,.,#,#,.,.]. In this way, we shift the whole # in a way we need.

Lastly, we need to know how many pixels are lit. We simply iterate through the grid and check which cells have the value # .

var litPixels = 0 grid.forEach { line -> line.forEach { it -> if (it == '#') litPixels += 1 } }

Reflection

The project took me two afternoons (~9 hours). Even though the code is shorter than the previous Advent of Code, I have to admit it was significantly more demanding. I tried to apply the idiomatic approach from the start, which would make my refactoring easier.

There are some parts I was not sure if I used Kotlin correctly, like on line 4 val grid = List(6) { lines.toMutableList() }.toMutableList(). When I tried to make lines mutable on the same line with initialization, it didn't work then. Despite this little issue it still made my code easier than looping the array by adding the #.

Now, I understand that this kind of code challenge tests my knowledge of strings. Without operations like .split() , .trim(), .toInt(), it would be tough to solve these tasks.

Advent of Code 2016 - Day 8: Two-Factor Authentication

GitHub Repo