Advent of Code 2016, Day 10: Balance Bots

#ruby #advent of code 2016

Part A

On Day 10 we are dealing with bots and chips. Each chip has a number or integer value. We also have information which values goes to which bot and what bot should do with those chips. Our input looks like this:

value 5 goes to bot 2
bot 2 gives low to bot 1 and high to bot 0
value 3 goes to bot 1
bot 1 gives low to output 1 and high to bot 0
bot 0 gives low to output 2 and high to output 0
value 2 goes to bot 2

So value 5 and value 2 goes to bot 2. Then bot 2 give low value (2 in this case) to bot 1 and gives high value (5 in this case) to bot 0. And so on and so on. The bot can proceed only when it gets 2 chips.

For first part we need to say what is number of bot comparing chips 61 and 17. Below is my solution:

data = File.readlines("10.txt", chomp: true)

@bots = []
@indexes = {
  0 => [],
  1 => [],
  2 => []
}
@moves = {}
@outputs = []

@target_low_chip = 17
@target_high_chip = 61

def give_to_bot(source_id, target_id, chip)
  if source_id
    had_chips = @bots[source_id].size

    @bots[source_id].delete(chip)
    @indexes[had_chips].delete(source_id)
    @indexes[had_chips - 1].push(source_id)
  end

  @bots[target_id] ||= []
  had_chips = @bots[target_id].size
  @bots[target_id].push(chip)
  @indexes[had_chips].delete(target_id)
  @indexes[had_chips + 1].push(target_id)
end

def put_to_output(source_id, target_id, chip)
  had_chips = @bots[source_id].size

  @bots[source_id].delete(chip)
  @indexes[had_chips].delete(source_id)
  @indexes[had_chips - 1].push(source_id)

  @outputs[target_id] = chip
end

data.each do |line|
  if line =~ /value (\d+) goes to bot (\d+)/
    match = Regexp.last_match
    chip, target_id = match.captures.map(&:to_i)

    give_to_bot(nil, target_id, chip)
  elsif line =~ /bot (\d+) gives low to (.+?) (\d+) and high to (.+?) (\d+)/
    match = Regexp.last_match
    source_bot = match[1].to_i
    low_target_type = match[2]
    low_target_id = match[3]
    high_target_type = match[4]
    high_target_id = match[5]

    @moves[source_bot.to_i] = {
      low: [low_target_type.to_sym, low_target_id.to_i],
      high: [high_target_type.to_sym, high_target_id.to_i]
    }
  end
end

while @indexes[2].size > 0
  source_id = @indexes[2][0]
  low_chip, high_chip = @bots[source_id].sort

  if low_chip == @target_low_chip && high_chip == @target_high_chip
    puts source_id
  end

  low = @moves[source_id][:low]
  high = @moves[source_id][:high]

  if low[0] == :bot
    target_id = low[1]
    give_to_bot(source_id, target_id, low_chip)
  else
    target_id = low[1]
    put_to_output(source_id, target_id, low_chip)
  end

  if high[0] == :bot
    target_id = high[1]
    give_to_bot(source_id, target_id, high_chip)
  else
    target_id = high[1]
    put_to_output(source_id, target_id, high_chip)
  end
end

# Part B
puts @outputs[0..2].reduce(1) { |product, bin| product * bin }

In this program we maintain @bots array that keeps track of what chips bots currently hold. @indexes hash keeps track which bots has 0, 1 or 2 chips. @outputs array keeps track of values that were put to output.

Then we have helper methods give_to_bot which updates all those arrays accordingly and put_to_output is doing the same when we need to put chip to output. Because we are modifying 3 different arrays or hashes it is crucial to implement it correctly.

In the end we just read the input and execute specified commands. If we compare values 17 and 61 we can output bot number.

Part B

In second part we just need to multiple values in outputs 0, 1 and 2. I added code for that in the solution for first part.