Advent of Code 2022, Day three - Part One
This image is generated using Dall-EPrompt: 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