# Advent of Code 2022, Day 4 - 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 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)
}
}