Post

Advent of Code 2022, Day 3 - Part One

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