diff --git a/advent-of-code-2021.csproj b/advent-of-code-2021.csproj index 8cf4a20..8c40388 100644 --- a/advent-of-code-2021.csproj +++ b/advent-of-code-2021.csproj @@ -69,6 +69,9 @@ PreserveNewest + + PreserveNewest + diff --git a/inputs/18.txt b/inputs/18.txt new file mode 100644 index 0000000..889631d --- /dev/null +++ b/inputs/18.txt @@ -0,0 +1,100 @@ +[3,[5,[7,[3,9]]]] +[[[[7,0],0],[2,[2,8]]],[[[7,8],1],3]] +[[[[2,7],0],7],4] +[[2,1],[9,0]] +[[[[7,1],[3,2]],[[9,8],5]],[2,7]] +[[[8,9],[[8,7],0]],[[[8,7],[6,3]],[[1,7],[8,9]]]] +[[8,6],[[9,[1,7]],[6,[3,9]]]] +[[2,[[5,6],6]],[[4,[5,9]],[3,[4,5]]]] +[[[[2,0],[1,1]],[6,6]],[[1,9],[[2,7],[6,8]]]] +[[[4,6],[[6,3],[3,9]]],[[[2,6],[6,1]],[[9,9],[1,5]]]] +[[[4,[3,1]],3],6] +[[0,[[5,2],8]],[1,[9,[4,3]]]] +[[[[8,6],[2,1]],[2,[8,6]]],[[[7,1],[3,9]],0]] +[[[[4,7],[2,7]],[[8,9],2]],[[[2,4],[7,2]],[3,7]]] +[[5,[2,2]],[[1,6],[[9,1],[5,0]]]] +[[5,[[1,2],[6,4]]],[6,8]] +[[[5,[1,7]],7],[7,[8,1]]] +[[1,9],[[0,3],[[6,7],[2,4]]]] +[1,[7,[[0,6],0]]] +[[[[5,7],9],[[3,2],7]],[[5,1],[9,9]]] +[[[[0,4],[9,6]],[[8,3],[7,4]]],[7,[6,2]]] +[[[[1,6],0],[[8,0],[3,4]]],[[3,[0,3]],4]] +[4,[[7,8],[4,[9,7]]]] +[[[2,[3,7]],5],[0,[9,9]]] +[[[2,0],[[5,8],[7,6]]],[[9,[6,2]],[3,2]]] +[[[3,1],3],[[[3,7],6],[9,8]]] +[[7,[[2,5],5]],[5,[3,[4,5]]]] +[[[6,7],6],[2,[[9,3],9]]] +[[[[5,6],7],[[3,2],5]],[[9,[4,3]],[3,8]]] +[0,7] +[[[4,6],[2,9]],[[[7,6],[5,1]],7]] +[[0,5],[[1,[4,1]],[[7,3],9]]] +[[[2,[3,8]],5],[[[5,9],8],[7,0]]] +[[[6,[8,6]],[[3,6],7]],[[2,1],[6,[7,5]]]] +[[2,[[6,3],[8,9]]],[[[5,6],4],[[7,0],1]]] +[[[[7,1],[5,6]],8],[[[8,9],4],[8,3]]] +[[[9,2],[1,0]],0] +[[5,[5,[8,5]]],4] +[[3,[5,[4,9]]],3] +[[8,[[7,7],6]],5] +[[4,[[5,1],1]],[1,[1,[9,8]]]] +[[[7,[3,6]],[[2,8],[4,7]]],[[[8,8],[4,0]],[2,4]]] +[[[[3,6],3],[0,9]],2] +[[2,8],[[8,[8,6]],[[1,1],[4,5]]]] +[[2,[1,[1,0]]],[[[6,2],[7,4]],[[7,1],6]]] +[3,[8,[7,[8,6]]]] +[[1,0],[[[0,4],[0,5]],[1,5]]] +[[[[5,0],4],[[7,8],[8,8]]],[[1,7],0]] +[1,[[[4,1],7],[6,[9,0]]]] +[[[1,8],2],[[5,5],[8,5]]] +[[4,[9,[0,6]]],[[[8,9],[4,5]],4]] +[[[[5,4],[1,7]],[[3,1],[7,9]]],[[[0,8],[4,7]],[[5,9],6]]] +[[[[8,0],9],4],[[7,[1,3]],5]] +[[[[5,0],6],[[6,1],8]],[[9,1],7]] +[[9,[6,[8,8]]],[7,[[7,1],6]]] +[[[5,[1,5]],[3,[4,2]]],[[[5,2],7],[[6,9],[2,8]]]] +[[[5,[5,5]],[5,7]],[4,[[2,9],7]]] +[[[[0,4],0],[[0,6],[3,0]]],[0,[[8,1],2]]] +[[[7,[4,6]],[[7,2],[4,6]]],[[[9,3],[4,9]],6]] +[[6,7],7] +[[[4,1],[8,[1,5]]],[[4,6],0]] +[[[4,[5,5]],5],[[0,[2,7]],[1,1]]] +[[[[0,1],3],[6,7]],[4,7]] +[[4,[6,4]],[[[9,8],1],[9,3]]] +[[[4,9],0],[[[7,0],[0,9]],[1,[1,0]]]] +[[[7,9],[[9,5],[6,9]]],[[0,[3,0]],[0,[5,9]]]] +[9,[[0,0],[[1,9],9]]] +[[[5,[0,5]],[[9,8],[9,5]]],[[0,[2,5]],7]] +[[[[5,8],6],9],[[[2,7],7],[[7,8],5]]] +[[8,[[4,7],6]],2] +[[[[7,1],[9,0]],[9,[1,7]]],[[8,[6,7]],[2,5]]] +[[4,[2,9]],8] +[[[[7,6],[5,3]],[5,[9,7]]],[[6,[8,1]],[[6,4],9]]] +[[7,[[7,8],4]],[[1,3],[4,[9,7]]]] +[[[6,[6,7]],[[2,8],3]],[7,[6,[0,3]]]] +[[9,8],[[0,[4,8]],[[9,1],1]]] +[[[[4,0],[5,9]],7],[6,[[5,9],[9,6]]]] +[[8,1],[1,[9,[8,3]]]] +[[[1,[5,1]],[6,7]],[[5,9],[2,[6,7]]]] +[[[3,7],[[7,8],1]],[[0,[6,3]],[8,0]]] +[[5,[[9,3],[1,2]]],7] +[[[1,[9,9]],3],[[6,4],[4,1]]] +[[6,[1,[3,6]]],[2,9]] +[[2,[0,2]],[5,[[9,4],[5,0]]]] +[[4,[[3,1],[7,0]]],[[9,1],[[5,5],[6,7]]]] +[[3,[[7,1],[3,4]]],[7,[9,[9,4]]]] +[[9,9],[[5,4],[[9,7],4]]] +[[[5,1],8],[[6,7],9]] +[[[0,[9,5]],[4,3]],[3,2]] +[[[6,[4,1]],[[8,7],[5,3]]],[[[1,2],5],[[9,2],5]]] +[[[[7,4],[9,0]],[[1,8],[2,9]]],[[5,[1,9]],[4,0]]] +[[[4,[3,8]],[[3,3],[2,8]]],[[[1,3],9],[[8,5],6]]] +[[[[6,4],[7,9]],[[7,6],8]],[7,[9,8]]] +[[7,[3,5]],7] +[[[[5,0],[2,3]],[3,7]],[[4,[6,3]],[7,[4,4]]]] +[[6,[3,[7,6]]],[[[5,8],[8,1]],[3,[1,5]]]] +[[8,[9,[5,2]]],2] +[[1,[5,4]],[[7,[8,0]],8]] +[[[[2,7],4],3],[[1,4],[8,4]]] +[3,[9,2]] \ No newline at end of file diff --git a/src/18.cs b/src/18.cs new file mode 100644 index 0000000..b377a13 --- /dev/null +++ b/src/18.cs @@ -0,0 +1,799 @@ +using System.Diagnostics; + +namespace aoc2021; + +internal class Day18 : Day +{ + class SFNum + { + public int? left; + public int? right; + public SFNum? leftNum; + public SFNum? rightNum; + public SFNum? owner; + + public bool exploding; + public bool splittingLeft; + public bool splittingRight; + + public override string ToString() => $"{(exploding ? "<+white>" : "")}" + + $"[" + + $"{leftNum?.ToString() ?? ""}{(splittingLeft ? "<+white>" : "")}{left?.ToString() ?? ""}{(splittingLeft ? "" : "")}" + + $"," + + $"{rightNum?.ToString() ?? ""}{(splittingRight ? "<+white>" : "")}{right?.ToString() ?? ""}{(splittingRight ? "" : "")}" + + $"]" + + $"{(exploding ? "" : "")}"; + + public SFNum() + { + + } + + public SFNum(SFNum other) + { + left = other.left; + right = other.right; + if (other.leftNum != null) + { + leftNum = new SFNum(other.leftNum) + { + owner = this + }; + } + if (other.rightNum != null) + { + rightNum = new SFNum(other.rightNum) + { + owner = this + }; + } + + exploding = other.exploding; + splittingLeft = other.splittingLeft; + splittingRight = other.splittingRight; + } + + public SFNum Root + { + get + { + SFNum curr = this; + while (curr.owner != null) + { + curr = curr.owner; + } + + return curr; + } + } + + public long Magnitude + { + get + { + long result = 0; + if (leftNum != null) + { + result += 3 * leftNum.Magnitude; + } + else + { + result += 3 * (int)left!; + } + + if (rightNum != null) + { + result += 2 * rightNum.Magnitude; + } + else + { + result += 2 * (int)right!; + } + + return result; + } + } + } + + [Conditional("DEBUG")] + private static void DoTests() + { + DoExplodeTests(); + DoSplitTests(); + DoMagnitudeTests(); + DoAddTests(); + DoReduceTests(); + DoPart1Tests(); + } + + private static void DoExplodeTests() + { + Logger.Log("test: explode"); + var tests = new List() + { + "[[[[[9,8],1],2],3],4]", + "[7,[6,[5,[4,[3,2]]]]]", + "[[6,[5,[4,[3,2]]]],1]", + "[[3,[2,[1,[7,3]]]],[6,[5,[4,[3,2]]]]]", + "[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]", + "[[[[[4,3],4],4],[7,[[8,4],9]]],[1,1]]", + "[[[[0,7],4],[7,[[8,4],9]]],[1,1]]", + "[[[[0,7],4],[[7,8],[0,[6,7]]]],[1,1]]", + }; + var exploded = new List() + { + "[[[[0,9],2],3],4]", + "[7,[6,[5,[7,0]]]]", + "[[6,[5,[7,0]]],3]", + "[[3,[2,[8,0]]],[9,[5,[4,[3,2]]]]]", + "[[3,[2,[8,0]]],[9,[5,[7,0]]]]", + "[[[[0,7],4],[7,[[8,4],9]]],[1,1]]", + "[[[[0,7],4],[15,[0,13]]],[1,1]]", + "[[[[0,7],4],[[7,8],[6,0]]],[8,1]]", + }; + + for (int i = 0; i < tests.Count; i++) + { + Logger.Log($"1.{(i + 1)}"); + SFNum test = ParseSFNum(tests[i]); + SFNum result = ParseSFNum(exploded[i]); + var didExplode = CheckExplodes(test); + if (!didExplode || test.ToString() != result.ToString()) + { + throw new Exception(); + } + Logger.Log(""); + } + + var twoStepTests = new List() + { + "[[[[[4,3],4],4],[7,[[8,4],9]]],[1,1]]", + }; + var twoStepExploded = new List() + { + "[[[[0,7],4],[15,[0,13]]],[1,1]]", + }; + + for (int i = 0; i < twoStepTests.Count; i++) + { + Logger.Log($"2.{(i + 1)}"); + SFNum test = ParseSFNum(twoStepTests[i]); + SFNum result = ParseSFNum(twoStepExploded[i]); + var didExplode = CheckExplodes(test); + if (!didExplode) + { + throw new Exception(); + } + didExplode = CheckExplodes(test); + if (!didExplode || test.ToString() != result.ToString()) + { + throw new Exception(); + } + Logger.Log(""); + } + } + + private static void DoSplitTests() + { + Logger.Log("test: split"); + var tests = new List() + { + "[[[[0,7],4],[15,[0,13]]],[1,1]]", + "[[[[0,7],4],[[7,8],[0,13]]],[1,1]]", + "[[[[7,7],[7,8]],[[9,5],[8,0]]],[[[9,10],20],[8,[9,0]]]]", + }; + var split = new List() + { + "[[[[0,7],4],[[7,8],[0,13]]],[1,1]]", + "[[[[0,7],4],[[7,8],[0,[6,7]]]],[1,1]]", + "[[[[7,7],[7,8]],[[9,5],[8,0]]],[[[9,[5,5]],20],[8,[9,0]]]]", + }; + + for (int i = 0; i < tests.Count; i++) + { + Logger.Log($"{(i + 1)}"); + SFNum test = ParseSFNum(tests[i]); + SFNum result = ParseSFNum(split[i]); + var didSplit = CheckSplits(test); + if (!didSplit || test.ToString() != result.ToString()) + { + throw new Exception(); + } + // todo: add tests verifying owners are correct + Logger.Log(""); + } + } + + private static void DoMagnitudeTests() + { + Logger.Log("test: magnitude"); + var tests = new List() + { + "[9,1]", + "[1,9]", + "[[9,1],[1,9]]", + "[[1,2],[[3,4],5]]", + "[[[[0,7],4],[[7,8],[6,0]]],[8,1]]", + "[[[[1,1],[2,2]],[3,3]],[4,4]]", + "[[[[3,0],[5,3]],[4,4]],[5,5]]", + "[[[[5,0],[7,4]],[5,5]],[6,6]]", + "[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]", + }; + var magnitudes = new List() + { + 29, + 21, + 129, + 143, + 1384, + 445, + 791, + 1137, + 3488, + }; + + for (int i = 0; i < tests.Count; i++) + { + Logger.Log($"{(i + 1)}"); + SFNum test = ParseSFNum(tests[i]); + var magnitude = test.Magnitude; + if (magnitude != magnitudes[i]) + { + throw new Exception(); + } + Logger.Log(""); + } + } + + private static void DoAddTests() + { + Logger.Log("test: add"); + var tests = new List>() + { + new() + { + "[[[[4,3],4],4],[7,[[8,4],9]]]", + "[1,1]", + }, + }; + var results = new List() + { + "[[[[[4,3],4],4],[7,[[8,4],9]]],[1,1]]", + }; + + for (int i = 0; i < tests.Count; i++) + { + Logger.Log($"{(i + 1)}"); + SFNum test = ParseSFNum(tests[i][0]); + for (int j = 1; j < tests[i].Count; j++) + { + test = Add(test, ParseSFNum(tests[i][j])); + } + SFNum result = ParseSFNum(results[i]); + if (test.ToString() != result.ToString()) + { + throw new Exception(); + } + if (result.leftNum!.owner != result || result.rightNum!.owner != result) + { + throw new Exception(); + } + Logger.Log(""); + } + } + + private static void DoReduceTests() + { + Logger.Log("test: reduce"); + var tests = new List() + { + "[[[[[4,3],4],4],[7,[[8,4],9]]],[1,1]]", + }; + var results = new List() + { + "[[[[0,7],4],[[7,8],[6,0]]],[8,1]]", + }; + + for (int i = 0; i < tests.Count; i++) + { + Logger.Log($"{(i + 1)}"); + SFNum test = ParseSFNum(tests[i]); + SFNum result = ParseSFNum(results[i]); + Reduce(test); + if (test.ToString() != result.ToString()) + { + throw new Exception(); + } + Logger.Log(""); + } + } + + private static void DoPart1Tests() + { + Logger.Log("test: add+reduce"); + var vals = new List>() + { + new() + { + ParseSFNum("[1,1]"), + ParseSFNum("[2,2]"), + ParseSFNum("[3,3]"), + ParseSFNum("[4,4]"), + }, + new() + { + ParseSFNum("[1,1]"), + ParseSFNum("[2,2]"), + ParseSFNum("[3,3]"), + ParseSFNum("[4,4]"), + ParseSFNum("[5,5]"), + }, + new() + { + ParseSFNum("[1,1]"), + ParseSFNum("[2,2]"), + ParseSFNum("[3,3]"), + ParseSFNum("[4,4]"), + ParseSFNum("[5,5]"), + ParseSFNum("[6,6]"), + }, + new() + { + ParseSFNum("[[[[4,3],4],4],[7,[[8,4],9]]]"), + ParseSFNum("[1,1]"), + }, + new() + { + ParseSFNum("[[[0,[4,5]],[0,0]],[[[4,5],[2,6]],[9,5]]]"), + ParseSFNum("[7,[[[3,7],[4,3]],[[6,3],[8,8]]]]"), + }, + new() + { + ParseSFNum("[[[[4,0],[5,4]],[[7,7],[6,0]]],[[8,[7,7]],[[7,9],[5,0]]]]"), + ParseSFNum("[[2,[[0,8],[3,4]]],[[[6,7],1],[7,[1,6]]]]"), + }, + new() + { + ParseSFNum("[[[[6,7],[6,7]],[[7,7],[0,7]]],[[[8,7],[7,7]],[[8,8],[8,0]]]]"), + ParseSFNum("[[[[2,4],7],[6,[0,5]]],[[[6,8],[2,8]],[[2,1],[4,5]]]]"), + }, + new() + { + ParseSFNum("[[[[7,0],[7,7]],[[7,7],[7,8]]],[[[7,7],[8,8]],[[7,7],[8,7]]]]"), + ParseSFNum("[7,[5,[[3,8],[1,4]]]]"), + }, + new() + { + ParseSFNum("[[[[7,7],[7,8]],[[9,5],[8,7]]],[[[6,8],[0,8]],[[9,9],[9,0]]]]"), + ParseSFNum("[[2,[2,2]],[8,[8,1]]]"), + }, + new() + { + ParseSFNum("[[[[6,6],[6,6]],[[6,0],[6,7]]],[[[7,7],[8,9]],[8,[8,1]]]]"), + ParseSFNum("[2,9]"), + }, + new() + { + ParseSFNum("[[[[6,6],[7,7]],[[0,7],[7,7]]],[[[5,5],[5,6]],9]]"), + ParseSFNum("[1,[[[9,3],9],[[9,0],[0,7]]]]"), + }, + new() + { + ParseSFNum("[[[[7,8],[6,7]],[[6,8],[0,8]]],[[[7,7],[5,0]],[[5,5],[5,6]]]]"), + ParseSFNum("[[[5,[7,4]],7],1]"), + }, + new() + { + ParseSFNum("[[[[7,7],[7,7]],[[8,7],[8,7]]],[[[7,0],[7,7]],9]]"), + ParseSFNum("[[[[4,2],2],6],[8,7]]"), + }, + }; + var results = new List() + { + ParseSFNum("[[[[1,1],[2,2]],[3,3]],[4,4]]"), + ParseSFNum("[[[[3,0],[5,3]],[4,4]],[5,5]]"), + ParseSFNum("[[[[5,0],[7,4]],[5,5]],[6,6]]"), + ParseSFNum("[[[[0,7],4],[[7,8],[6,0]]],[8,1]]"), + ParseSFNum("[[[[4,0],[5,4]],[[7,7],[6,0]]],[[8,[7,7]],[[7,9],[5,0]]]]"), + ParseSFNum("[[[[6,7],[6,7]],[[7,7],[0,7]]],[[[8,7],[7,7]],[[8,8],[8,0]]]]"), + ParseSFNum("[[[[7,0],[7,7]],[[7,7],[7,8]]],[[[7,7],[8,8]],[[7,7],[8,7]]]]"), + ParseSFNum("[[[[7,7],[7,8]],[[9,5],[8,7]]],[[[6,8],[0,8]],[[9,9],[9,0]]]]"), + ParseSFNum("[[[[6,6],[6,6]],[[6,0],[6,7]]],[[[7,7],[8,9]],[8,[8,1]]]]"), + ParseSFNum("[[[[6,6],[7,7]],[[0,7],[7,7]]],[[[5,5],[5,6]],9]]"), + ParseSFNum("[[[[7,8],[6,7]],[[6,8],[0,8]]],[[[7,7],[5,0]],[[5,5],[5,6]]]]"), + ParseSFNum("[[[[7,7],[7,7]],[[8,7],[8,7]]],[[[7,0],[7,7]],9]]"), + ParseSFNum("[[[[8,7],[7,7]],[[8,6],[7,7]]],[[[0,7],[6,6]],[8,7]]]"), + }; + + for (int i = 0; i < vals.Count; i++) + { + Logger.Log($"{(i + 1)}"); + SFNum curr = vals[i][0]; + for (int j = 1; j < vals[i].Count; j++) + { + curr = Add(curr, vals[i][j]); + Reduce(curr); + } + + if (curr.ToString() != results[i].ToString()) + { + Logger.Log($" expected: {results[i]}"); + throw new Exception(); + } + Logger.Log(""); + } + } + + internal override void Go() + { + DoTests(); + + var lines = Util.ReadAllLines("inputs/18.txt"); + List nums = new(); + foreach (var line in lines) + { + SFNum parsed = ParseSFNum(line); + nums.Add(parsed); + } + Part1(nums); + Part2(nums); + } + + private static SFNum ParseSFNum(string line) + { + int phase = 0; + SFNum root = new(); + SFNum? curr = null; + string numStr = string.Empty; + + var assignNum = () => + { + if (numStr.Length == 0) + { + return; + } + + var val = Convert.ToInt32(numStr); + if (phase == 0) + { + curr!.left = val; + } + else + { + curr!.right = val; + } + + numStr = string.Empty; + }; + + foreach (var ch in line) + { + switch (ch) + { + case '[': + if (curr == null) + { + curr = root; + break; + } + + if (phase == 0) + { + curr!.leftNum = new() + { + owner = curr, + }; + curr = curr.leftNum; + } + else + { + curr!.rightNum = new() + { + owner = curr, + }; + curr = curr.rightNum; + } + phase = 0; + break; + + case ',': + assignNum(); + phase = 1; + break; + + case ']': + assignNum(); + curr = curr!.owner; + break; + + default: + numStr += ch; + break; + } + } + + return root; + } + + private static void Part1(IEnumerable nums) + { + using var t = new Timer(); + + SFNum? curr = null; + foreach (var num in nums) + { + if (curr == null) + { + curr = num; + continue; + } + curr = Add(curr, num); + Reduce(curr); + } + + t.Stop(); + Logger.Log($"<+black>> part1: {curr} -> <+white>{curr!.Magnitude}"); + } + + private static void Part2(IEnumerable nums) + { + using var t = new Timer(); + + List<(SFNum first, SFNum second, SFNum reduced, long magnitude)> totals = new(); + for (int i = 0; i < nums.Count() - 1; i++) + { + for (int j = i + 1; j < nums.Count(); j++) + { + var first = nums.ElementAt(i); + var second = nums.ElementAt(j); + var curr = Add(first, second); + Reduce(curr); + totals.Add((first, second, curr, curr.Magnitude)); + + curr = Add(second, first); + Reduce(curr); + totals.Add((second, first, curr, curr.Magnitude)); + } + } + + var highest = totals.MaxBy(x => x.magnitude); + + t.Stop(); + Logger.Log($"<+black>> part2: {highest.first} + {highest.second} = {highest.reduced} -> <+white>{highest.magnitude}"); + } + + private static SFNum Add(SFNum first, SFNum second) + { + var result = new SFNum() + { + leftNum = new SFNum(first), + rightNum = new SFNum(second), + }; + result.leftNum.owner = result; + result.rightNum.owner = result; + + return result; + } + + private static void Reduce(SFNum num) + { + for (int i = 0; i < 10000; i++) + { + if (CheckExplodes(num)) + { + continue; + } + + if (CheckSplits(num)) + { + continue; + } + + return; + } + + throw new Exception(); + } + + private static bool CheckExplodes(SFNum num, int depth = 0) + { +#if DEBUG + var initial = new SFNum() + { + left = num.left, + right = num.right, + leftNum = num.leftNum, + rightNum = num.rightNum, + owner = num.owner, + }; +#endif + if (num.leftNum == null && num.rightNum == null && depth >= 4) + { + Explode(num); +#if DEBUG + if (initial.Root.ToString() == num.Root.ToString()) + { + throw new Exception(); + } +#endif + return true; + } + + if (num.leftNum != null) + { + if (CheckExplodes(num.leftNum, depth + 1)) + { + return true; + } + } + if (num.rightNum != null) + { + if (CheckExplodes(num.rightNum, depth + 1)) + { + return true; + } + } + + return false; + } + + private static void Explode(SFNum num) + { +#if DEBUG + num.exploding = true; + Logger.Log($"exploding: {num.Root}"); +#endif + + var curr = num; + var last = num; + while (true) + { + curr = curr.owner; + if (curr == null) + { + break; + } + + if (last == curr.leftNum) + { + last = curr; + continue; + } + + if (curr.leftNum != null) + { + curr = curr.leftNum; + while (curr.rightNum != null) + { + curr = curr.rightNum; + } + curr.right += num.left; + } + else + { + curr.left += num.left; + } + break; + } + + curr = num; + last = num; + while (true) + { + curr = curr.owner; + if (curr == null) + { + break; + } + + if (last == curr.rightNum) + { + last = curr; + continue; + } + + if (curr.rightNum != null) + { + curr = curr.rightNum; + while (curr.leftNum != null) + { + curr = curr.leftNum; + } + curr.left += num.right; + } + else + { + curr.right += num.right; + } + break; + } + + if (num == num.owner?.leftNum) + { + num.owner.leftNum = null; + num.owner.left = 0; + } + else if (num == num.owner?.rightNum) + { + num.owner.rightNum = null; + num.owner.right = 0; + } + +#if DEBUG + Logger.Log($" -> {num.Root}"); +#endif + + num.owner = null; + } + + private static bool CheckSplits(SFNum num) + { + if (num.leftNum != null) + { + if (CheckSplits(num.leftNum)) + { + return true; + } + } + + if (Split(num)) + { + return true; + } + + if (num.rightNum != null) + { + if (CheckSplits(num.rightNum)) + { + return true; + } + } + + return false; + } + + private static bool Split(SFNum num) + { + if (num.left != null && num.left >= 10) + { + num.splittingLeft = true; +#if DEBUG + Logger.Log($"splitting: {num.Root}"); +#endif + num.leftNum = new() + { + left = (int)Math.Floor((int)num.left / 2.0), + right = (int)Math.Ceiling((int)num.left / 2.0), + owner = num, + }; + num.left = null; + num.splittingLeft = false; +#if DEBUG + Logger.Log($" -> {num.Root}"); +#endif + return true; + } + + if (num.right != null && num.right >= 10) + { + num.splittingRight = true; +#if DEBUG + Logger.Log($"splitting: {num.Root}"); +#endif + num.rightNum = new() + { + left = (int)Math.Floor((int)num.right / 2.0), + right = (int)Math.Ceiling((int)num.right / 2.0), + owner = num, + }; + num.right = null; + num.splittingRight = false; +#if DEBUG + Logger.Log($" -> {num.Root}"); +#endif + return true; + } + + return false; + } +} diff --git a/src/main.cs b/src/main.cs index 666915a..3c43b1a 100644 --- a/src/main.cs +++ b/src/main.cs @@ -35,7 +35,8 @@ else "14" => new Day14(), "15" => new Day15(), "16" => new Day16(), - _ => new Day17(), + "17"=> new Day17(), + _ => new Day18(), }; day.Go(); }