-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday18.rb
189 lines (157 loc) · 4.4 KB
/
day18.rb
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# frozen_string_literal: true
require 'json'
module Refinements
refine String do
def to_sn
sn_array = JSON.parse(self)
SnailfishNumber.from_array(sn_array)
end
end
end
using Refinements
# https://adventofcode.com/2021/day/18
class SnailfishNumber
attr_accessor :left, :right, :value, :parent
# Turn a suitable array into a snailfish number. N.B. as this is unwrapped a call further down the recursion chain may pass in a value and not an array.
def self.from_array(sn_array)
if sn_array.is_a? Array
sn_number = SnailfishNumber.new
sn_number.left = from_array(sn_array.first)
sn_number.left.parent = sn_number
sn_number.right = from_array(sn_array.last)
sn_number.right.parent = sn_number
return sn_number
end
return SnailfishNumber.new(value: sn_array) if sn_array.is_a? Numeric
nil
end
def initialize(left: nil, right: nil, value: nil, parent: nil)
@left = left
@right = right
@value = value
@parent = parent
end
def to_a
return @value unless @value.nil?
[@left.to_a, @right.to_a]
end
def clone
to_a.to_s.to_sn
end
def +(other)
add(other)
end
def add(other)
result = SnailfishNumber.new(left: clone, right: other.clone, parent: parent)
result.left.parent = result
result.right.parent = result
result.reduce
end
def reduce
to_expl = to_explode(0)
if to_expl.nil?
to_spl = to_split
unless to_spl.nil?
to_spl.split
reduce
end
else
to_expl.explode
reduce
end
self
end
def to_explode(depth)
depth += 1
return self if depth > 4
unless left.nil? || !left.pair?
to_explode_from_left = left.to_explode(depth)
return to_explode_from_left unless to_explode_from_left.nil?
end
unless right.nil? || !right.pair?
to_explode_from_right = right.to_explode(depth)
return to_explode_from_right unless to_explode_from_right.nil?
end
nil
end
def pair?
!left.nil?
end
def explode
raise 'It was said that "Exploding pairs will always consist of two regular numbers"' if left.value.nil? || right.value.nil?
add_to_next_left(left.value)
add_to_next_right(right.value)
@left = nil
@right = nil
@value = 0
end
def add_to_next_left(to_add)
return nil if parent.nil?
if parent.right == self
parent.left.add_to_rightest_value(to_add)
else
parent.add_to_next_left(to_add)
end
end
def add_to_next_right(to_add)
return nil if parent.nil?
if parent.left == self
parent.right.add_to_leftest_value(to_add)
else
parent.add_to_next_right(to_add)
end
end
def add_to_rightest_value(to_add)
if @right.nil?
@value += to_add
else
@right.add_to_rightest_value(to_add)
end
end
def add_to_leftest_value(to_add)
if @left.nil?
@value += to_add
else
@left.add_to_leftest_value(to_add)
end
end
def to_split
return self if [email protected]? && @value > 9
unless left.nil?
to_split_from_left = left.to_split
return to_split_from_left unless to_split_from_left.nil?
end
unless right.nil?
to_split_from_right = right.to_split
return to_split_from_right unless to_split_from_right.nil?
end
nil
end
def split
@left = SnailfishNumber.new(value: @value / 2, parent: self)
@right = SnailfishNumber.new(value: (@value + 1) / 2, parent: self)
@value = nil
end
# The magnitude of a pair is 3 times the magnitude of its left element plus 2 times the magnitude of its right element. The magnitude of a regular number is just
# that number
def magnitude
return 3 * @left.magnitude + 2 * @right.magnitude if @value.nil?
@value
end
end
def homework(filename)
sn_numbers = File.readlines(filename).map(&:chomp).map(&:to_sn)
added = sn_numbers.drop(1).reduce(sn_numbers[0]) { |sum, sn| sum + sn }
result = added.magnitude
puts "The magnitude of the sum of the snailfish numbers described in #{filename} is #{result}"
magnitudes = []
sn_numbers.each do |sn1|
sn_numbers.each do |sn2|
next if sn1 == sn2
magnitudes << (sn1 + sn2).magnitude
magnitudes << (sn2 + sn1).magnitude
end
end
puts "The maximum magnitude of the sum of any two of the snailfish numbers described in #{filename} is #{magnitudes.max}"
end
homework('../inputs/2021/day18-input-01.txt') if __FILE__ == $PROGRAM_NAME