namespace aoc2021; /// /// Day 18: /// public sealed class Day18 : Day { private readonly List _fishes; public Day18() : base(18, "Snailfish") { _fishes = Input.ToList(); } private static Tree Parse(string input) { static Tree.Node ParseFish(Tree.Node? parent, string input, ref int cursor) { if (input[cursor] != '[') return new(parent, input[cursor++] - '0'); var node = new Tree.Node(parent, -1); cursor++; node.Left = ParseFish(node, input, ref cursor); cursor++; node.Right = ParseFish(node, input, ref cursor); cursor++; return node; } var cursor = 0; return new(ParseFish(null, input, ref cursor)); } private static Tree Add(Tree a, Tree b) { var reduced = new Tree(new(null, -1) {Left = a.Root, Right = b.Root}); Reduce(reduced); return reduced; } private static Tree.Node? SiblingOf(Tree.Node from, Func.Node, Tree.Node?> move1, Func.Node, Tree.Node?> move2) { var current = from; while (current.Parent != null) { if (move1(current.Parent) == current) { var other = move2(current.Parent); while (other?.Data == -1) other = move1(other) ?? move2(other); return other; } current = current.Parent; } return null; } private static void Reduce(Tree tree) { bool ReduceRecurse(Tree.Node node, Func, Tree.Node, bool> reducer) { if (reducer(tree, node)) return true; if (node.Left != null && ReduceRecurse(node.Left, reducer)) return true; return node.Right != null && ReduceRecurse(node.Right, reducer); } bool Explode(Tree t, Tree.Node node) { if (node.Data != -1 || node.DistanceToParent(t.Root) < 4) return false; var left = SiblingOf(node, n => n.Right, n => n.Left); if (left != null) left.Data += node.Left!.Data; var right = SiblingOf(node, n => n.Left, n => n.Right); if (right != null) right.Data += node.Right!.Data; node.Left = null; node.Right = null; node.Data = 0; return true; } bool Split(Tree t, Tree.Node node) { if (node.Data < 10) return false; node.Left = new(node, node.Data / 2); node.Right = new(node, node.Data / 2 + node.Data % 2); node.Data = -1; return true; } var changed = true; while (changed) { changed = false; while (ReduceRecurse(tree.Root, Explode)) changed = true; if (ReduceRecurse(tree.Root, Split)) changed = true; } } private static long Magnitude(Tree.Node? node) { if (node?.Data >= 0) return node.Data; return 3 * Magnitude(node?.Left) + 2 * Magnitude(node?.Right); } public override object Part1() => Magnitude(_fishes.Skip(1).Aggregate(Parse(_fishes.First()), (a, b) => Add(a, Parse(b))).Root); public override object Part2() { var best = 0L; for (var i = 0; i < _fishes.Count; i++) best = _fishes .Where((_, j) => i != j) .Select(t => Add(Parse(_fishes[i]), Parse(t))) .Select(result => Magnitude(result.Root)) .Prepend(best) .Max(); return best; } }