Advent of Code 2022, Day 5 - Part Two
This image is generated using Dall-EPrompt: Generate an image of elves unloading a ship using a yellow cargo crane in a minimalistic flat style
Preface
Now that we have the following data structure from our previous post, we can start by thinking about the business logic to check how many clean up ranges are completely inside another clean up range.
The data structures our sanitizer provides, are the stacks and the instructions.
1
2
3
4
5
[
Stack('N', 'Z'), // Stack 1
Stack('D', 'C', 'M'), // Stack 2
Stack('P') // Stack 3
]
1
2
3
4
5
6
[
Triple(1, 2, 1),
Triple(3, 1, 3),
Triple(2, 2, 1),
Triple(1, 1, 2)
]
Design
If we look at the difference between part one and part two, we see that the only changed requirement is the fact that the items that are moved need to stay in the same order.
So where our previous instruction would look like this
1
2
3
4
5
6
private fun moveItem(noItemsToMove: Int, stackToRetrieveItem: Stack<Char>, stackToStoreItem: Stack<Char>) {
for (i in 1..noItemsToMove) {
val item = stackToRetrieveItem.pop()
stackToStoreItem.push(item)
}
}
Where we take the items from the stackToRetrieve
and put it into the stackToStoreItem
immediatly. Now we need to use an intermediate value to store the items we take from the stackToRetrieve
.
1
2
3
4
5
6
7
8
9
10
11
private fun moveItem(noItemsToMove: Int, stackToRetrieveItem: Stack<Char>, stackToStoreItem: Stack<Char>) {
var items = ""
for (i in 1..noItemsToMove) {
items += stackToRetrieveItem.pop()
}
items.forEach {
stackToStoreItem.push(it)
}
}
But, you might say, this will result in exactly the same behaviour as before. Well that’s right! So there is one crucial step we need to take before pushing the value into the stackToStoreItem
and that is the reversed
method on the items
.
1
2
3
4
5
6
7
8
9
10
11
private fun moveItem(noItemsToMove: Int, stackToRetrieveItem: Stack<Char>, stackToStoreItem: Stack<Char>) {
var items = ""
for (i in 1..noItemsToMove) {
items += stackToRetrieveItem.pop()
}
items.reversed().forEach {
stackToStoreItem.push(it)
}
}
Implementation
Business logic
Now we know what to change as opposed to part one, we can start implementing the getResult
in the same way as part one. And use the moveItem
as we’ve discussed in the design above.
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
class PartTwo(
val sanitizer: Sanitizer
) {
fun getResult(): String {
val stacks = sanitizer.getStacks()
sanitizer.getInstructions()?.forEach {
val noItemsToMove = it.first
val stackToRetrieveItemIndex = it.second - 1
val stackToStoreItemIndex = it.third - 1
val stackToRetrieveItem = stacks.get(stackToRetrieveItemIndex)
val stackToStoreItem = stacks.get(stackToStoreItemIndex)
moveItem(noItemsToMove, stackToRetrieveItem, stackToStoreItem)
}
return stacks.joinToString("") {
it.peek().toString()
}
}
/***
* Move a specific number of items from one stack to another and remove them from the
* stack where the items are moved from.
*
* @param noItemsToMove the number of items to move
* @param stackToRetrieveItem the stack where the items are moved from
* @param stackToStoreItem the stack where the items are moved to
*/
private fun moveItem(noItemsToMove: Int, stackToRetrieveItem: Stack<Char>, stackToStoreItem: Stack<Char>) {
var items = ""
for (i in 1..noItemsToMove) {
items += stackToRetrieveItem.pop()
}
items.reversed().forEach {
stackToStoreItem.push(it)
}
}
}
Test case
We have written our business logic, and for our test input we now that the expected output for the test data is MCD
. So we can start to write our test case.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class PartTwoTest {
@Test
fun testGetResult() {
// Arrange
val resource = {}::class.java.getResource("/input.txt")
val sanitizer = Sanitizer(resource)
val sut = PartTwo(sanitizer)
val expectedResult = "MCD"
// Act
val result = sut.getResult()
// Assert
assertEquals(expectedResult, result)
}
}
Categories
Related articles