2021-12-18 15:54:06 +00:00
|
|
|
|
namespace aoc2021;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Day 18: <see href="https://adventofcode.com/2021/day/18"/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public sealed class Day18 : Day
|
|
|
|
|
{
|
|
|
|
|
private readonly List<string> _fishes;
|
2021-12-23 18:46:26 +00:00
|
|
|
|
|
2021-12-18 15:54:06 +00:00
|
|
|
|
public Day18() : base(18, "Snailfish")
|
|
|
|
|
{
|
|
|
|
|
_fishes = Input.ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Tree<int> Parse(string input)
|
|
|
|
|
{
|
|
|
|
|
static Tree<int>.Node ParseFish(Tree<int>.Node? parent, string input, ref int cursor)
|
|
|
|
|
{
|
|
|
|
|
if (input[cursor] != '[') return new(parent, input[cursor++] - '0');
|
2021-12-23 18:46:26 +00:00
|
|
|
|
|
2021-12-18 15:54:06 +00:00
|
|
|
|
var node = new Tree<int>.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<int> Add(Tree<int> a, Tree<int> b)
|
|
|
|
|
{
|
2021-12-18 16:01:23 +00:00
|
|
|
|
var reduced = new Tree<int>(new(null, -1) {Left = a.Root, Right = b.Root});
|
2021-12-18 15:54:06 +00:00
|
|
|
|
Reduce(reduced);
|
|
|
|
|
return reduced;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static Tree<int>.Node? SiblingOf(Tree<int>.Node from, Func<Tree<int>.Node, Tree<int>.Node?> move1,
|
|
|
|
|
Func<Tree<int>.Node, Tree<int>.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<int> tree)
|
|
|
|
|
{
|
|
|
|
|
bool ReduceRecurse(Tree<int>.Node node, Func<Tree<int>, Tree<int>.Node, bool> reducer)
|
|
|
|
|
{
|
|
|
|
|
if (reducer(tree, node)) return true;
|
|
|
|
|
if (node.Left != null && ReduceRecurse(node.Left, reducer)) return true;
|
2021-12-23 18:46:26 +00:00
|
|
|
|
return node.Right != null && ReduceRecurse(node.Right, reducer);
|
2021-12-18 15:54:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Explode(Tree<int> t, Tree<int>.Node node)
|
|
|
|
|
{
|
|
|
|
|
if (node.Data != -1 || node.DistanceToParent(t.Root) < 4) return false;
|
|
|
|
|
var left = SiblingOf(node, n => n.Right, n => n.Left);
|
2021-12-18 16:01:23 +00:00
|
|
|
|
if (left != null) left.Data += node.Left!.Data;
|
2021-12-18 15:54:06 +00:00
|
|
|
|
var right = SiblingOf(node, n => n.Left, n => n.Right);
|
2021-12-18 16:01:23 +00:00
|
|
|
|
if (right != null) right.Data += node.Right!.Data;
|
2021-12-18 15:54:06 +00:00
|
|
|
|
|
|
|
|
|
node.Left = null;
|
|
|
|
|
node.Right = null;
|
|
|
|
|
node.Data = 0;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Split(Tree<int> t, Tree<int>.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<int>.Node? node)
|
|
|
|
|
{
|
|
|
|
|
if (node?.Data >= 0) return node.Data;
|
|
|
|
|
return 3 * Magnitude(node?.Left) + 2 * Magnitude(node?.Right);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-18 21:09:07 +00:00
|
|
|
|
public override object Part1() =>
|
|
|
|
|
Magnitude(_fishes.Skip(1).Aggregate(Parse(_fishes.First()), (a, b) => Add(a, Parse(b))).Root);
|
2021-12-18 15:54:06 +00:00
|
|
|
|
|
2021-12-18 15:59:07 +00:00
|
|
|
|
public override object Part2()
|
|
|
|
|
{
|
|
|
|
|
var best = 0L;
|
2021-12-23 18:46:26 +00:00
|
|
|
|
for (var i = 0; i < _fishes.Count; i++)
|
2021-12-18 15:59:07 +00:00
|
|
|
|
best = _fishes
|
|
|
|
|
.Where((_, j) => i != j)
|
|
|
|
|
.Select(t => Add(Parse(_fishes[i]), Parse(t)))
|
|
|
|
|
.Select(result => Magnitude(result.Root))
|
|
|
|
|
.Prepend(best)
|
|
|
|
|
.Max();
|
|
|
|
|
|
|
|
|
|
return best;
|
|
|
|
|
}
|
2021-12-18 15:54:06 +00:00
|
|
|
|
}
|