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
đź‘‹ While you are here

Reinvent your career. Join DEV.

It takes one minute and is worth it for your career.

Get started

Top comments (0)

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

đź‘‹ Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay