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

Advent of Code 2022, Day three - Part One

Advent of Code 2022, Day three - Part One
This image is generated using Dall-E
  • Prompt: Generate an image of elves walking through the forest carrying big green backpacks 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 retrieve the code that’s inside all the rucksacks.

    The data structure our sanitizer provides, looks like this.

    1
    2
    3
    4
    5
    6
    7
    8
    
    [
        { "vJrwpWtwJgWr"    , "hcsFMMfFFhFp"     }, // Rucksack 1
        { "jqHRNqRjqzjGDLGL", "rsFMfFZSrLrFZsSL" }, // Rucksack 2
        { "PmmdzqPrV"       , "vPwwTWBwg"        }, // Rucksack 3
        { "wMqvLMZHhHMvwLH" , "jbvcjnnSBnvTQFn"  }, // Rucksack 4
        { "ttgJtRGJ"        , "QctTZtZT"         }, // Rucksack 5
        { "CrZsJsPPZsGz"    , "wwsLwLmpwMDw"     }  // Rucksack 6
    ]
    

    Design

    So now that we have our rucksacks with each compartment seperated, we can think about how we want to setup our business logic. So to retrieve the code from all the rucksacks, we need to get the unique character that’s present in both compartments.

    So functionally, our design will look something like this.

    flowchart LR
    
        subgraph rsOne["Rucksack One"]
            direction LR
    
            rsOneCompartmentOne["vJrwpWtwJgWr"]
            rsOneCompartmentTwo["hcsFMMfFFhFp"]
        end
    
        subgraph rsTwo["Rucksack Two"]
            direction LR
    
            rsTwoCompartmentOne["jqHRNqRjqzjGDLGL"]
            rsTwoCompartmentTwo["rsFMfFZSrLrFZsSL"]
        end
    
        subgraph rsThree["Rucksack Three"]
            direction LR
    
            rsThreeCompartmentOne["PmmdzqPrV"]
            rsThreeCompartmentTwo["vPwwTWBwg"]
        end
    
        subgraph rsFour["Rucksack Four"]
            direction LR
    
            rsFourCompartmentOne["wMqvLMZHhHMvwLH"]
            rsFourCompartmentTwo["jbvcjnnSBnvTQFn"]
        end
    
        subgraph rsFive["Rucksack Five"]
            direction LR
    
            rsFiveCompartmentOne["ttgJtRGJ"]
            rsFiveCompartmentTwo["QctTZtZT"]
        end
    
        subgraph rsSix["Rucksack Six"]
            direction LR
    
            rsSixCompartmentOne["CrZsJsPPZsGz"]
            rsSixCompartmentTwo["wwsLwLmpwMDw"]
        end
    
    
        rsOneMatchingCharacter["p"]
        rsTwoMatchingCharacter["L"]
        rsThreeMatchingCharacter["P"]
        rsFourMatchingCharacter["v"]
        rsFiveMatchingCharacter["t"]
        rsSixMatchingCharacter["s"]
    
        sum["Sum()"]
    
        rsOneCompartmentOne --> findCharacterOne["FindMatchingCharacter(compA, compB)"]
        rsOneCompartmentTwo --> findCharacterOne["FindMatchingCharacter(compA, compB)"]
    
        rsTwoCompartmentOne --> findCharacterTwo["FindMatchingCharacter(compA, compB)"]
        rsTwoCompartmentTwo --> findCharacterTwo["FindMatchingCharacter(compA, compB)"]
    
        rsThreeCompartmentOne --> findCharacterThree["FindMatchingCharacter(compA, compB)"]
        rsThreeCompartmentTwo --> findCharacterThree["FindMatchingCharacter(compA, compB)"]
    
        rsFourCompartmentOne --> findCharacterFour["FindMatchingCharacter(compA, compB)"]
        rsFourCompartmentTwo --> findCharacterFour["FindMatchingCharacter(compA, compB)"]
    
        rsFiveCompartmentOne --> findCharacterFive["FindMatchingCharacter(compA, compB)"]
        rsFiveCompartmentTwo --> findCharacterFive["FindMatchingCharacter(compA, compB)"]
    
        rsSixCompartmentOne --> findCharacterSix["FindMatchingCharacter(compA, compB)"]
        rsSixCompartmentTwo --> findCharacterSix["FindMatchingCharacter(compA, compB)"]
    
        findCharacterOne --> rsOneMatchingCharacter
        findCharacterTwo --> rsTwoMatchingCharacter
        findCharacterThree --> rsThreeMatchingCharacter
        findCharacterFour --> rsFourMatchingCharacter
        findCharacterFive --> rsFiveMatchingCharacter
        findCharacterSix --> rsSixMatchingCharacter
    
        rsOneMatchingCharacter --> getPriorityOne["GetPriority(char)"]
        rsTwoMatchingCharacter --> getPriorityTwo["GetPriority(char)"]
        rsThreeMatchingCharacter --> getPriorityThree["GetPriority(char)"]
        rsFourMatchingCharacter --> getPriorityFour["GetPriority(char)"]
        rsFiveMatchingCharacter --> getPriorityFive["GetPriority(char)"]
        rsSixMatchingCharacter --> getPrioritySix["GetPriority(char)"]
    
        rsOnePriority["16"]
        rsTwoPriority["38"]
        rsThreePriority["42"]
        rsFourPriority["22"]
        rsFivePriority["20"]
        rsSixPriority["19"]
    
        getPriorityOne --> rsOnePriority
        getPriorityTwo --> rsTwoPriority
        getPriorityThree --> rsThreePriority
        getPriorityFour --> rsFourPriority
        getPriorityFive --> rsFivePriority
        getPrioritySix --> rsSixPriority
    
        rsOnePriority --> sum
        rsTwoPriority --> sum
        rsThreePriority --> sum
        rsFourPriority --> sum
        rsFivePriority --> sum
        rsSixPriority --> sum
    
        sum --> total["<b>Total</b> 157"]
    

    The priority is based on this information.

    Lowercase item types a through z have priorities 1 through 26.
    Uppercase item types A through Z have priorities 27 through 52.

    And if we sum up each priority of our item types, we get the expected output of 157

    Implementation

    Business logic

    Now we know what we want our code to do, let’s start implementing it in our PartOne class.

    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
    
    class PartOne(
        private val sanitizer: Sanitizer
    ) {
        fun getResult(): Int {
            val result = sanitizer.getItems()
                ?.map {
                    findMatchingCharacter(it.first, it.second)
                }?.sumOf {
                    getPriority(it)
                }
    
            return result ?: -1
        }
    
        /**
         * Find the first character in compartment A that is also
         * present in compartment B
         *
         * @param compA the first compartment of the rucksack
         * @param compB the second compartment of the rucksack
         * @return the character that is present in both compartments
         *         or an empty char
         */
        private fun findMatchingCharacter(compA: String, compB: String): Char {
            compA.forEach {
                if (compB.contains(it)) {
                    return it
                }
            }
    
            return Char.MIN_VALUE
        }
    
        /**
         * Get the priority of a character based on the following information
         *
         * Lowercase item types a through z have priorities 1 through 26.
         * Uppercase item types A through Z have priorities 27 through 52.
         *
         * @param character the character for which to get the property
         * @return the priority of the given character
         */
        private fun getPriority(character: Char): Int {
            val priorityAlphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
            return priorityAlphabet.indexOf(character) + 1
        }
    }
    

    In our code we use the sumOf method from Kotlin which allows us to return the sum of all values produced by the selector function. This is applied to each element in the collection (Jetbrains, n.d.). So this means that for each element the result of our getPriority method is summed up.

    In the getPriority method we get the priority based on the index plus one from our priority string. Because a - z is priority 1 - 27, we can base our priority on the length of the alphabet. We use the plus one, because the indexOf method is a 0-based index but we need the 1-based index position.

    Test case

    Now that we’ve established that the expected test output is 157 we can start setting up our test case.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    class PartOneTest {
        @Test
        fun testGetResult() {
            // Arrange
            val resource = {}::class.java.getResource("/input.txt")
            val sanitizer = Sanitizer(resource)
            val sut = PartOne(sanitizer)
            val expectedNumberOfPoints = 157
    
            // Act
            val result = sut.getResult()
    
            // Assert
            assertEquals(expectedNumberOfPoints, result)
        }
    }
    

    Categories

    Related articles

    Advent of Code 2022, Day three - Sanitizer

    Turn the input into a list of rucksacks.

    Advent of Code 2022, Day three - Part Two

    Find the item type that is common between two or more elves.