Post

Advent of Code 2022, Day 5 - Part Two

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)
    }
}
This post is licensed under CC BY 4.0 by the author.