4
4
package day05
5
5
6
6
import common.day
7
- import common.middle
7
+ import common.collectionops. middle
8
8
import common.readInput
9
9
10
- typealias OrderingRules = Map <Int , Set <Int >>
11
- typealias Updates = List <Int >
10
+ private typealias OrderingRules = Map <Int , Set <Int >>
11
+ private typealias Updates = List <Int >
12
12
13
13
/* *
14
- * We parse the input failure rules into two distinct structures.
15
- * The entries are of the form a|b, where if b occurs before a in a list of updates,
16
- * the process should fail.
17
- *
18
- * Thus, this is parsed into a Map<Int, Set<Int>> where all the b from above comprise keys,
19
- * and any entry a1|b, a2|b, etc. form the set of values {a1, a2, ...} indicate that if an
20
- * ai occurs after b, the updates fail.
14
+ * Parse the violation rules into a map where:
15
+ * - Keys are the integers `b` that must not precede certain `a` values.
16
+ * - Values are sets of `a` values that cause a violation if they follow the key.
21
17
*/
22
18
private fun parseViolationRules (input : String ): OrderingRules =
23
19
input.lines()
@@ -26,69 +22,87 @@ private fun parseViolationRules(input: String): OrderingRules =
26
22
val (a, b) = line.trim().split(' |' ).map(String ::toInt)
27
23
b to a
28
24
}
29
- .groupBy(Pair <Int , Int >::first, Pair <Int , Int >::second)
30
- .mapValues { (_, b) -> b.toSet() }
31
-
25
+ .groupBy({ it.first }, { it.second })
26
+ .mapValues { (_, values) -> values.toSet() }
32
27
28
+ /* *
29
+ * Parse updates as lists of integers from input lines.
30
+ */
33
31
private fun parseUpdates (input : String ): List <Updates > =
34
- input.lines()
35
- .map { line -> line.trim().split(' ,' ).map(String ::toInt) }
32
+ input.lines().map { line ->
33
+ line.trim().split(' ,' ).map(String ::toInt)
34
+ }
36
35
37
- fun parseViolation (input : String ): Pair <OrderingRules , List <Updates >> {
36
+ /* *
37
+ * Parse the violation rules and updates from the input string.
38
+ * The input is split into two sections separated by a blank line.
39
+ */
40
+ private fun parseViolation (input : String ): Pair <OrderingRules , List <Updates >> {
38
41
val (rulesString, updatesString) = input.split(" \n\n " )
39
- val rules = parseViolationRules(rulesString)
40
- val updates = parseUpdates(updatesString)
41
- return rules to updates
42
+ return parseViolationRules(rulesString) to parseUpdates(updatesString)
42
43
}
43
44
44
- fun passesViolation (updates : Updates , violationRules : OrderingRules ): Boolean =
45
- updates.fold(emptySet<Int >()) { disallowed, page ->
45
+ /* *
46
+ * Check if a given sequence of updates passes the violation rules.
47
+ * Returns `true` if no violations occur, otherwise `false`.
48
+ */
49
+ private fun passesViolation (updates : Updates , violationRules : OrderingRules ): Boolean {
50
+ val disallowed = mutableSetOf<Int >()
51
+ for (page in updates) {
46
52
if (page in disallowed) return false
47
- disallowed + (violationRules[page] ? : emptySet())
48
- }.let { true }
53
+ disallowed + = violationRules[page] ? : emptySet()
54
+ }
55
+ return true
56
+ }
49
57
50
58
/* *
51
- * Idea:
52
- * Gather all elements.
53
- * While there are still elements in the remaining element set:
54
- * Keep picking the element that doesn't appear in any other violation for remaining elements
55
- * and add it to the ordering.
56
- * Remove it from the remaining element set.
59
+ * Reorder the updates to satisfy violation rules.
60
+ * Uses a recursive approach to build the ordering.
57
61
*/
58
- fun reorder (updates : Updates , violationRules : OrderingRules ): List < Int > {
62
+ private fun reorder (updates : Updates , violationRules : OrderingRules ): Updates {
59
63
tailrec fun aux (
60
- reorder : List < Int > = emptyList(),
64
+ reordered : Updates = emptyList(),
61
65
remaining : Set <Int > = updates.toSet()
62
66
): Updates {
63
- if (remaining.isEmpty()) return reorder
64
- val disallowed = remaining.flatMap { violationRules[it] ? : emptySet() }
67
+ if (remaining.isEmpty()) return reordered
68
+ val disallowed = remaining.flatMap { violationRules[it] ? : emptySet() }.toSet()
65
69
val candidates = remaining - disallowed
66
- if (candidates.isEmpty()) throw RuntimeException ( " No candidate for reordering. " )
67
- val candidate = candidates.first( )
68
- return aux(reorder + candidate, remaining - candidate)
70
+ val candidate = candidates.firstOrNull( )
71
+ ? : throw RuntimeException ( " No candidate for reordering. " )
72
+ return aux(reordered + candidate, remaining - candidate)
69
73
}
70
74
return aux()
71
75
}
72
76
73
- fun answer1 (violationRules : OrderingRules , updatesList : List <Updates >): Int =
74
- updatesList.filter { update -> passesViolation(update, violationRules) }
75
- .sumOf(List <Int >::middle)
76
-
77
- fun answer2 (violationRules : OrderingRules , updatesList : List <Updates >): Int =
78
- updatesList.filterNot { passesViolation(it, violationRules) }
79
- .map { reorder(it, violationRules) }
80
- .sumOf(List <Int >::middle)
77
+ /* *
78
+ * Part 1: Sum the "middle" values of updates that pass the violation rules.
79
+ */
80
+ fun answer1 (input : String ): Int =
81
+ parseViolation(input).let { (violationRules, updatesList) ->
82
+ updatesList
83
+ .filter { passesViolation(it, violationRules) }
84
+ .sumOf(List <Int >::middle)
85
+ }
81
86
87
+ /* *
88
+ * Part 2: Sum the "middle" values of reordered updates that fail the violation rules.
89
+ */
90
+ fun answer2 (input : String ): Int =
91
+ parseViolation(input).let { (violationRules, updatesList) ->
92
+ updatesList
93
+ .filterNot { passesViolation(it, violationRules) }
94
+ .map { reorder(it, violationRules) }
95
+ .sumOf(List <Int >::middle)
96
+ }
82
97
83
98
fun main () {
84
99
val input = readInput({}::class .day()).trim()
85
- val (violationRules, updateList) = parseViolation(input)
86
100
87
101
println (" --- Day 5: Print Queue ---" )
88
102
89
- // Answer 1: 4281
90
- println (" Part 1: ${answer1(violationRules, updateList )} " )
103
+ // Part 1: 4281
104
+ println (" Part 1: ${answer1(input )} " )
91
105
92
- // Answer 2: 5466
93
- println (" Part 2: ${answer2(violationRules, updateList )} " )
94
- }
106
+ // Part 2: 5466
107
+ println (" Part 2: ${answer2(input )} " )
108
+ }
0 commit comments