Bart Kessels
Bart Kessels
Passionate open source software engineer who loves to go backpacking

Advent of Code 2022, Day 5 - Part Two

Advent of Code 2022, Day 5 - Part Two
This image is generated using Dall-E
  • Prompt: 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

    Advent of Code 2022, Day five - Sanitizer

    Turn the input into a list of stacks and another list of operations for the crane.

    Advent of Code 2022, Day five - Part One

    Execute the instructions to move the cargo from one stack to another.