|
1 | 1 | package jurisk.adventofcode.y2024
|
2 | 2 |
|
3 |
| -import jurisk.utils.FileInput._ |
4 |
| -import jurisk.utils.Parsing.StringOps |
| 3 | +import jurisk.math.pow |
| 4 | +import jurisk.utils.Memoize |
| 5 | +import mouse.all.booleanSyntaxMouse |
| 6 | + |
| 7 | +import scala.math.log10 |
5 | 8 |
|
6 | 9 | object Advent11 {
|
7 |
| - type Input = List[Command] |
| 10 | + type Input = Vector[Long] |
8 | 11 | type N = Long
|
9 | 12 |
|
10 |
| - sealed trait Command extends Product with Serializable |
11 |
| - object Command { |
12 |
| - case object Noop extends Command |
13 |
| - final case class Something( |
14 |
| - values: List[N] |
15 |
| - ) extends Command |
16 |
| - final case class Other(value: String) extends Command |
17 |
| - |
18 |
| - def parse(s: String): Command = |
19 |
| - s match { |
20 |
| - case "noop" => Noop |
21 |
| - case s"something $rem" => Something(rem.extractLongList) |
22 |
| - case s if s.nonEmpty => Other(s) |
23 |
| - case _ => s.failedToParse |
24 |
| - } |
| 13 | + private def halves(n: Long): Option[(Long, Long)] = { |
| 14 | + val digits = log10(n.doubleValue).toInt + 1 |
| 15 | + |
| 16 | + (digits % 2 == 0).option { |
| 17 | + val halfLength = digits / 2 |
| 18 | + val divisor = pow(10, halfLength) |
| 19 | + val left = n / divisor |
| 20 | + val right = n % divisor |
| 21 | + (left, right) |
| 22 | + } |
25 | 23 | }
|
26 | 24 |
|
27 |
| - def parse(input: String): Input = |
28 |
| - input.parseLines(Command.parse) |
| 25 | + def blink(data: Input): Input = |
| 26 | + data.flatMap { n => |
| 27 | + if (n == 0) { |
| 28 | + Vector(1) |
| 29 | + } else { |
| 30 | + halves(n) match { |
| 31 | + case Some((left, right)) => |
| 32 | + Vector(left, right) |
| 33 | + case None => |
| 34 | + Vector(n * 2024) |
| 35 | + } |
| 36 | + } |
| 37 | + } |
29 | 38 |
|
30 |
| - def part1(data: Input): N = |
31 |
| - 0 |
| 39 | + private val memoizedSolve: (Long, Int) => N = Memoize.memoize2(solve) |
| 40 | + private def solve(n: Long, blinks: Int): Long = |
| 41 | + if (blinks <= 0) { |
| 42 | + 1 |
| 43 | + } else { |
| 44 | + if (n == 0) { |
| 45 | + memoizedSolve(1, blinks - 1) |
| 46 | + } else { |
| 47 | + halves(n) match { |
| 48 | + case Some((left, right)) => |
| 49 | + memoizedSolve(left, blinks - 1) + memoizedSolve(right, blinks - 1) |
| 50 | + case None => |
| 51 | + memoizedSolve(n * 2024, blinks - 1) |
| 52 | + } |
| 53 | + } |
| 54 | + } |
32 | 55 |
|
33 |
| - def part2(data: Input): N = |
34 |
| - 0 |
| 56 | + def blinkNTimes(data: Input, blinks: Int): Input = |
| 57 | + (0 until blinks).foldLeft(data)((acc, _) => blink(acc)) |
35 | 58 |
|
36 |
| - def parseFile(fileName: String): Input = |
37 |
| - parse(readFileText(fileName)) |
| 59 | + def part1(data: Input, blinks: Int = 25): N = |
| 60 | + blinkNTimes(data, blinks).size |
38 | 61 |
|
39 |
| - def fileName(suffix: String): String = |
40 |
| - s"2024/11$suffix.txt" |
| 62 | + def part2(data: Input, blinks: Int = 75): N = |
| 63 | + data.map(n => memoizedSolve(n, blinks)).sum |
41 | 64 |
|
42 |
| - def main(args: Array[String]): Unit = { |
43 |
| - val realData: Input = parseFile(fileName("")) |
| 65 | + val realData: Input = Vector(6563348, 67, 395, 0, 6, 4425, 89567, 739318) |
44 | 66 |
|
| 67 | + def main(args: Array[String]): Unit = { |
45 | 68 | println(s"Part 1: ${part1(realData)}")
|
46 | 69 | println(s"Part 2: ${part2(realData)}")
|
47 | 70 | }
|
|
0 commit comments