1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
class Factory
  attr_reader :bots, :bins

  def initialize
    @bots = Hash.new {|h,k| h[k] = Bot.new(self) }
    @bins = Hash.new {|h,k| h[k] = Bin.new }
  end

  def <<(instruction)
    case instruction
    when /value (\d+) goes to bot (\d+)/
      bots[$2.to_i] << $1.to_i
    when /bot (\d+) gives low to (.+) (\d+) and high to (.+) (\d+)/
      bot = bots[$1.to_i]
      bot.outs = [out($2, $3.to_i),
                  out($4, $5.to_i)]
    else
      raise "invalid instruction: #{instruction}"
    end
  end

  private

  def out(type, id)
    case type
    when 'bot'
      bots[id]
    when 'output'
      bins[id]
    else
      raise "invalid type: #{type}"
    end
  end
end

class Bot
  attr_reader :factory, :values, :outs, :log

  def initialize(factory)
    @factory = factory
    @values = []
    @log = []
  end

  def <<(value)
    values << value
    try_run
  end

  def outs=(outs)
    @outs = outs
    try_run
  end

  private

  def try_run
    return unless outs && values.size == 2

    log << values.sort

    low, high = values.sort
    values.clear

    outs[0] << low
    outs[1] << high
  end
end

class Bin
  attr_reader :value

  def <<(value)
    @value = value
  end
end

if __FILE__ == $0
  factory = Factory.new
  DATA.each_line do |line|
    factory << line
  end

  p factory.bins.values_at(0, 1, 2).map(&:value).inject(:*)
end

require 'minitest'
# require 'minitest/autorun'

class TestDay10 < Minitest::Test
  def test_day_10
    instructions = <<INSTRUCTIONS
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
INSTRUCTIONS

    factory = Factory.new
    instructions.lines.each do |line|
      factory << line
    end

    assert_equal [5, 2, 3], factory.bins.sort_by {|k,_| k }.map(&:last).map(&:value)
  end
end

__END__