You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/2023/puzzles/day19.md
+222Lines changed: 222 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -2,10 +2,232 @@ import Solver from "../../../../../website/src/components/Solver.js"
2
2
3
3
# Day 19: Aplenty
4
4
5
+
by [@mbovel](https://github.com/mbovel)
6
+
5
7
## Puzzle description
6
8
7
9
https://adventofcode.com/2023/day/19
8
10
11
+
## Data structures
12
+
13
+
We define the following data structures for today's puzzle:
14
+
15
+
```scala
16
+
enumChannel:
17
+
caseX, M, A, S
18
+
19
+
enumOperator:
20
+
caseLessThan, GreaterThan
21
+
22
+
enumResult:
23
+
caseReject, Accept
24
+
25
+
enumInstruction:
26
+
caseIfThenElse(
27
+
channel: Channel,
28
+
operator: Operator,
29
+
value: Int,
30
+
thenBranch: GoTo|Return,
31
+
elseBranch: Instruction
32
+
)
33
+
caseReturn(result: Result)
34
+
caseGoTo(target: String)
35
+
36
+
importInstruction.*
37
+
38
+
typeWorkflow=Map[String, Instruction]
39
+
40
+
caseclassPart(x: Int, m: Int, a: Int, s: Int)
41
+
```
42
+
43
+
## Parsing
44
+
45
+
Then, we write the associated parsing methods, using `String`'s `split` method and [regular expression patterns](https://docs.scala-lang.org/tour/regular-expression-patterns.html)
46
+
47
+
```scala
48
+
objectChannel:
49
+
defparse(input: String):Channel=
50
+
input match
51
+
case"x"=>Channel.X
52
+
case"m"=>Channel.M
53
+
case"a"=>Channel.A
54
+
case"s"=>Channel.S
55
+
case _ =>throwException(s"Invalid channel: $input")
56
+
57
+
objectOperator:
58
+
defparse(input: String):Operator=
59
+
input match
60
+
case"<"=>Operator.LessThan
61
+
case">"=>Operator.GreaterThan
62
+
case _ =>throwException(s"Invalid operator: $input")
63
+
64
+
objectResult:
65
+
defparse(input: String):Result=
66
+
input match
67
+
case"R"=>Result.Reject
68
+
case"A"=>Result.Accept
69
+
case _ =>throwException(s"Invalid result: $input")
To solve the second part efficiently, we use [abstract interpretation](https://en.wikipedia.org/wiki/Abstract_interpretation) to count the number of executions of the workflow that lead to an `Accept` result.
146
+
147
+
The _abstract domain_ in our case is sets of `Part`s.
148
+
149
+
We represent such sets with the `AbstractPart` structure, where the value associated to each channel is not a number, but a range of possible values:
150
+
151
+
```scala
152
+
caseclassRange(from: Long, until: Long):
153
+
assert(from < until)
154
+
defcount() = until - from
155
+
156
+
objectRange:
157
+
defsafe(from: Long, until: Long):Option[Range] =
158
+
if from < until thenSome(Range(from, until)) elseNone
We will start the evaluation with abstract parts that contain all possible values for each channel: four ranges from 1 until 4001.
179
+
180
+
When we evaluate an `IfThenElse` instruction, we split the argument `AbstractPart` into two parts, one that contains only the values that satisfy the condition, and one that contains only the values that do not satisfy the condition.
181
+
182
+
This is achieved with the `split` method of `AbstractPart`:
0 commit comments