-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay03.scala
77 lines (61 loc) · 2.26 KB
/
Day03.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
package space.astrionic
package adventofcode2023.solutions.day03
import adventofcode2023.framework.executeSolution
@main def day03(): Unit = executeSolution("03", part1, part2)
private def part1(input: String): String = {
val lines = input.split("\n")
val isAdjacentToSymbol: PuzzleNumber => Boolean = (num: PuzzleNumber) => {
val start = Math.max(0, num.start - 1)
val end = Math.min(lines(num.lineIndex).length, num.end + 1)
val lineBefore = num.lineIndex - 1
val lineAfter = num.lineIndex + 1
val getSymbolsOnLine = (lineIndex: Int) =>
lines
.lift(lineIndex)
.map(_.substring(start, end))
.getOrElse("")
.toCharArray
.filter(c => !c.isDigit && c != '.')
(lineBefore to lineAfter)
.flatMap(getSymbolsOnLine(_))
.nonEmpty
}
lines.zipWithIndex
.flatMap(lineToPuzzleNumberList)
.filter(isAdjacentToSymbol)
.map(_.value)
.sum
.toString
}
private def part2(input: String): String = {
val lines = input.split("\n")
val gearRegex = raw"(\*)".r.unanchored
val gears = lines.zipWithIndex
.flatMap((line, idx) => gearRegex.findAllMatchIn(line).map(m => Gear(idx, m.start)))
val numbers = lines.zipWithIndex
.flatMap(lineToPuzzleNumberList)
gears
.flatMap(g => {
numbers.filter(areAdjacent(g, _)).toList match {
case one :: two :: Nil => Some(one.value * two.value)
case _ => None
}
})
.sum
.toString
}
private case class PuzzleNumber(value: Int, lineIndex: Int, start: Int, end: Int)
private case class Gear(lineIndex: Int, rowIndex: Int)
private def lineToPuzzleNumberList(line: String, idx: Int): List[PuzzleNumber] = {
val numberRegex = raw"(\d+)".r.unanchored
numberRegex
.findAllMatchIn(line)
.flatMap(m => m.matched.toIntOption.map(num => PuzzleNumber(num, idx, m.start, m.end)))
.toList
}
private def areAdjacent(gear: Gear, num: PuzzleNumber): Boolean = {
// Because all numbers in the input have exactly 3 digits, they have to start or end in this window
val adjacentLines = gear.lineIndex - 1 to gear.lineIndex + 1
val adjacentRows = gear.rowIndex - 1 to gear.rowIndex + 1
adjacentLines.contains(num.lineIndex) && (adjacentRows.contains(num.start) || adjacentRows.contains(num.end - 1))
}