From 25bc83d0110529f86b19b7c6de84e0f118f6add4 Mon Sep 17 00:00:00 2001 From: Alexander Gehrke Date: Sun, 19 Dec 2021 04:44:12 +0100 Subject: [PATCH] Day 18: Fish use trees for numbers --- input/2021/day18-sample1.txt | 7 ++ input/2021/day18-sample2.txt | 2 + input/2021/day18-sample3.txt | 1 + input/2021/day18-sample4.txt | 1 + input/2021/day18-sample5.txt | 1 + input/2021/day18-sample6.txt | 10 ++ input/2021/day18-sample7.txt | 10 ++ input/2021/day18.txt | 100 +++++++++++++++++ .../scala/de.qwertyuiop.aoc/2021/day18.scala | 101 +++++++++++++++++- 9 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 input/2021/day18-sample1.txt create mode 100644 input/2021/day18-sample2.txt create mode 100644 input/2021/day18-sample3.txt create mode 100644 input/2021/day18-sample4.txt create mode 100644 input/2021/day18-sample5.txt create mode 100644 input/2021/day18-sample6.txt create mode 100644 input/2021/day18-sample7.txt create mode 100644 input/2021/day18.txt diff --git a/input/2021/day18-sample1.txt b/input/2021/day18-sample1.txt new file mode 100644 index 0000000..93e9d14 --- /dev/null +++ b/input/2021/day18-sample1.txt @@ -0,0 +1,7 @@ +[1,2] +[[1,2],3] +[9,[8,7]] +[[1,9],[8,5]] +[[[[1,2],[3,4]],[[5,6],[7,8]]],9] +[[[9,[3,8]],[[0,9],6]],[[[3,7],[4,9]],3]] +[[[[1,3],[5,3]],[[1,3],[8,7]]],[[[4,9],[6,9]],[[8,2],[7,3]]]] diff --git a/input/2021/day18-sample2.txt b/input/2021/day18-sample2.txt new file mode 100644 index 0000000..61b0df6 --- /dev/null +++ b/input/2021/day18-sample2.txt @@ -0,0 +1,2 @@ +[[[[4,3],4],4],[7,[[8,4],9]]] +[1,1] diff --git a/input/2021/day18-sample3.txt b/input/2021/day18-sample3.txt new file mode 100644 index 0000000..bdb3eda --- /dev/null +++ b/input/2021/day18-sample3.txt @@ -0,0 +1 @@ +[[[[1,1],[2,2]],[3,3]],[4,4]] diff --git a/input/2021/day18-sample4.txt b/input/2021/day18-sample4.txt new file mode 100644 index 0000000..d1c17ef --- /dev/null +++ b/input/2021/day18-sample4.txt @@ -0,0 +1 @@ +[[[[3,0],[5,3]],[4,4]],[5,5]] diff --git a/input/2021/day18-sample5.txt b/input/2021/day18-sample5.txt new file mode 100644 index 0000000..0b25ee4 --- /dev/null +++ b/input/2021/day18-sample5.txt @@ -0,0 +1 @@ +[[[[5,0],[7,4]],[5,5]],[6,6]] diff --git a/input/2021/day18-sample6.txt b/input/2021/day18-sample6.txt new file mode 100644 index 0000000..70e9071 --- /dev/null +++ b/input/2021/day18-sample6.txt @@ -0,0 +1,10 @@ +[[[0,[4,5]],[0,0]],[[[4,5],[2,6]],[9,5]]] +[7,[[[3,7],[4,3]],[[6,3],[8,8]]]] +[[2,[[0,8],[3,4]]],[[[6,7],1],[7,[1,6]]]] +[[[[2,4],7],[6,[0,5]]],[[[6,8],[2,8]],[[2,1],[4,5]]]] +[7,[5,[[3,8],[1,4]]]] +[[2,[2,2]],[8,[8,1]]] +[2,9] +[1,[[[9,3],9],[[9,0],[0,7]]]] +[[[5,[7,4]],7],1] +[[[[4,2],2],6],[8,7]] diff --git a/input/2021/day18-sample7.txt b/input/2021/day18-sample7.txt new file mode 100644 index 0000000..1368dc4 --- /dev/null +++ b/input/2021/day18-sample7.txt @@ -0,0 +1,10 @@ +[[[0,[5,8]],[[1,7],[9,6]]],[[4,[1,2]],[[1,4],2]]] +[[[5,[2,8]],4],[5,[[9,9],0]]] +[6,[[[6,2],[5,6]],[[7,6],[4,7]]]] +[[[6,[0,7]],[0,9]],[4,[9,[9,0]]]] +[[[7,[6,4]],[3,[1,3]]],[[[5,5],1],9]] +[[6,[[7,3],[3,2]]],[[[3,8],[5,7]],4]] +[[[[5,4],[7,7]],8],[[8,3],8]] +[[9,3],[[9,9],[6,[4,9]]]] +[[2,[[7,7],7]],[[5,8],[[9,3],[0,2]]]] +[[[[5,2],5],[8,[3,7]]],[[5,[7,5]],[4,4]]] diff --git a/input/2021/day18.txt b/input/2021/day18.txt new file mode 100644 index 0000000..de44c92 --- /dev/null +++ b/input/2021/day18.txt @@ -0,0 +1,100 @@ +[[[[2,8],[4,6]],[[2,4],[9,4]]],[[[0,6],[4,6]],[1,6]]] +[7,[[5,7],1]] +[[[[8,8],7],5],[[[5,6],1],6]] +[[[8,5],[[0,0],[4,9]]],[2,8]] +[7,[[5,2],[[3,0],[7,7]]]] +[[6,[6,8]],[3,[5,2]]] +[6,[[[8,9],[9,9]],[3,8]]] +[[[1,[0,2]],[7,[3,0]]],8] +[[9,6],6] +[[[2,3],1],[9,[3,7]]] +[5,[[[5,8],3],9]] +[[[[8,8],3],[2,2]],[2,3]] +[[[4,9],3],[[[7,3],8],5]] +[[[3,5],[3,7]],[[[9,7],9],[9,[7,8]]]] +[[7,1],8] +[0,[[[6,8],[1,1]],[1,[5,8]]]] +[[[[2,2],[9,5]],[0,[1,0]]],[4,[[2,4],4]]] +[[[[2,5],[7,3]],[7,6]],[[6,[4,4]],[3,8]]] +[[3,[[7,9],2]],[[0,[4,4]],[[6,9],9]]] +[[[7,7],[[1,4],[1,6]]],[7,[[6,3],6]]] +[[0,8],[[[1,6],2],4]] +[[0,[[2,7],[0,4]]],[[[3,8],[7,7]],5]] +[[[[9,9],[1,3]],[9,[4,3]]],[[[3,4],[6,4]],1]] +[[[9,[0,9]],[2,[7,6]]],[2,[[1,9],[3,3]]]] +[[4,[5,6]],[[[1,5],6],[[1,5],[5,2]]]] +[1,[[3,[2,1]],5]] +[[4,[3,8]],[3,[6,3]]] +[[7,1],[[3,[6,0]],[5,[1,1]]]] +[[8,7],[[[0,1],[2,6]],[5,[4,7]]]] +[9,[[[1,6],[8,9]],[6,6]]] +[4,9] +[[[[0,8],[8,5]],9],[7,[1,3]]] +[[[[8,5],0],[[4,6],4]],[8,4]] +[[[[8,9],8],[[3,1],[7,6]]],2] +[[[[6,3],0],[2,[4,8]]],[[[0,3],[3,5]],4]] +[0,[[9,[0,6]],5]] +[[[[1,9],[2,7]],[[4,0],[9,9]]],[[8,[3,6]],[3,4]]] +[[[[0,7],[8,4]],1],[[8,3],[[3,5],[8,0]]]] +[[[[3,5],4],[0,9]],[[[1,7],5],[9,[8,0]]]] +[[[8,[6,8]],[[3,7],[0,8]]],[[[5,2],[1,7]],[9,5]]] +[[[[5,1],[0,7]],4],[0,4]] +[[[[9,8],[3,9]],[[0,6],3]],[[[9,1],[8,7]],2]] +[[9,[[0,3],6]],[[3,4],[[8,9],5]]] +[[1,[1,8]],[[6,[4,2]],1]] +[7,[[1,[5,2]],[[9,7],0]]] +[0,[8,6]] +[1,4] +[[8,[4,1]],[[[4,0],[0,0]],[7,[3,4]]]] +[2,[[1,[1,8]],[[3,4],1]]] +[[8,[[1,2],[3,1]]],[[[4,4],[7,9]],1]] +[[4,[0,[6,4]]],[9,[0,[1,2]]]] +[[6,[3,1]],[[7,8],[8,[2,5]]]] +[[[2,[3,3]],[[6,4],[9,4]]],[[[1,5],[7,4]],[0,6]]] +[[[[8,0],3],[[4,0],3]],[[7,5],4]] +[[[2,[4,3]],[[2,1],5]],1] +[[[8,1],[0,4]],[9,[[1,4],[9,0]]]] +[[[5,0],[[7,7],9]],[[6,[6,2]],7]] +[[[[5,9],0],[[4,6],[3,8]]],[6,[6,5]]] +[[[6,[7,8]],[5,3]],[[3,[6,5]],[[8,7],[4,7]]]] +[[9,[[8,7],4]],[[[6,3],0],[[2,3],[5,9]]]] +[[[[1,8],6],1],[[[7,8],4],[7,2]]] +[[[[7,1],[6,2]],[[7,8],2]],0] +[[[4,5],[0,3]],[[2,4],1]] +[[[9,1],7],[[[8,8],[0,7]],[8,0]]] +[[5,[[7,5],[7,5]]],[3,[4,8]]] +[[7,[1,0]],[[3,[1,5]],0]] +[[[5,1],[[5,2],[7,3]]],[[7,[3,9]],9]] +[5,[1,[[9,9],[3,0]]]] +[[2,0],[9,[6,[3,3]]]] +[[[[0,4],[4,8]],[[1,9],[5,8]]],[[[7,0],5],[5,1]]] +[[[[1,5],[9,2]],[6,[3,6]]],[4,[1,[1,5]]]] +[[[[1,4],[4,6]],[[5,5],[3,5]]],[[[7,1],4],[[0,7],4]]] +[[6,[3,5]],1] +[8,[[1,[0,7]],[[2,5],6]]] +[[[[1,6],3],[[9,7],9]],[[7,8],3]] +[[[[9,9],[2,0]],0],[1,4]] +[[[[1,3],[5,1]],[[0,4],2]],0] +[[3,2],[7,[[9,3],8]]] +[[9,0],[4,[[8,7],[5,5]]]] +[[[[7,4],8],[[4,4],1]],9] +[[9,[[7,9],1]],[[[6,5],7],[[2,5],2]]] +[7,2] +[[[6,6],[[9,4],4]],6] +[[1,[[5,0],3]],[5,[4,4]]] +[[[3,2],[[4,6],6]],[[3,[9,5]],[[0,2],[4,6]]]] +[5,[[0,[3,0]],[7,[7,9]]]] +[[[[0,4],[1,5]],4],[8,[[4,7],8]]] +[[[[9,1],0],0],4] +[[[[8,4],[4,2]],[9,[1,7]]],[6,3]] +[2,[[[8,3],2],[[3,1],8]]] +[[[[9,0],[7,8]],[[2,7],[0,3]]],[[[8,5],3],[9,[6,8]]]] +[[[[8,9],[9,1]],[4,[0,1]]],[[[7,8],2],2]] +[[[[2,2],[4,1]],[2,[2,8]]],[[[6,5],1],9]] +[[[[3,0],7],7],[[[9,3],7],4]] +[[[[7,5],1],3],[[[0,7],7],[[2,6],[9,9]]]] +[[[[5,2],8],[9,[8,8]]],[2,[[0,8],[5,6]]]] +[[[[7,7],[1,2]],[6,6]],[8,[5,8]]] +[[7,[4,[8,9]]],[[4,[7,2]],8]] +[[[6,4],[7,7]],[[[3,7],0],[0,1]]] +[[1,[5,9]],[8,[4,6]]] diff --git a/src/main/scala/de.qwertyuiop.aoc/2021/day18.scala b/src/main/scala/de.qwertyuiop.aoc/2021/day18.scala index a4c0eb2..701b8c0 100644 --- a/src/main/scala/de.qwertyuiop.aoc/2021/day18.scala +++ b/src/main/scala/de.qwertyuiop.aoc/2021/day18.scala @@ -2,5 +2,104 @@ package de.qwertyuiop.aoc.`2021` import de.qwertyuiop.aoc.lib.* import cats.*, cats.implicits.given +import atto.*, Atto.* -def day18(using InputSource): Unit = ??? +def day18(using InputSource): Unit = + import Snailfish.* + val lines = input() + val snumbers = lines.map(i => snailfishNumber.parseOnly(i).option.get) + val result = snumbers.tail.foldLeft(snumbers.head)(_ + _) + //val sum = Pair(snumbers(0), snumbers(1)) + //tryExplode(sum) + //println(reduce(snailfishNumber.parseOnly("[[[[0,7],4],[7,[[8,4],9]]],[1,1]]").option.get)) + println(s"Reduced: $result") + println(s"Magnitude: ${result.magnitude}") + + val maxMag = snumbers.combinations(2).map{ case List(a, b) => (a + b).magnitude max (b + a).magnitude }.max + println(s"maximum: $maxMag") + + +enum Dir: + case L, R +extension (d: Dir) + def opposite: Dir = if d == Dir.L then Dir.R else Dir.L + + +trait SnailfishNumber: + def replace(path: List[Dir], elem: SnailfishNumber): SnailfishNumber = + modify(path)(_ => elem) + + def modify(path: List[Dir])(f: SnailfishNumber => SnailfishNumber): SnailfishNumber = + (this, path) match + case (Pair(l, r), Dir.L :: Nil) => Pair(f(l), r) + case (Pair(l, r), Dir.R :: Nil) => Pair(l, f(r)) + case (Pair(l, r), Dir.L :: rest) => Pair(l.modify(rest)(f), r) + case (Pair(l, r), Dir.R :: rest) => Pair(l, r.modify(rest)(f)) + case (snailnum, _) => f(snailnum) // either Lit or end of path + + def magnitude: Int = + this match + case Pair(l, r) => l.magnitude * 3 + r.magnitude * 2 + case Lit(x) => x + + infix def +(right: SnailfishNumber): SnailfishNumber = + Snailfish.reduce(Pair(this, right)) + +case class Lit(value: Int) extends SnailfishNumber: + override def toString = s"$value" +case class Pair(left: SnailfishNumber, right: SnailfishNumber) extends SnailfishNumber: + override def toString = s"[$left,$right]" + +object Snailfish: + + def snailfishNumber: Parser[SnailfishNumber] = + ( + int.map(Lit.apply) || + squareBrackets( + (snailfishNumber <~ char(',')) ~ snailfishNumber + ).map(Pair.apply.tupled) + ).map(_.merge) + + + def reduce(sn: SnailfishNumber): SnailfishNumber = + tryExplode(sn).orElse(trySplit(sn)).map(reduce).getOrElse(sn) + + def trySplit(sn: SnailfishNumber): Option[SnailfishNumber] = + sn match + case Pair(l, r) => + trySplit(l).map(newL => Pair(newL, r)) + .orElse(trySplit(r).map(newR => Pair(l, newR))) + case Lit(x) if x < 10 => None + case Lit(x) => Some(Pair(Lit(x / 2), Lit(x / 2 + x % 2))) + + + def tryExplode(sn: SnailfishNumber): Option[SnailfishNumber] = + def findPath(path: List[Dir], depth: Int, elem: SnailfishNumber): Option[(List[Dir], Int, Int)] = elem match + case Pair(l, r) if depth < 4 => + val left = findPath(Dir.L :: path, depth + 1, l) + if left.nonEmpty then left + else findPath(Dir.R :: path, depth + 1, r) + case Pair(Lit(l),Lit(r)) => (path.reverse, l, r).some + case Lit(_) => None + + def nextNeighbour(dir: Dir, path: List[Dir]): Option[List[Dir]] = + val rpath = path.reverse + val pos = rpath.indexOf(dir.opposite) + if pos < 0 then None + else + val (pre, _ :: post) = rpath.splitAt(pos) + Some((pre.map(_ => dir.opposite) ++ (dir:: post)).reverse) + + def add(value: Int, dir: Dir)(snail: SnailfishNumber) : SnailfishNumber = snail match + case Lit(x) => Lit(x + value) + case Pair(l, r) => + if dir == Dir.R then Pair(l, add(value, dir)(r)) + else Pair(add(value, dir)(l), r) + + findPath(List(), 0, sn).map { (path, l, r) => + val zeroed = sn.replace(path, Lit(0)) + val left = nextNeighbour(Dir.L, path) + val right = nextNeighbour(Dir.R, path) + val leftAdded = left.map(p => zeroed.modify(p)(add(l, Dir.R))).getOrElse(zeroed) + right.map(p => leftAdded.modify(p)(add(r, Dir.L))).getOrElse(leftAdded) + }