Code for the Advent of Code event
aoc advent-of-code
at rust 179 lines 4.1 kB view raw
1#!/usr/bin/env ruby 2# frozen_string_literal: true 3 4class Pair 5 SIDE_INSPECT_S = { root: '', left: 'L', right: 'R' }.freeze 6 7 attr_accessor :parent, :left, :right, :side 8 9 def initialize(parent, left, right, side) 10 @parent = parent 11 @left = left 12 @right = right 13 @side = side 14 if @left.is_a?(Pair) 15 @left.parent = self 16 @left.side = :left 17 end 18 return unless @right.is_a?(Pair) 19 @right.parent = self 20 @right.side = :right 21 end 22 23 def self.from_array(array, parent = nil, side = :root) 24 pair = Pair.new(parent, nil, nil, side) 25 26 if array[0].is_a? Numeric # rubocop:disable Style/ConditionalAssignment 27 pair.left = array[0] 28 else 29 pair.left = from_array(array[0], pair, :left) 30 end 31 32 if array[1].is_a? Numeric # rubocop:disable Style/ConditionalAssignment 33 pair.right = array[1] 34 else 35 pair.right = from_array(array[1], pair, :right) 36 end 37 38 pair 39 end 40 41 def self.combine(left, right) 42 Pair.new(nil, left, right, :root) 43 end 44 45 def left? 46 @side == :left 47 end 48 49 def right? 50 @side == :right 51 end 52 53 def root? 54 @side == :root 55 end 56 57 def plain? 58 left.is_a?(Numeric) && right.is_a?(Numeric) 59 end 60 61 def depth 62 return 0 if parent.nil? 63 1 + parent.depth 64 end 65 66 def magnitude 67 left_mag = (left.is_a?(Pair) ? left.magnitude : left) * 3 68 right_mag = (right.is_a?(Pair) ? right.magnitude : right) * 2 69 left_mag + right_mag 70 end 71 72 def add_ancestor_left(value, came_from = nil, direction = :left) 73 if came_from.nil? 74 return if parent.nil? 75 parent.add_ancestor_left(value, self, direction) 76 elsif direction == :left 77 if @left.is_a?(Numeric) 78 @left += value 79 elsif @left == came_from 80 return if parent.nil? 81 parent.add_ancestor_left(value, self, direction) 82 else 83 @left.add_ancestor_left(value, self, :right) 84 end 85 elsif @right.is_a?(Numeric) 86 @right += value 87 else 88 @right.add_ancestor_left(value, self, :right) 89 end 90 end 91 92 def add_ancestor_right(value, came_from = nil, direction = :right) 93 if came_from.nil? 94 return if parent.nil? 95 parent.add_ancestor_right(value, self, direction) 96 elsif direction == :right 97 if @right.is_a?(Numeric) 98 @right += value 99 elsif @right == came_from 100 return if parent.nil? 101 parent.add_ancestor_right(value, self, direction) 102 else 103 @right.add_ancestor_right(value, self, :left) 104 end 105 elsif @left.is_a?(Numeric) 106 @left += value 107 else 108 @left.add_ancestor_right(value, self, :left) 109 end 110 end 111 112 def dup 113 p_dup(nil) 114 end 115 def p_dup(dup_p = nil) 116 Pair.new(dup_p, left.is_a?(Numeric) ? left : left.p_dup(self), right.is_a?(Numeric) ? right : right.p_dup(self), side) 117 end 118 119 def to_s 120 "[#{left}, #{right}]" 121 end 122 123 def inspect 124 "#{SIDE_INSPECT_S[side]}(#{left.inspect}, #{right.inspect})" 125 end 126end 127 128def explode(pair) 129 return false unless pair.is_a?(Pair) 130 131 if pair.depth >= 4 && pair.plain? 132 pair.add_ancestor_left pair.left 133 pair.add_ancestor_right pair.right 134 pair.parent.send("#{pair.side}=", 0) 135 return true 136 end 137 138 return true if explode(pair.left) 139 return true if explode(pair.right) 140 141 false 142end 143 144def split(pair) 145 return false unless pair.is_a?(Pair) 146 147 if pair.left.is_a?(Numeric) && pair.left >= 10 148 pair.left = Pair.new(pair, pair.left / 2, (pair.left / 2.0).ceil, :left) 149 return true 150 end 151 152 return true if split(pair.left) 153 154 if pair.right.is_a?(Numeric) && pair.right >= 10 155 pair.right = Pair.new(pair, pair.right / 2, (pair.right / 2.0).ceil, :right) 156 return true 157 end 158 159 return true if split(pair.right) 160 161 false 162end 163 164def sum(a, b) 165 result = Pair.combine a, b 166 loop do 167 exploded = explode result 168 next if exploded 169 splitted = split result 170 break unless splitted 171 end 172 result 173end 174 175pairs = ARGF.readlines.map { Pair.from_array(eval(_1)) } 176part2 = pairs.map(&:dup) 177 178puts pairs.reduce { sum _1, _2 }.magnitude 179puts part2.permutation(2).map { sum(_1.dup, _2.dup).magnitude }.max