Something went wrong. Try again.
Code for the Advent of Code event
aoc advent-of-code
Something went wrong. Try again.
4.1 kB · 179 lines
Ruby
at rust
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180#!/usr/bin/env ruby# frozen_string_literal: true
class Pair SIDE_INSPECT_S = { root: '', left: 'L', right: 'R' }.freeze
attr_accessor :parent, :left, :right, :side
def initialize(parent, left, right, side) @parent = parent @left = left @right = right @side = side if @left.is_a?(Pair) @left.parent = self @left.side = :left end return unless @right.is_a?(Pair) @right.parent = self @right.side = :right end
def self.from_array(array, parent = nil, side = :root) pair = Pair.new(parent, nil, nil, side)
if array[0].is_a? Numeric # rubocop:disable Style/ConditionalAssignment pair.left = array[0] else pair.left = from_array(array[0], pair, :left) end
if array[1].is_a? Numeric # rubocop:disable Style/ConditionalAssignment pair.right = array[1] else pair.right = from_array(array[1], pair, :right) end
pair end
def self.combine(left, right) Pair.new(nil, left, right, :root) end
def left? @side == :left end
def right? @side == :right end
def root? @side == :root end
def plain? left.is_a?(Numeric) && right.is_a?(Numeric) end
def depth return 0 if parent.nil? 1 + parent.depth end
def magnitude left_mag = (left.is_a?(Pair) ? left.magnitude : left) * 3 right_mag = (right.is_a?(Pair) ? right.magnitude : right) * 2 left_mag + right_mag end
def add_ancestor_left(value, came_from = nil, direction = :left) if came_from.nil? return if parent.nil? parent.add_ancestor_left(value, self, direction) elsif direction == :left if @left.is_a?(Numeric) @left += value elsif @left == came_from return if parent.nil? parent.add_ancestor_left(value, self, direction) else @left.add_ancestor_left(value, self, :right) end elsif @right.is_a?(Numeric) @right += value else @right.add_ancestor_left(value, self, :right) end end
def add_ancestor_right(value, came_from = nil, direction = :right) if came_from.nil? return if parent.nil? parent.add_ancestor_right(value, self, direction) elsif direction == :right if @right.is_a?(Numeric) @right += value elsif @right == came_from return if parent.nil? parent.add_ancestor_right(value, self, direction) else @right.add_ancestor_right(value, self, :left) end elsif @left.is_a?(Numeric) @left += value else @left.add_ancestor_right(value, self, :left) end end
def dup p_dup(nil) end def p_dup(dup_p = nil) Pair.new(dup_p, left.is_a?(Numeric) ? left : left.p_dup(self), right.is_a?(Numeric) ? right : right.p_dup(self), side) end
def to_s "[#{left}, #{right}]" end
def inspect "#{SIDE_INSPECT_S[side]}(#{left.inspect}, #{right.inspect})" endend
def explode(pair) return false unless pair.is_a?(Pair)
if pair.depth >= 4 && pair.plain? pair.add_ancestor_left pair.left pair.add_ancestor_right pair.right pair.parent.send("#{pair.side}=", 0) return true end
return true if explode(pair.left) return true if explode(pair.right)
falseend
def split(pair) return false unless pair.is_a?(Pair)
if pair.left.is_a?(Numeric) && pair.left >= 10 pair.left = Pair.new(pair, pair.left / 2, (pair.left / 2.0).ceil, :left) return true end
return true if split(pair.left)
if pair.right.is_a?(Numeric) && pair.right >= 10 pair.right = Pair.new(pair, pair.right / 2, (pair.right / 2.0).ceil, :right) return true end
return true if split(pair.right)
falseend
def sum(a, b) result = Pair.combine a, b loop do exploded = explode result next if exploded splitted = split result break unless splitted end resultend
pairs = ARGF.readlines.map { Pair.from_array(eval(_1)) }part2 = pairs.map(&:dup)
puts pairs.reduce { sum _1, _2 }.magnitudeputs part2.permutation(2).map { sum(_1.dup, _2.dup).magnitude }.max