Advent of Code 2022, Day four - Part One
This image is generated using Dall-EPrompt: Generate an image of elves unloading a big ship of supplies 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 structure our sanitizer provides, looks like this.
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
[
{ // Line 1
{ 2, 4 }, // First elve
{ 6, 8 } // Second elve
},
{ // Line 2
{ 2, 3 }, // First elve
{ 4, 5 } // Second elve
},
{ // Line 3
{ 5, 7 }, // First elve
{ 7, 9 } // Second elve
},
{ // Line 4
{ 2, 8 }, // First elve
{ 3, 7 } // Second elve
},
{ // Line 5
{ 6, 6 }, // First elve
{ 4, 6 } // Second elve
},
{ // Line 6
{ 2, 6 }. // First elve
{ 4, 8 } // Second elve
}
]
Design
Now we have each range for each elve seperated we can start to think about how we want to achieve the check wheter or not one range is completely inside another range.
We can do this by checking if one pair is completely inside another pair, in pseudo-code this would look something like this.
1
2
3
4
5
6
fun isRangeInsideAnother(rangeA: Pair<Int, Int>, rangeB: Pair<Int, Int>): Boolean {
val isStartInsideRangeA = rangeB.first >= rangeA.first && rangeB.first <= rangeA.second
val isEndInsideRangeA = rangeB.second >= rangeA.first && rangeB.second <= rangeA.second
return isStartInsideRangeA && isEndInsideRangeA
}
For this to work both ways, we need to call the above method twice in the order of isRangeInsideAnotherRange(rangeA, rangeB)
and isRangeInsideAnotherRange(rangeB, rangeA)
. And if we then or
the result, we get the expected behaviour.
Implementation
Business logic
Now we have an idea of our design, 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
class PartOne(
private val sanitizer: Sanitizer
) {
fun getResult(): Int {
val rangesThatOverlapCompletely = sanitizer
.getItems()
?.filter { // Step 1
isRangeInsideAnother(it.first, it.second) ||
isRangeInsideAnother(it.second, it.first)
}
?.count()
return rangesThatOverlapCompletely ?: -1
}
/***
* Check if rangeB is completely inside rangeA
*
* @param rangeA the range that is used to check rangeB against
* @param rangeB the range that is checked against rangeA
*/
private fun isRangeInsideAnother(rangeA: Pair<Int, Int>, rangeB: Pair<Int, Int>): Boolean {
val isStartInsideRangeA = rangeB.first >= rangeA.first && rangeB.first <= rangeA.second
val isEndInsideRangeA = rangeB.second >= rangeA.first && rangeB.second <= rangeA.second
return isStartInsideRangeA && isEndInsideRangeA
}
}
At Step 1 we filter the input elements based on the overlapping ranges. We do this by validating that rangeB is inside rangeA or if rangeA is inside rangeB.
Test case
We have written our business logic, and for our test input we now that the expected number of overlapping ranges is 2. So 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 expectedNumberOfOverlappingPairs = 2
// Act
val result = sut.getResult()
// Assert
assertEquals(expectedNumberOfOverlappingPairs, result)
}
}
Categories
Related articles