Advent of Code 2022, Day four - Part Two
This image is generated using Dall-EPrompt: Generate an image of elves unloading a big ship of supplies in a minimalistic flat style
Preface
In this part of the advent of code, we need to check if two ranges overlap at all. So we can take the code from our previous part and change a couple of lines.
Because the assignment is almost the same but with the only exception that we need to check how many ranges overlap at all compared to how many ranges overlap completely.
Design
Let’s take a look at our design from the previous post.
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
}
In this method we check if the start of rangeB and the end of rangeB is inside rangeA. The keyword here is and. If we change the operation by checking if the start of rangeB is inside rangeA or if the end of rangeB is inside rangeA we get all the ranges that overlap at some point. So it’ll look like this.
1
2
3
4
5
6
fun doesRangeOverlap(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 doesRangeOverlap(rangeA, rangeB)
and doesRangeOverlap(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 PartTwo 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 PartTwo(
private val sanitizer: Sanitizer
) {
fun getResult(): Int {
val rangesThatOverlap = sanitizer
.getItems()
?.filter { // Step 1
doesRangeOverlap(it.first, it.second) ||
doesRangeOverlap(it.second, it.first)
}
?.count()
return rangesThatOverlap ?: -1
}
/***
* Check if rangeB overlaps somewehere with rangeA
*
* @param rangeA the range that is used to check rangeB against
* @param rangeB the range that is checked against rangeA
*/
private fun doesRangeOverlap(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 overlaps with rangeA or if rangeA overlaps with rangeB.
Test case
We have written our business logic, and for our test input we now that the expected number of overlapping ranges is 4. 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 PartTwoTest {
@Test
fun testGetResult() {
// Arrange
val resource = {}::class.java.getResource("/input.txt")
val sanitizer = Sanitizer(resource)
val sut = PartTwo(sanitizer)
val expectedNumberOfOverlappingPairs = 4
// Act
val result = sut.getResult()
// Assert
assertEquals(expectedNumberOfOverlappingPairs, result)
}
}
Categories
Related articles