aboutsummaryrefslogtreecommitdiff
path: root/rb/day7.rb
blob: 3d4283b4a26591b06bacf4777db82b339b066719 (plain)
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/usr/bin/env ruby

# What can I say? I like the word reify.

vars = {}
nets = []

class Var
  def initialize(name)
    @name = name
    @value = nil
  end

  attr_reader :name, :value

  def reified?
    !@value.nil?
  end

  def try_reify_numeric!
    if @name =~ /\A\d+\Z/
      @value = @name.to_i
    end
  end

  def reify!(value)
    @value = value
  end

  def reify_negative!
    @value = 65536 + @value if @value < 0
  end

  def reset!
    @value = nil
  end
end

class Net
  def initialize(left, op, right, out)
    @left = left
    @op = op
    @right = right
    @out = out
    @reified = false
  end

  def reified?
    @reified
  end

  def reifiable?
    return false unless @left.nil? || @left.reified?
    return false unless @right.reified?
    return true
  end

  def reify!
    case @op
    when nil
      @out.reify!(@right.value)
    when "NOT"
      @out.reify!(~@right.value)
    when "AND"
      @out.reify!(@left.value & @right.value)
    when "OR"
      @out.reify!(@left.value | @right.value)
    when "LSHIFT"
      @out.reify!(@left.value << @right.value)
    when "RSHIFT"
      @out.reify!(@left.value >> @right.value)
    end

    @reified = true
  end

  def reset!
    @reified = false
  end
end

File.readlines("day7.input").map(&:strip).each do |line|
  input, output = line.split(" -> ")
  input = input.split

  vars[output] = Var.new(output) if vars[output].nil?
  output = vars[output]

  case input.length
  when 1
    vars[input[0]] = Var.new(input[0]) if vars[input[0]].nil?
    nets << Net.new(nil, nil, vars[input[0]], output)
  when 2
    vars[input[1]] = Var.new(input[1]) if vars[input[1]].nil?
    nets << Net.new(nil, input[0], vars[input[1]], output)
  when 3
    vars[input[0]] = Var.new(input[0]) if vars[input[0]].nil?
    vars[input[2]] = Var.new(input[2]) if vars[input[2]].nil?
    nets << Net.new(vars[input[0]], input[1], vars[input[2]], output)
  end
end

# Part 1
vars.values.each(&:try_reify_numeric!)

until nets.all?(&:reified?)
  nets.each do |net|
    net.reify! if net.reifiable?
  end
end

vars.values.each(&:reify_negative!)

part1 = vars["a"].value
puts "Part 1: #{part1}"

# Part 2
vars.values.each(&:reset!)
nets.each(&:reset!)

vars.values.each(&:try_reify_numeric!)

until nets.all?(&:reified?)
  nets.each do |net|
    vars["b"].reify!(part1)
    net.reify! if net.reifiable?
  end
end

vars.values.each(&:reify_negative!)

part2 = vars["a"].value
puts "Part 2: #{part2}"