1
+ package adventofcode2023
2
+
3
+ import java.io.File
4
+ import java.util.LinkedList
5
+
6
+ object Day19 {
7
+ private const val ACCEPT = " A"
8
+ private const val REJECT = " R"
9
+ private const val STARTING_WORKFLOW = " in"
10
+
11
+ private val inputs = File (" resources/adventofcode2023/Day19.txt" )
12
+ .readText()
13
+ .split(" \n\n " )
14
+ .map { it.split(" \n " ) }
15
+
16
+ private val workflows = inputs.first().associate { line ->
17
+ val split = line.split(' {' )
18
+ val steps = split.last().removeSuffix(" }" ).split(' ,' ).map { step ->
19
+ val stepSplit = step.split(' :' )
20
+ if (stepSplit.size == 1 )
21
+ Pair (null , stepSplit.first())
22
+ else
23
+ Pair (
24
+ Condition (
25
+ stepSplit.first().first(),
26
+ stepSplit.first().drop(1 ).first(),
27
+ stepSplit.first().drop(2 ).toInt()
28
+ ),
29
+ stepSplit.last()
30
+ )
31
+ }
32
+ split[0 ] to steps
33
+ }
34
+
35
+ private val parts = inputs.last().map { line ->
36
+ val split = line.removeSurrounding(" {" , " }" ).split(' ,' )
37
+ split.associate {
38
+ val rating = it.split(' =' )
39
+ rating.first().first() to rating.last().toInt()
40
+ }
41
+ }
42
+
43
+ private data class Condition (val variable : Char , val checker : Char , val value : Int ) {
44
+ fun check (checked : Map <Char , Int >): Boolean =
45
+ checkValue(checked[variable]!! )
46
+
47
+ fun checkRange (checked : Map <Char , Pair <Int , Int >>): Int =
48
+ (if (checkValue(checked[variable]!! .first)) RANGE_LOWER else 0 ) +
49
+ (if (checkValue(checked[variable]!! .second)) RANGE_HIGHER else 0 )
50
+
51
+ private fun checkValue (checkedValue : Int ): Boolean =
52
+ when (checker) {
53
+ ' >' -> checkedValue > value
54
+ ' <' -> checkedValue < value
55
+ else -> throw Exception (" Unknown checker" )
56
+ }
57
+
58
+ companion object {
59
+ const val RANGE_NONE = 0
60
+ const val RANGE_LOWER = 1
61
+ const val RANGE_HIGHER = 2
62
+ const val RANGE_BOTH = 3
63
+ }
64
+ }
65
+
66
+ private fun checkPart (part : Map <Char , Int >): Boolean {
67
+ var currentWorkflow = workflows[STARTING_WORKFLOW ]!!
68
+ var stepIndex = 0
69
+
70
+ while (stepIndex < currentWorkflow.size) {
71
+ val step = currentWorkflow[stepIndex]
72
+ stepIndex++
73
+
74
+ if (step.first != null && ! step.first!! .check(part)) continue
75
+
76
+ when (step.second) {
77
+ ACCEPT -> return true
78
+ REJECT -> return false
79
+ else -> {
80
+ currentWorkflow = workflows[step.second]!!
81
+ stepIndex = 0
82
+ }
83
+ }
84
+ }
85
+
86
+ throw Exception (" Couldn't check part" )
87
+ }
88
+
89
+ private fun acceptedRanges (): List <Map <Char , Pair <Int , Int >>> {
90
+ // Map of values (x, m, a, s), Workflow name, Workflow step index
91
+ val ranges = LinkedList <Triple <Map <Char , Pair <Int , Int >>, String , Int >> ()
92
+
93
+ val accepted = mutableListOf<Map <Char , Pair <Int , Int >>>()
94
+
95
+ ranges.add(Triple (mapOf (
96
+ ' x' to Pair (1 , 4000 ),
97
+ ' m' to Pair (1 , 4000 ),
98
+ ' a' to Pair (1 , 4000 ),
99
+ ' s' to Pair (1 , 4000 )
100
+ ), STARTING_WORKFLOW , 0 ))
101
+
102
+ while (ranges.isNotEmpty()) {
103
+ val range = ranges.removeFirst()
104
+ val (valueRanges, workflowName, stepIndex) = range
105
+
106
+ when (workflowName) {
107
+ ACCEPT -> {
108
+ accepted.add(valueRanges)
109
+ continue
110
+ }
111
+ REJECT -> continue
112
+ }
113
+
114
+ val step = workflows[workflowName]!! [stepIndex]
115
+
116
+ if (step.first != null ) {
117
+ val condition = step.first!!
118
+ val (variable, _, value) = condition
119
+
120
+ fun createTriple (
121
+ changeWorkflow : Boolean , firstValue : Int = -1, secondValue : Int = -1
122
+ ): Triple <Map <Char , Pair <Int , Int >>, String, Int> {
123
+ val newRangeMap = valueRanges.toMutableMap().apply {
124
+ if (firstValue >= 0 ) this [variable] = this [variable]!! .copy(first = firstValue)
125
+ if (secondValue >= 0 ) this [variable] = this [variable]!! .copy(second = secondValue)
126
+ }
127
+
128
+ return Triple (newRangeMap, if (changeWorkflow) step.second else workflowName, if (changeWorkflow) 0 else stepIndex + 1 )
129
+ }
130
+
131
+ when (condition.checkRange(valueRanges)) {
132
+ Condition .RANGE_NONE -> {
133
+ ranges.add(Triple (valueRanges, workflowName, stepIndex + 1 ))
134
+ }
135
+ Condition .RANGE_LOWER -> {
136
+ ranges.add(createTriple(true , secondValue = value - 1 ))
137
+ ranges.add(createTriple(false , firstValue = value))
138
+ }
139
+ Condition .RANGE_HIGHER -> {
140
+ ranges.add(createTriple(true , firstValue = value + 1 ))
141
+ ranges.add(createTriple(false , secondValue = value))
142
+ }
143
+ Condition .RANGE_BOTH -> {
144
+ ranges.add(Triple (valueRanges, step.second, 0 ))
145
+ }
146
+ }
147
+ } else {
148
+ ranges.add(Triple (valueRanges, step.second, 0 ))
149
+ }
150
+ }
151
+
152
+ return accepted
153
+ }
154
+
155
+ fun part1 () = println (parts.filter { checkPart(it) }.sumOf { it.values.sum() })
156
+
157
+ fun part2 () = println (acceptedRanges().sumOf { ranges ->
158
+ ranges.values.fold(1L ) { acc, pair -> acc * (pair.second - pair.first + 1 ) }
159
+ })
160
+ }
161
+
162
+ fun main () {
163
+ Day19 .part1()
164
+ Day19 .part2()
165
+ }
0 commit comments