forked from matelaszlo/advent-of-code-scala
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay08.scala
67 lines (52 loc) · 2.49 KB
/
Day08.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.lmat.adventofcode.year2023
import com.lmat.adventofcode.SimpleCommonPuzzle
import com.lmat.adventofcode.year2023.Day08Definitions._
import com.lmat.util.Files.readResource
import com.lmat.util.Maths
object Day08Definitions {
case class Network(steps: List[Direction], nodes: Map[String, Map[Direction, String]])
sealed trait Direction
case object Left extends Direction
case object Right extends Direction
}
object Day08 extends SimpleCommonPuzzle[Network, Long, Long] {
override def parse(resource: String): Network =
parseNetwork(readResource(resource).toList)
def parseNetwork(rows: List[String]): Network = {
def directions(left: String, right: String): Map[Direction, String] =
Map(Left -> left, Right -> right)
val steps = rows.head.toCharArray.flatMap(parseDirection).toList
val nodes = rows.drop(2).flatMap(parseNode).groupBy(_._1).map { case (k, v) => (k, directions(v.head._2, v.head._3)) }
Network(steps, nodes)
}
def parseDirection(c: Char): Option[Direction] = c match {
case 'R' => Some(Right)
case 'L' => Some(Left)
case _ => None
}
def parseNode(row: String): Option[(String, String, String)] = {
val pattern = s"(.*) = \\((.*), (.*)\\).*".r
row match {
case pattern(current, left, right) => Some(current, left, right)
case _ => None
}
}
case class State(currentNode: String, currentStep: Int, total: Long)
override def part1(network: Network): Long = simulate(network)("AAA", _ == "ZZZ").total
// LCM works as there is only 1 XXZ in each cycle, the directions from an XXA and an XXZ node pair consistently the same just inverse
override def part2(network: Network): Long = {
println(s"Start nodes: ${network.nodes.filter(_._1.endsWith("A"))}")
println(s"Possible end nodes: ${network.nodes.filter(_._1.endsWith("Z"))}")
val simulations = network.nodes.keySet.filter(_.endsWith("A")).map(start => (start, simulate(network)(start, _.endsWith("Z"))))
println(s"Simulations: $simulations")
simulations.map(_._2.total).reduce(Maths.lcm)
}
def simulate(network: Network)(start: String, isEnd: String => Boolean): State =
LazyList.iterate(State(start, 0, 0))(next(network)).find(state => isEnd(state.currentNode)).get
def next(network: Network)(state: State): State = {
val nextNode = network.nodes(state.currentNode)(network.steps(state.currentStep))
val nextStep = (state.currentStep + 1) % network.steps.size
val total = state.total + 1
State(nextNode, nextStep, total)
}
}