Bart Kessels
Bart Kessels
Passionate open source software engineer who loves to go backpacking

Advent of Code 2022, Day four - Part Two

Advent of Code 2022, Day four - Part Two
This image is generated using Dall-E
  • Prompt: 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

    Advent of Code 2022, Day four - Sanitizer

    Turn the input into a list of assigned compartments for the elves to unload.

    Advent of Code 2022, Day four - Part One

    Find the compartments that overlap between the different assignments.