-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAdvent08.scala
92 lines (74 loc) · 2.8 KB
/
Advent08.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
91
92
package jurisk.adventofcode.y2022
import cats.data.NonEmptyList
import cats.implicits._
import jurisk.geometry.Coords2D
import jurisk.geometry.Field2D
import jurisk.geometry.visualizeBoolean
import jurisk.utils.FileInput._
import org.scalatest.matchers.should.Matchers._
import scala.collection.immutable.ArraySeq
object Advent08 {
type TreeHeight = Int
type Parsed = Field2D[TreeHeight]
type Processed = Parsed
type Result1 = Long
type Result2 = Long
def readFileAndParse(fileName: String): Parsed = {
val lines = readFileText(fileName)
Field2D.parseDigitField(lines)
}
private def visibleFromOutside(data: NonEmptyList[TreeHeight]): Boolean =
data.tail.maxOption match {
case None => true
case Some(m) => data.head > m
}
private def viewingDistance(value: NonEmptyList[TreeHeight]): Int = {
val visibleTrees = value.tail.takeWhile(_ < value.head).size
if (visibleTrees == value.tail.size) visibleTrees else visibleTrees + 1
}
private def slicesInAllDirections[T](
data: Field2D[T],
from: Coords2D,
): List[NonEmptyList[T]] = {
def splitBothDirections(
value: ArraySeq[T],
idx: Int,
): List[NonEmptyList[T]] =
NonEmptyList.fromListUnsafe(value.toList.take(idx + 1).reverse) ::
NonEmptyList.fromListUnsafe(value.toList.drop(idx)) ::
Nil
splitBothDirections(
data.column(from.x),
from.y,
) ::: splitBothDirections(data.row(from.y), from.x)
}
def part1(data: Parsed): Result1 = {
def isVisible(c: Coords2D): Boolean =
slicesInAllDirections(data, c).exists(visibleFromOutside)
val visible = data.mapByCoords(isVisible)
val visualisation: Field2D[Char] = visible map visualizeBoolean
Field2D.printCharField(visualisation)
visible.count(_ == true)
}
def part2(data: Parsed): Result2 = {
def scenicScore(c: Coords2D): Long =
slicesInAllDirections(data, c).map(viewingDistance).product
data.allCoords.map(scenicScore).max
}
def main(args: Array[String]): Unit = {
visibleFromOutside(NonEmptyList.of(1, 5, 5, 2)) shouldEqual false
visibleFromOutside(NonEmptyList.of(1, 2)) shouldEqual false
visibleFromOutside(NonEmptyList.of(1, 7)) shouldEqual false
visibleFromOutside(NonEmptyList.of(1, 3, 4, 9)) shouldEqual false
viewingDistance(NonEmptyList.of(5, 3)) shouldEqual 1 // sees 3
viewingDistance(NonEmptyList.of(5, 5, 2)) shouldEqual 1 // sees 5
viewingDistance(NonEmptyList.of(5, 1, 2)) shouldEqual 2 // sees 1, 2
viewingDistance(NonEmptyList.of(5, 3, 5, 3)) shouldEqual 2 // sees 3, 5
val test = readFileAndParse("2022/08-test.txt")
val real = readFileAndParse("2022/08.txt")
part1(test) shouldEqual 21
part1(real) shouldEqual 1690
part2(test) shouldEqual 8
part2(real) shouldEqual 535680
}
}