forked from matelaszlo/advent-of-code-scala
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDay12.scala
75 lines (60 loc) · 2.46 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
72
73
74
75
package com.lmat.adventofcode.year2018
import com.lmat.adventofcode.CommonPuzzle
import com.lmat.adventofcode.year2018.Day12Definitions._
import com.lmat.util.Files.readResource
object Day12Definitions {
case class Rule(from: Seq[Boolean], next: Boolean)
type RawConfiguration = (Seq[Boolean], Seq[Rule])
type Configuration = (Set[Int], Set[Seq[Boolean]])
}
object Day12 extends CommonPuzzle[RawConfiguration, Configuration, Int, Long] {
override def parse(resource: String): RawConfiguration = parseInput(readResource(resource))
def parseInput(rows: Seq[String]): (Seq[Boolean], Seq[Rule]) = {
def parseInitial(row: String): Option[Seq[Boolean]] = {
val pattern = "initial state: (.*?)".r
row match {
case pattern(initialS) => Some(initialS.toIndexedSeq.map(_ == '#'))
case _ => None
}
}
def parseRule(row: String): Option[Rule] = {
val pattern = "(.*?) => (.)".r
row match {
case pattern(fromS, toS) => Some(Rule(fromS.toIndexedSeq.map(_ == '#'), toS.head == '#'))
case _ => None
}
}
val initialRow +: _ +: ruleRows = rows
(parseInitial(initialRow).get, ruleRows.flatMap(parseRule))
}
override def preProcess(raw: RawConfiguration): Configuration = {
val (initial, rules) = raw
(initial.zipWithIndex.filter(_._1).map(_._2).toSet, rules.filter(_.next).map(_.from).toSet)
}
override def part1(configuration: Configuration): Int = {
val (initial, rules) = configuration
simulate(rules, 20)(initial).sum
}
def simulate(rules: Set[Seq[Boolean]], n: Int)(plants: Set[Int]): Set[Int] =
(1 to n).foldLeft(plants) { case (state, _) => next(rules)(state) }
def next(rules: Set[Seq[Boolean]])(plants: Set[Int]): Set[Int] = {
val min = plants.min - 2
val max = plants.max + 2
val selectors = -2 to 2
(min to max).filter(i => {
val pattern = selectors.map(_ + i).map(plants.contains)
rules.contains(pattern)
}).toSet
}
/**
* Since simulating 50000000000 would take took long we need to find a pattern
* Luckily after around a 100 generations the growth rate stabilizes which we can use to skip simulation of the rest of the cases
*/
override def part2(configuration: Configuration): Long = {
val (initial, rules) = configuration
val value100 = simulate(rules, 100)(initial).sum
val value200 = simulate(rules, 200)(initial).sum
val delta = (value200 - value100) / 100
(50000000000L - 100L) * delta + value100
}
}