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;
}
}