-
Notifications
You must be signed in to change notification settings - Fork 0
/
day_09.scala
90 lines (75 loc) · 2.44 KB
/
day_09.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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
import scala.io.Source
object Day09 {
enum Direction { case Left, Right, Up, Down }
case class Coord(x: Int, y: Int) {
def step(direction: Direction): Coord = direction match {
case Direction.Left => copy(x = x - 1)
case Direction.Right => copy(x = x + 1)
case Direction.Up => copy(y = y - 1)
case Direction.Down => copy(y = y + 1)
}
def seekTowards(other: Coord): Coord = {
def isTouching(other: Coord): Boolean =
(x - other.x).abs <= 1 && (y - other.y).abs <= 1
if (isTouching(other)) {
this
} else {
val xNew =
if (other.x > x) { x + 1 }
else if (other.x < x) { x - 1 }
else { x }
val yNew =
if (other.y > y) { y + 1 }
else if (other.y < y) { y - 1 }
else { y }
Coord(xNew, yNew)
}
}
}
case class RopeState(knots: List[Coord], tailVisited: Set[Coord]) {
def step(direction: Direction): RopeState = {
val updatedHead = knots.head.step(direction)
val updatedKnots =
knots.tail
.foldLeft(List(updatedHead))((updatedParents, currentKnot) =>
currentKnot.seekTowards(updatedParents.head) :: updatedParents
)
.reverse
RopeState(
knots = updatedKnots,
tailVisited = tailVisited + updatedKnots.last
)
}
}
case class Instruction(direction: Direction, count: Int)
def main = {
val lines = Source.fromFile("day_09.input").getLines().toList
val instructions = lines.map(parseLine)
println(s"Part 1: ${simulateRope(2, instructions).tailVisited.size}")
println(s"Part 2: ${simulateRope(10, instructions).tailVisited.size}")
}
def simulateRope(
length: Int,
instructions: List[Instruction]
): RopeState = {
val initialRopeState = RopeState(
knots = List.fill(length)(Coord(0, 0)),
tailVisited = Set(Coord(0, 0))
)
instructions
.flatMap { case Instruction(dir, count) => List.fill(count)(dir) }
.foldLeft[RopeState](initialRopeState) { (state, direction) =>
state.step(direction)
}
}
def parseLine(line: String): Instruction = {
val Array(directionStr, countStr) = line.split(" "): @unchecked
val direction = directionStr match {
case "L" => Direction.Left
case "R" => Direction.Right
case "U" => Direction.Up
case "D" => Direction.Down
}
Instruction(direction, countStr.toInt)
}
}