Skip to content

Commit aa82d3f

Browse files
committed
adding day 11 pt 2
1 parent 768eba3 commit aa82d3f

File tree

4 files changed

+366
-3
lines changed

4 files changed

+366
-3
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ $ git commit -a
4848
| [Day 08](https://adventofcode.com/2016/day/8) | Two-Factor Authentication | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day08/Task01.kt) | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day08/Task02.kt) |
4949
| [Day 09](https://adventofcode.com/2016/day/9) | Explosives in Cyberspace | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day09/Task01.kt) | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day09/Task02.kt) |
5050
| [Day 10](https://adventofcode.com/2016/day/10) | Balance Bots | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day10/Task01.kt) | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day10/Task02.kt) |
51-
| [Day 11](https://adventofcode.com/2016/day/11) | Radioisotope Thermoelectric Generators | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day11/Task01.kt) | |
51+
| [Day 11](https://adventofcode.com/2016/day/11) | Radioisotope Thermoelectric Generators | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day11/Task01.kt) | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day11/Task02.kt) |
5252
| [Day 12](https://adventofcode.com/2016/day/12) | Leonardo's Monorail | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day12/Task01.kt) | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day12/Task02.kt) |
5353
| [Day 13](https://adventofcode.com/2016/day/13) | A Maze of Twisty Little Cubicles | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day13/Task01.kt) | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day13/Task02.kt) |
5454
| [Day 14](https://adventofcode.com/2016/day/14) | One-Time Pad | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day14/Task01.kt) | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day14/Task02.kt) |
@@ -62,4 +62,4 @@ $ git commit -a
6262
| [Day 22](https://adventofcode.com/2016/day/22) | Grid Computing | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day22/Task01.kt) | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day22/Task02.kt) |
6363
| [Day 23](https://adventofcode.com/2016/day/23) | Safe Cracking | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day23/Task01.kt) | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day23/Task02.kt) |
6464
| [Day 24](https://adventofcode.com/2016/day/24) | Air Duct Spelunking | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day24/Task01.kt) | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day24/Task02.kt) |
65-
| [Day 25](https://adventofcode.com/2016/day/25) | Clock Signal | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day25/Task01.kt) | |
65+
| [Day 25](https://adventofcode.com/2016/day/25) | Clock Signal | [🌟](https://github.com/andrewfitzy/2016-advent-of-code/blob/main/app/src/main/kotlin/io/github/andrewfitzy/day25/Task01.kt) | 🌟 |

app/src/main/kotlin/io/github/andrewfitzy/day11/Task01.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ class Task01(puzzleInput: List<String>) {
177177
nextFloorToBe: MutableList<String>,
178178
state: State,
179179
): State {
180-
var validState: State
180+
val validState: State
181181
if (FLOOR_ONE == currentElevator && FLOOR_TWO == nextElevator) {
182182
validState =
183183
State(
Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
package io.github.andrewfitzy.day11
2+
3+
private const val FLOOR_ONE = 1
4+
private const val FLOOR_TWO = 2
5+
private const val FLOOR_THREE = 3
6+
private const val FLOOR_FOUR = 4
7+
8+
private const val GENERATOR = "generator"
9+
private const val MICROCHIP = "microchip"
10+
11+
class Task02(puzzleInput: List<String>) {
12+
private val input: List<String> = puzzleInput
13+
14+
fun solve(): Int {
15+
val inputRegex = "\\b(\\w+(?:-\\w+)?\\s(?:generator|microchip))\\b".toRegex()
16+
val stateMap = mutableMapOf<Int, Set<String>>()
17+
18+
var floorCount = 1
19+
for (floor in input) {
20+
val matches = inputRegex.findAll(floor).map { it.groupValues[1] }.toList()
21+
if (floorCount == FLOOR_ONE) {
22+
val floorOne =
23+
mutableListOf(
24+
"elerium generator",
25+
"elerium-compatible microchip",
26+
"dilithium generator",
27+
"dilithium-compatible microchip",
28+
)
29+
floorOne.addAll(matches)
30+
stateMap[floorCount] = floorOne.toSet()
31+
} else {
32+
stateMap[floorCount] = matches.toSet()
33+
}
34+
floorCount++
35+
}
36+
val state =
37+
State(
38+
FLOOR_ONE,
39+
stateMap[FLOOR_ONE]!!,
40+
stateMap[FLOOR_TWO]!!,
41+
stateMap[FLOOR_THREE]!!,
42+
stateMap[FLOOR_FOUR]!!,
43+
)
44+
45+
val result = getShortestRoute(state)
46+
return result
47+
}
48+
49+
private fun getShortestRoute(stateMap: State): Int {
50+
val deque = ArrayDeque<Step>()
51+
val start = Step(0, stateMap)
52+
deque.add(start)
53+
val seen = mutableSetOf<Map<String, Int>>()
54+
while (!deque.isEmpty()) {
55+
val step = deque.removeFirst()
56+
if (step.state.isFinished()) {
57+
return step.cost
58+
}
59+
60+
val availableMoves = getAvailableMoves(step.state)
61+
for (move in availableMoves) {
62+
val encodedMove = encodeMove(move)
63+
64+
if (seen.contains(encodedMove)) {
65+
continue
66+
}
67+
seen.add(encodedMove)
68+
69+
val tmpStep = Step(step.cost + 1, move)
70+
deque.add(tmpStep)
71+
}
72+
}
73+
74+
error("We should have found an end state")
75+
}
76+
77+
private fun encodeMove(move: State): Map<String, Int> {
78+
val encodedMove = mutableMapOf<String, Int>()
79+
encodedMove["elevator"] = move.elevator
80+
encodedMove["floor_01_generator"] = move.floorOne.filter { it.endsWith(GENERATOR) }.size
81+
encodedMove["floor_01_microchip"] = move.floorOne.filter { it.endsWith(MICROCHIP) }.size
82+
encodedMove["floor_02_generator"] = move.floorTwo.filter { it.endsWith(GENERATOR) }.size
83+
encodedMove["floor_02_microchip"] = move.floorTwo.filter { it.endsWith(MICROCHIP) }.size
84+
encodedMove["floor_03_generator"] = move.floorThree.filter { it.endsWith(GENERATOR) }.size
85+
encodedMove["floor_03_microchip"] = move.floorThree.filter { it.endsWith(MICROCHIP) }.size
86+
encodedMove["floor_04_generator"] = move.floorFour.filter { it.endsWith(GENERATOR) }.size
87+
encodedMove["floor_04_microchip"] = move.floorFour.filter { it.endsWith(MICROCHIP) }.size
88+
return encodedMove
89+
}
90+
91+
private fun getAvailableMoves(state: State): List<State> {
92+
val availableStates = mutableListOf<State>()
93+
94+
// Elevator can go up, add the up states from current state
95+
if (FLOOR_ONE == state.elevator || FLOOR_TWO == state.elevator || FLOOR_THREE == state.elevator) {
96+
val nextElevator = state.elevator + 1
97+
val moves = getValidMoves(state.elevator, nextElevator, state)
98+
availableStates.addAll(moves)
99+
}
100+
101+
// Elevator can go down, add the down states from current state
102+
if (FLOOR_TWO == state.elevator || FLOOR_THREE == state.elevator || FLOOR_FOUR == state.elevator) {
103+
val nextElevator = state.elevator - 1
104+
val moves = getValidMoves(state.elevator, nextElevator, state)
105+
availableStates.addAll(moves)
106+
}
107+
108+
return availableStates
109+
}
110+
111+
private fun getValidMoves(
112+
currentElevator: Int,
113+
nextElevator: Int,
114+
state: State,
115+
): List<State> {
116+
val validMoves = mutableListOf<State>()
117+
val currentFloor =
118+
when (currentElevator) {
119+
FLOOR_ONE -> state.floorOne.toList()
120+
FLOOR_TWO -> state.floorTwo.toList()
121+
FLOOR_THREE -> state.floorThree.toList()
122+
else -> state.floorFour.toList()
123+
}
124+
125+
val nextFloor =
126+
when (nextElevator) {
127+
FLOOR_ONE -> state.floorOne.toList()
128+
FLOOR_TWO -> state.floorTwo.toList()
129+
FLOOR_THREE -> state.floorThree.toList()
130+
else -> state.floorFour.toList()
131+
}
132+
// 1. build known movement combos
133+
val movementCombinations = buildMovementCombinations(currentFloor)
134+
135+
// 2. for each movement combo
136+
for (movement in movementCombinations) {
137+
val currentFloorToBe = mutableListOf<String>()
138+
for (item in currentFloor) {
139+
if (!movement.contains(item)) {
140+
currentFloorToBe.add(item)
141+
}
142+
}
143+
val nextFloorToBe = movement.toMutableList()
144+
nextFloorToBe.addAll(nextFloor)
145+
// 3. validate the floor left behind
146+
val newCurrentFloorValid = floorIsValid(currentFloorToBe)
147+
// 4. validate the floor to be
148+
val newNextFloorValid = floorIsValid(nextFloorToBe)
149+
150+
if (newCurrentFloorValid && newNextFloorValid) {
151+
validMoves.add(buildNextState(currentElevator, nextElevator, currentFloorToBe, nextFloorToBe, state))
152+
}
153+
}
154+
155+
return validMoves
156+
}
157+
158+
@Suppress("NestedBlockDepth", "CyclomaticComplexMethod")
159+
private fun buildMovementCombinations(currentFloor: List<String>): MutableList<MutableList<String>> {
160+
// get possible movement combos
161+
var movementCombinations = mutableListOf<MutableList<String>>()
162+
163+
// 1. We can move individual items
164+
for (item in currentFloor) {
165+
movementCombinations.add(mutableListOf(item))
166+
}
167+
168+
for (index01 in 0..<currentFloor.size) {
169+
for (index02 in index01..<currentFloor.size) {
170+
val thisItem = currentFloor[index01]
171+
val nextItem = currentFloor[index02]
172+
if (thisItem == nextItem) {
173+
continue
174+
}
175+
176+
if (thisItem.endsWith(GENERATOR) && nextItem.endsWith(GENERATOR)) {
177+
// 2. we can move 2 generators
178+
movementCombinations.add(mutableListOf(thisItem, nextItem))
179+
} else if (thisItem.endsWith(MICROCHIP) && nextItem.endsWith(MICROCHIP)) {
180+
// 3. we can move 2 microchips
181+
movementCombinations.add(mutableListOf(thisItem, nextItem))
182+
} else if (thisItem.endsWith(GENERATOR) && nextItem.endsWith(MICROCHIP)) {
183+
// 4. we can move a microchip and generator if they are the same element
184+
val element = thisItem.split(" ")[0]
185+
if (nextItem.startsWith(element)) {
186+
movementCombinations.add(mutableListOf(thisItem, nextItem))
187+
}
188+
} else if (thisItem.endsWith(MICROCHIP) && nextItem.endsWith(GENERATOR)) {
189+
val element = nextItem.split(" ")[0]
190+
if (thisItem.startsWith(element)) {
191+
movementCombinations.add(mutableListOf(thisItem, nextItem))
192+
}
193+
} else {
194+
error("Weird additional combination")
195+
}
196+
}
197+
}
198+
return movementCombinations
199+
}
200+
201+
private fun buildNextState(
202+
currentElevator: Int,
203+
nextElevator: Int,
204+
currentFloorToBe: MutableList<String>,
205+
nextFloorToBe: MutableList<String>,
206+
state: State,
207+
): State {
208+
val validState: State
209+
if (FLOOR_ONE == currentElevator && FLOOR_TWO == nextElevator) {
210+
validState =
211+
State(
212+
nextElevator,
213+
currentFloorToBe.toSet(),
214+
nextFloorToBe.toSet(),
215+
state.floorThree,
216+
state.floorFour,
217+
)
218+
} else if (FLOOR_TWO == currentElevator && FLOOR_THREE == nextElevator) {
219+
validState =
220+
State(
221+
nextElevator,
222+
state.floorOne,
223+
currentFloorToBe.toSet(),
224+
nextFloorToBe.toSet(),
225+
state.floorFour,
226+
)
227+
} else if (FLOOR_THREE == currentElevator && FLOOR_FOUR == nextElevator) {
228+
validState =
229+
State(
230+
nextElevator,
231+
state.floorOne,
232+
state.floorTwo,
233+
currentFloorToBe.toSet(),
234+
nextFloorToBe.toSet(),
235+
)
236+
} else if (FLOOR_FOUR == currentElevator && FLOOR_THREE == nextElevator) {
237+
validState =
238+
State(
239+
nextElevator,
240+
state.floorOne,
241+
state.floorTwo,
242+
nextFloorToBe.toSet(),
243+
currentFloorToBe.toSet(),
244+
)
245+
} else if (FLOOR_THREE == currentElevator && FLOOR_TWO == nextElevator) {
246+
validState =
247+
State(
248+
nextElevator,
249+
state.floorOne,
250+
nextFloorToBe.toSet(),
251+
currentFloorToBe.toSet(),
252+
state.floorFour,
253+
)
254+
} else {
255+
validState =
256+
State(
257+
nextElevator,
258+
nextFloorToBe.toSet(),
259+
currentFloorToBe.toSet(),
260+
state.floorThree,
261+
state.floorFour,
262+
)
263+
}
264+
return validState
265+
}
266+
267+
@Suppress("ReturnCount", "CyclomaticComplexMethod")
268+
private fun floorIsValid(floor: List<String>): Boolean {
269+
// 1. if floor is empty or one item return true
270+
if (floor.size == 0 || floor.size == 1) {
271+
return true
272+
}
273+
274+
// 2. if all items are generators return true
275+
var allGenerators = true
276+
for (item in floor) {
277+
if (!item.endsWith(GENERATOR)) {
278+
allGenerators = false
279+
break
280+
}
281+
}
282+
if (allGenerators) {
283+
return true
284+
}
285+
286+
// 3. if all items are microchips return true
287+
var allMicrochips = true
288+
for (item in floor) {
289+
if (!item.endsWith(MICROCHIP)) {
290+
allMicrochips = false
291+
break
292+
}
293+
}
294+
if (allMicrochips) {
295+
return true
296+
}
297+
298+
// 4. for each chip, does it have a generator ,all paired return true
299+
var chipsHaveGenerators = true
300+
for (item in floor) {
301+
if (item.endsWith(MICROCHIP)) {
302+
val element = item.split("-")[0]
303+
if (!floor.contains(element + " " + GENERATOR)) {
304+
chipsHaveGenerators = false
305+
break
306+
}
307+
}
308+
}
309+
if (chipsHaveGenerators) {
310+
return true
311+
}
312+
return false
313+
}
314+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
package io.github.andrewfitzy.day11
3+
4+
import getFileContentForDay
5+
import java.lang.IllegalStateException
6+
import kotlin.test.Test
7+
import kotlin.test.assertEquals
8+
import kotlin.test.assertNotNull
9+
10+
class TestTask02 {
11+
@Test
12+
fun testSolveWithDemoData_01() {
13+
// Given
14+
val fileContent =
15+
listOf(
16+
"The first floor contains a hydrogen-compatible microchip and a lithium-compatible microchip.",
17+
"The second floor contains a hydrogen generator.",
18+
"The third floor contains a lithium generator.",
19+
"The fourth floor contains nothing relevant.",
20+
)
21+
val classUnderTest = Task02(fileContent)
22+
23+
// When
24+
var result = -1
25+
var exception: IllegalStateException? = null
26+
try {
27+
result = classUnderTest.solve() // this is no longer possible to solvewith task 2
28+
} catch (e: IllegalStateException) {
29+
exception = e
30+
}
31+
32+
// Then
33+
assertEquals(-1, result)
34+
assertNotNull(exception)
35+
}
36+
37+
@Test
38+
fun testSolveWithRealDate_01() {
39+
// Given
40+
val fileContent = getFileContentForDay("11")
41+
val classUnderTest = Task02(fileContent)
42+
43+
// When
44+
val result = classUnderTest.solve()
45+
46+
// Then
47+
assertEquals(71, result)
48+
}
49+
}

0 commit comments

Comments
 (0)