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)