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
Top comments (0)