/
Day12.scala
71 lines (60 loc) · 1.9 KB
/
Day12.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
class Day12 extends munit.FunSuite:
/// core logic
// `seen` is how many #s in a row we have just seen
val matches: (Vector[Char], Vector[Int], Int) => Long =
Memo.memoize: (states, counts, seen) =>
if states.isEmpty
then
if (seen == 0 && counts.isEmpty) || counts == Vector(seen)
then 1L
else 0L
else states.head match
case '.' =>
if counts.headOption.contains(seen)
then matches(states.tail, counts.tail, 0)
else if seen == 0L
then matches(states.tail, counts, 0)
else 0L
case '#' =>
if counts.headOption.contains(seen)
then 0L
else matches(states.tail, counts, seen + 1)
case '?' =>
matches('#' +: states.tail, counts, seen) +
matches('.' +: states.tail, counts, seen)
case c =>
require(false, s"invalid: $c"); ???
/// reading & parsing
def getInput(name: String): Vector[(Vector[Char], Vector[Int])] =
def parse(line: String) =
line.split(' ') match
case Array(states, counts) =>
(states.toVector,
counts.split(',').map(_.toInt).toVector)
io.Source.fromResource(name)
.getLines
.map(parse)
.toVector
/// part 1
def part1(name: String): Long =
getInput(name)
.map: (states, counts) =>
matches(states, counts, 0)
.sum
test("part 1 sample"):
assertEquals(part1("day12-sample.txt"), 21L)
test("part 1"):
assertEquals(part1("day12.txt"), 7361L)
/// part 2
def part2(name: String): Long =
getInput(name)
.map: (states, counts) =>
matches(
Vector.fill(5)('?' +: states).flatten.tail,
Vector.fill(5)(counts).flatten, 0)
.sum
test("part 2 sample"):
assertEquals(part2("day12-sample.txt"), 525152L)
test("part 2"):
assertEquals(part2("day12.txt"), 83317216247365L)
end Day12