DEV Community

Caleb Weeks
Caleb Weeks

Posted on

Advent of Code #14 (in Crystal)

My brother and I worked on this puzzle together. That is partly why the solution is more imperative than functional. Admittedly, though, I have found many of these Advent of Code puzzles easier to reason about with an imperative approach.

My brother suggested finding each stone and moving it up if the space above it was empty for each row. I was planning on over complicating it my looking how far the stone could move. In the end, I really like how the code turned out, and extending it to part 2 wasn't terrible.

Speaking of part 2, it was my brother's idea again that the stones would probably stabilize well before a billion cycles, and that we wouldn't have to get that far. We tested out the theory, and then after identifying a cycle in the outputs, reduced the potential answers down to eight candidates. We got the right answer after 5 guesses.

I tried to code up a solution to return the correct answer instead of just spitting out the weights for each cycle and having to guess. Admittedly, I'm not sure if the final code is correct, but it works for my input. It doesn't work for the example because the cycle for the example contains duplicate numbers, which I wasn't sure how to account for.

Here's my code, which may or may not work on your input:

input = File.read("input").strip

rows = input.split("\n").map(&.chars)

def tilt(rows)
  (1...rows.size).each do |row_num|
    (0...row_num).each do |prev|
      current_row = row_num - prev
      row_above = current_row - 1
      stones = rows[current_row].join.scan(/O/).map(&.begin)
      stones.each do |stone|
        if rows[row_above][stone] == '.'
          rows[row_above][stone] = 'O'
          rows[current_row][stone] = '.'
        end
      end
    end
  end
end

def weigh(rows)
  (0...rows.size).reduce(0) do |sum, row_num|
    sum + rows[row_num].join.scan("O").size * (rows.size - row_num)
  end
end

part1 = begin
  tilt(rows) 
  weigh(rows)
end

puts part1

cache = Set(String).new

part2 = begin
  last = -1
  current = 0
  i = 1
  cycle_start = nil
  cycle = Array(Int32).new
  while !cycle_start || !cycle.group_by { |x| x }.values.map { |x| x.size > 4 }.all?
    if i % 4 == 1
      cycle_start = i if last == current
      weight = weigh(rows)
      cycle.push(weight) if cycle_start
      cache.add(rows.map(&.join).join("\n"))
      last = current
      current = cache.size
    end
    tilt(rows)
    rows = rows.reverse.transpose
    i += 1
  end
  cycle = cycle[...cycle.size // 5]
  cycle[(4_000_000_000 - i + 4) % cycle.size]
end

puts part2
Enter fullscreen mode Exit fullscreen mode

Top comments (0)