@@ -4,35 +4,124 @@ import java.io.File
4
4
5
5
val RULE_REGEX_PATTERN = """ ([\w]+) -> ([\w])""" .toRegex()
6
6
7
- fun runSolutionPart1 (template : String , rules : Map <String , String >) {
8
- println (" Day 14 Solution: Part 1 (Basic Implementation)" )
9
- val steps = 10
7
+ typealias InsertionRules = Map <String , Char >
8
+ typealias CacheKey = Pair <String , Int >
9
+ typealias CharCountMap = Map <Char , Long >
10
+ typealias Cache = Map <CacheKey , CharCountMap >
11
+
12
+ fun CharCountMap.sort (): List <Pair <Char , Long >> {
13
+ return this .toList().sortedWith { a, b ->
14
+ when (val countCompare = a.second.compareTo(b.second)) {
15
+ 0 -> a.first.compareTo(b.first)
16
+ else -> countCompare
17
+ }
18
+ }
19
+ }
20
+
21
+ object PolymerProcessor {
22
+ private fun processPolymer (
23
+ polymer : String ,
24
+ rules : InsertionRules ,
25
+ step : Int ,
26
+ cache : Cache ,
27
+ ): Pair <CharCountMap , Cache > {
28
+ val (nextCharCountMap, cacheResult) = polymer
29
+ .windowedSequence(2 )
30
+ .fold(mapOf<Char , Long >() to cache) { (thisAggregate, thisCache), nextPair ->
31
+ val (result, nextCache) = processElementPair(nextPair, rules, step, thisCache)
32
+ val nextAggregate = result.toList().fold(thisAggregate) { agg, (char, count) ->
33
+ agg + (char to (agg[char]?.let { it + count } ? : count))
34
+ }
35
+ nextAggregate to nextCache
36
+ }
37
+ val overlappingElements = polymer.substring(1 , polymer.lastIndex)
38
+ val charCountMapResult = overlappingElements
39
+ .fold(nextCharCountMap) { map, char ->
40
+ var count = map[char]?.let { it - 1 } ? : 1
41
+ map + (char to count)
42
+ }
43
+ return charCountMapResult to cacheResult
44
+ }
45
+
46
+ private fun processElementPair (
47
+ pair : String ,
48
+ rules : InsertionRules ,
49
+ step : Int ,
50
+ cache : Cache ,
51
+ ): Pair <CharCountMap , Cache > {
52
+ val cachedValue = cache[pair to step]
53
+ if (cachedValue != null ) {
54
+ return cachedValue to cache
55
+ }
56
+
57
+ if (step == 0 ) {
58
+ val expandedResult = pair.toList().fold(mapOf<Char , Long >()) { map, char ->
59
+ map + (char to map.getOrDefault(char, 0L ) + 1L )
60
+ }
61
+ val nextCache = cache + (Pair (pair, step) to expandedResult)
62
+ return expandedResult to nextCache
63
+ }
64
+
65
+ val insertedElem = rules[pair] ? : throw error(" No insertion rule for pair $pair " )
66
+ val polymer = " ${pair[0 ]}$insertedElem${pair[1 ]} "
67
+ val (aggregateResult, nextCache) = processPolymer(polymer, rules, step - 1 , cache)
68
+ val cacheResult = nextCache + (Pair (pair, step) to aggregateResult)
69
+
70
+ return aggregateResult to cacheResult
71
+ }
72
+
73
+ fun process (template : String , rules : InsertionRules , steps : Int ): CharCountMap {
74
+ val cache = mapOf<CacheKey , CharCountMap >()
75
+ val result = processPolymer(template, rules, steps, cache)
76
+ return result.first
77
+ }
78
+ }
79
+
80
+ fun summarizePolymerProcessingResult (result : CharCountMap ) {
81
+ val sortedCharCounts = result.sort()
82
+ val leastCommon = sortedCharCounts.first()
83
+ val mostCommon = sortedCharCounts.last()
84
+ val score = mostCommon.second - leastCommon.second
85
+
86
+ println (" Sort char-count: $sortedCharCounts " )
87
+ println (" Most common char count: $leastCommon " )
88
+ println (" Least common char count: $mostCommon " )
89
+ println (" Score: $score " )
90
+ }
91
+
92
+ fun runSolutionPart1 (template : String , rules : Map <String , Char >, steps : Int = 10) {
93
+ println (" Day 14 Solution: Part 1 (Basic Implementation)\n " )
10
94
val polymer = (0 until steps).fold(template) { polymer, _ ->
11
95
polymer
12
96
.windowedSequence(2 )
13
97
.mapNotNull { rules[it]?.let { elem -> " ${it[0 ]}$elem${it[1 ]} " } }
14
98
.mapIndexed { idx, chunk -> if (idx == 0 ) chunk else chunk.substring(1 ) }
15
99
.joinToString(" " )
16
100
}
17
- val charCounts = polymer.asSequence().fold(mapOf<Char , Int >()) { map, char ->
18
- map + (char to map.getOrDefault(char, 0 ) + 1 )
101
+ val charCounts: CharCountMap = polymer.asSequence().fold(mapOf<Char , Long >()) { map, char ->
102
+ map + (char to map.getOrDefault(char, 0L ) + 1L )
19
103
}
20
- val mostCommonCharCount = charCounts.maxOf { (_, count) -> count }
21
- val leastCommonCharCount = charCounts.minOf { (_, count) -> count }
22
- val score = mostCommonCharCount - leastCommonCharCount
23
104
24
- println (" Most common char count: $mostCommonCharCount " )
25
- println (" Least common char count: $leastCommonCharCount " )
26
- println (" Score: $score " )
105
+ summarizePolymerProcessingResult(charCounts)
106
+ }
107
+
108
+ fun runSolutionPart2 (template : String , rules : Map <String , Char >, steps : Int = 10) {
109
+ println (" Day 14 Solution: Part 2\n " )
110
+ val result = PolymerProcessor .process(template, rules, steps)
111
+ summarizePolymerProcessingResult(result)
27
112
}
28
113
29
114
fun main () {
30
- val input = File (" day14/src/main/resources/puzzleInput .txt" ).readLines()
115
+ val input = File (" day14/src/main/resources/sampleInput .txt" ).readLines()
31
116
val template = input.first()
32
117
val rules = input.drop(2 ).mapNotNull {
33
118
val result = RULE_REGEX_PATTERN .matchEntire(it)
34
- result?.groupValues?.let { (_, fromPair, toChar) -> fromPair to toChar }
119
+ result?.groupValues?.let { (_, fromPair, toChar) -> fromPair to toChar[ 0 ] }
35
120
}.toMap()
36
121
37
- runSolutionPart1(template, rules)
122
+ runSolutionPart1(template, rules, 10 )
123
+ println ()
124
+ runSolutionPart2(template, rules, 10 )
125
+
126
+
38
127
}
0 commit comments