/
Day21.kt
215 lines (179 loc) · 7.59 KB
/
Day21.kt
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
package com.akikanellis.adventofcode.year2022
import com.akikanellis.adventofcode.year2022.Day21.Operator.ADDITION
import com.akikanellis.adventofcode.year2022.Day21.Operator.DIVISION
import com.akikanellis.adventofcode.year2022.Day21.Operator.MULTIPLICATION
import com.akikanellis.adventofcode.year2022.Day21.Operator.SUBTRACTION
object Day21 {
private val MONKEY_REGEX = """(.+): (.+)""".toRegex()
fun monkeyNumber(input: String, monkeyName: String): Long {
var monkeys = monkeys(input)
if (monkeyName != "root") monkeys = solvingForGivenMonkey(monkeys, monkeyName)
return monkeys[monkeyName]!!.yellNumber(monkeys)
}
private fun monkeys(input: String) = input.lines()
.filter { it.isNotBlank() }
.map { line ->
val groupValues = MONKEY_REGEX.matchEntire(line)!!.groupValues
Monkey.of(name = groupValues[1], job = groupValues[2])
}.associateBy { it.name }
private fun solvingForGivenMonkey(
originalMonkeys: Map<String, Monkey>,
monkeyName: String
): Map<String, Monkey> {
val originalRootMonkey = originalMonkeys["root"]!!
val monkeys = originalMonkeys -
monkeyName +
("root" to originalRootMonkey.withSubtractionOperation())
val originalMonkeysNotVisited = monkeys.toMutableMap()
val newMonkeys = mutableMapOf<String, Monkey>()
val monkeysToVisit = mutableListOf(monkeyName)
while (monkeysToVisit.isNotEmpty()) {
val originalMonkeyName = monkeysToVisit.removeFirst()
val originalMonkey = originalMonkeysNotVisited.remove(originalMonkeyName)
if (originalMonkeyName == "root") {
newMonkeys["root"] = NumberMonkey(name = "root", number = 0)
continue
}
if (originalMonkey != null) {
newMonkeys[originalMonkeyName] = originalMonkey
monkeysToVisit += originalMonkey.operands
continue
}
val monkeyWithJobContainingOriginalMonkey = originalMonkeysNotVisited.values.single {
it.jobContainsMonkey(originalMonkeyName)
}
val monkeySolvingForOriginalMonkey =
monkeyWithJobContainingOriginalMonkey.solvingFor(originalMonkeyName)
originalMonkeysNotVisited.remove(monkeyWithJobContainingOriginalMonkey.name)
newMonkeys[monkeySolvingForOriginalMonkey.name] = monkeySolvingForOriginalMonkey
monkeysToVisit += monkeySolvingForOriginalMonkey.operands
}
return newMonkeys.toMap()
}
private interface Monkey {
val name: String
val operands: List<String>
fun jobContainsMonkey(monkeyName: String) = operands.contains(monkeyName)
fun withSubtractionOperation(): MathMonkey
fun yellNumber(monkeys: Map<String, Monkey>): Long
fun solvingFor(operand: String): MathMonkey
companion object {
fun of(name: String, job: String) =
if (job.toLongOrNull() != null) {
NumberMonkey(
name = name,
number = job.toLong()
)
} else {
val jobParts = job.split(" ")
MathMonkey(
name = name,
firstOperand = jobParts[0],
operator = Operator.of(jobParts[1].toCharArray().single()),
secondOperand = jobParts[2]
)
}
}
}
private data class NumberMonkey(
override val name: String,
override val operands: List<String> = emptyList(),
val number: Long
) : Monkey {
override fun withSubtractionOperation() = error("Operations are not applicable")
override fun yellNumber(monkeys: Map<String, Monkey>) = number
override fun solvingFor(operand: String) = error("Cannot solve for different operands")
}
private data class MathMonkey(
override val name: String,
val firstOperand: String,
val operator: Operator,
val secondOperand: String,
override val operands: List<String> = listOf(firstOperand, secondOperand)
) : Monkey {
override fun withSubtractionOperation() = copy(operator = SUBTRACTION)
override fun yellNumber(monkeys: Map<String, Monkey>): Long {
val firstOperandNumber = operandNumber(firstOperand, monkeys)
val secondOperandNumber = operandNumber(secondOperand, monkeys)
return when (operator) {
ADDITION -> firstOperandNumber + secondOperandNumber
SUBTRACTION -> firstOperandNumber - secondOperandNumber
MULTIPLICATION -> firstOperandNumber * secondOperandNumber
DIVISION -> firstOperandNumber / secondOperandNumber
}
}
private fun operandNumber(operand: String, monkeys: Map<String, Monkey>) =
operand.toLongOrNull() ?: monkeys[operand]!!.yellNumber(monkeys)
override fun solvingFor(operand: String) = when (operand) {
name -> this
firstOperand -> when (operator) {
ADDITION -> MathMonkey(
name = firstOperand,
firstOperand = name,
operator = SUBTRACTION,
secondOperand = secondOperand
)
SUBTRACTION -> MathMonkey(
name = firstOperand,
firstOperand = name,
operator = ADDITION,
secondOperand = secondOperand
)
MULTIPLICATION -> MathMonkey(
name = firstOperand,
firstOperand = name,
operator = DIVISION,
secondOperand = secondOperand
)
DIVISION -> MathMonkey(
name = firstOperand,
firstOperand = name,
operator = MULTIPLICATION,
secondOperand = secondOperand
)
}
secondOperand -> when (operator) {
ADDITION -> MathMonkey(
name = secondOperand,
firstOperand = name,
operator = SUBTRACTION,
secondOperand = firstOperand
)
SUBTRACTION -> MathMonkey(
name = secondOperand,
firstOperand = firstOperand,
operator = SUBTRACTION,
secondOperand = name
)
MULTIPLICATION -> MathMonkey(
name = secondOperand,
firstOperand = name,
operator = DIVISION,
secondOperand = firstOperand
)
DIVISION -> MathMonkey(
name = secondOperand,
firstOperand = firstOperand,
operator = DIVISION,
secondOperand = name
)
}
else -> error("Unknown operand '$operand'")
}
}
private enum class Operator {
ADDITION,
SUBTRACTION,
MULTIPLICATION,
DIVISION;
companion object {
fun of(representation: Char) = when (representation) {
'+' -> ADDITION
'-' -> SUBTRACTION
'*' -> MULTIPLICATION
'/' -> DIVISION
else -> error("Unknown operation '$representation'")
}
}
}
}