Problem description

This is a 2-dimensional grid problem. The input data is a grid, where each cell can be either empty (.) or contain a roll of paper (@).

..@@.@@@@.
@@@.@.@.@@
@@@@@.@.@@
@.@@@@..@.
@@.@@@@.@@
.@@@@@@@.@
.@.@.@.@@@
@.@@@.@@@@
.@@@@@@@@.
@.@.@@@.@.

In my code, I modelized the grid as a 2-dimensional Array of Booleans.

Part 1

First, we have to calculate how many rolls of paper have fewer than 4 rolls of paper in adjacent positions (horizontally, vertically or diagonally).

So I defined a list of vectors representing coordinate differences between a roll of paper and neighbouring positions.

private val dirs: List[(Int, Int)] = List((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1))

Then, I defined a method that returns the number of adjacent paper rolls.

private def adjacentRolls(grid: Array[Array[Boolean]], row: Int, col: Int): Int =
  var rolls = 0

  for
    dir <- DIRS
    if row + dir(0) >= 0 && row + dir(0) < grid.length
    if col + dir(1) >= 0 && col + dir(1) < grid.head.length
  do
    if grid(row + dir(0))(col + dir(1)) then rolls += 1

  rolls

Finally, I could use this method to count how many paper rolls have fewer than 4 paper rolls in their neighbourhood.

def solvePart1(input: List[String]): Int =
  val grid = input.map(line => line.map(c => c == '@').toArray).toArray
  var count = 0

  for
    row <- 0 until grid.length
    col <- 0 until grid.head.length
  do
    if grid(row)(col) && adjacentRolls(grid, row, col) < 4 then
      count += 1

  count

Part 2

Now, the paper rolls that have fewer than 4 neighbours are removed from the grid. This process is repeated until no paper roll can be accessed.

My code for Part 2 is a simple loop, with an exit condition (the variable done in my code). At the beginning of each step, the variable done is set to false, and it is set to true whenever an accessible paper roll is found.

Before and after the loop, the number of paper rolls is counted, and the result is the difference between the two numbers.

def solvePart2(input: List[String]): Int =
  var grid = input.map(line => line.map(c => c == '@').toArray).toArray
  val rolls = grid.flatten.count(c => c == true)
  var done = false

  while !done do
    done = true
    var newGrid = (0 until grid.length).map(row => (0 until grid.head.length).map(col => false).toArray).toArray
    for
      row <- 0 until grid.length
      col <- 0 until grid.head.length
      if grid(row)(col)
    do
      if adjacentRolls(grid, row, col) >= 4 then newGrid(row)(col) = true else done = false
    grid = newGrid

  rolls - grid.flatten.count(c => c == true)