Advent of Code 2016, Day 15: Timing is Everything

#ruby #advent of code 2016

Part A and B

On Day 15 we are dealing with a stack of spinning discs with slots. If we press a button then a capsule is dropped and it goes through the slots in those discs. If the discs are not aligned then the capsule bounces back. Our input looks like this:

Disc #1 has 5 positions; at time=0, it is at position 4.
Disc #2 has 2 positions; at time=0, it is at position 1.

We need find a time when we can press the button and get the capsule.

This task is a bit confusing. When I was writing this article I already forgot what is it about and then had to spend additional time to figure out what you need to do.

Let me try to explain in different words. We have those discs that are at a given position and they shift to the next position in each second. Then we have a capsule that drops and reaches each first disc at second 1, second disc at second 2, third disc at second 3 and so on. So when capsule will reach first disc it will move just once, second disc will move two times, third disc will move three times and so on.

So what we can do in the beginning is move each disc by that many times so it is in the position when capsule will reach it if we press button at time 0.

We move first disc one time, we move second disc two times etc. This is the final alignment if we press button at time 0. To get the capsule all discs must be at position 0. If that’s not the case we just need to move all discs one and check again. And repeat it until we reach a perfect alignment.

Here is the code:

@data = File.
  readlines("15a.txt", chomp: true).
  map do |line|
    match = line.match(/Disc \#\d+ has (\d+) positions; at time=0, it is at position (\d+)\./)
    [match[1].to_i, match[2].to_i]
  end

def move_disc(disc)
  @data[disc - 1][1] = (@data[disc - 1][1] + 1) % @data[disc - 1][0]
end

def aligned?
  @data.all? { |disc| disc[1] == 0 }
end

def move_all
  @data.size.times do |disc|
    move_disc(disc)
  end
end

@data.size.times do |i|
  (i + 1).times { move_disc(i + 1) }
end

time = 0
while true
  if aligned?
    puts @data.inspect
    puts "All discs are aligned at time #{time}"
    break
  end
  move_all

  time += 1
end

In Part B we have one more disc, so it takes a bit longer to calculate the answer. But the code is the same