From ecaf90adb4cbcabbf5beb4c2a062e4f7a0f26c27 Mon Sep 17 00:00:00 2001 From: Ivan Reshetnikov Date: Mon, 5 Feb 2024 00:27:57 +0500 Subject: [PATCH] AOC: solve 5th task --- .../src/main/kotlin/Solution05.kt | 154 ++++++++---------- .../src/test/kotlin/Solution05Test.kt | 11 +- 2 files changed, 75 insertions(+), 90 deletions(-) diff --git a/advent-of-code-2023/src/main/kotlin/Solution05.kt b/advent-of-code-2023/src/main/kotlin/Solution05.kt index 7b441cd..a2e06c3 100644 --- a/advent-of-code-2023/src/main/kotlin/Solution05.kt +++ b/advent-of-code-2023/src/main/kotlin/Solution05.kt @@ -45,103 +45,91 @@ class Solution05(lines: List) { // Part 2. fun getLowestLocationNumberForRangeOfSeeds(): Long { - var ranges = mutableListOf() + var ranges = mutableListOf() for (i in seeds.indices step 2) { - ranges.add( - RangeWithFirstSeed( - LongRange(seeds[i], seeds[i] + seeds[i+1] - 1), - seeds[i], - ) - ) + ranges.add(LongRange(seeds[i], seeds[i] + seeds[i+1] - 1)) } + for (map in maps) { - val newList = mutableListOf() + var minCheckedValue = Long.MIN_VALUE + val newList = mutableListOf() - for (mapper in map.mappers) { - for (rng in ranges) { - val maxStart = max(mapper.sourceRangeStart, rng.range.first) - val minEndExcl = min(mapper.sourceRangeStart + mapper.rangeLength, rng.range.last + 1) + for (mapper in map.mappers.sortedBy{it.sourceRangeStart}) { + // + val mapperEnd = mapper.sourceRangeStart + mapper.rangeLength - 1 + val diff = mapper.destRangeStart - mapper.sourceRangeStart - val len = minEndExcl - maxStart - if (len > 0L) { + // find all values before this range. + val unmatchedValues = ranges + .filter{it.first < mapper.sourceRangeStart} + .map{LongRange(max(minCheckedValue, it.first), min(mapper.sourceRangeStart - 1, it.last))} + .filter{it.first <= it.last} + newList.addAll(unmatchedValues) - val destStart = mapper.destRangeStart + (maxStart - mapper.sourceRangeStart) - newList.add( - RangeWithFirstSeed( - LongRange(destStart, destStart + len - 1), - rng.firstSeedInRange - ) + // map values + val overlappingLeftValues = ranges + .filter{it.first < mapper.sourceRangeStart && it.last >= mapper.sourceRangeStart && it.last <= mapperEnd} + .map{ + LongRange( + max(mapper.sourceRangeStart, it.first) + diff, + it.last + diff, ) } - } + .filter{it.first <= it.last} + newList.addAll(overlappingLeftValues) + + val overlappingMiddleValues = ranges + .filter{it.first >= mapper.sourceRangeStart && it.last <= mapperEnd} + .map{ + LongRange( + it.first + diff, it.last + diff + ) + } + .filter{it.first <= it.last} + newList.addAll(overlappingMiddleValues) + + val overlappingRightValues = ranges + .filter{it.first >= mapper.sourceRangeStart && it.first <= mapperEnd && it.last >= mapperEnd} + .map{ + LongRange( + it.first + diff, + min(it.last, mapperEnd) + diff + ) + } + .filter{it.first <= it.last} + newList.addAll(overlappingRightValues) + + val bigRanges = ranges + .filter{it.first < mapper.sourceRangeStart && it.last > mapperEnd} + .map{ + LongRange( + mapper.sourceRangeStart + diff, mapperEnd + diff + ) + } + .filter{it.first <= it.last} + newList.addAll(bigRanges) + + minCheckedValue = mapperEnd + 1 } - // понять, какие диапазоны значений остались неиспользованными, - // тоже добавить их в список. - - val mprs = ListOfRanges(map.mappers.map{LongRange(it.sourceRangeStart, it.sourceRangeStart + it.rangeLength - 1)}.toMutableList()) - - for (rng in ranges) { - var currentRange = rng.range - - var intrs = mprs - .findIntersectingRanges(rng.range) - .sortedBy{it.first} - - if (intrs.isEmpty()) { - newList.add( - rng - ) - continue - } - - for (i in intrs.indices) { - if (intrs[i].start > currentRange.start) { - newList.add( - RangeWithFirstSeed( - LongRange(intrs[i].start, currentRange.start - 1), - rng.firstSeedInRange - ) + newList.addAll( + ranges + .filter{it.last >= minCheckedValue} + .map{ + LongRange( + max(it.first, minCheckedValue), + it.last, ) } - - var end = intrs[i].last - - if (i < intrs.size - 1 && intrs[i].last < intrs[i+1].first - 1) { - end = intrs[i+1].first - 1 - - newList.add( - RangeWithFirstSeed( - LongRange(intrs[i].last + 1, intrs[i+1].first - 1), - rng.firstSeedInRange, - ) - ) - } - - currentRange = LongRange(end + 1, currentRange.endInclusive) - } - - if (!currentRange.isEmpty()) { - newList.add( - RangeWithFirstSeed( - currentRange, - rng.firstSeedInRange - ) - ) - } - } + ) ranges = newList - .map{ - if (it.range.last >= it.range.first) it else RangeWithFirstSeed(LongRange(it.range.last, it.range.first), it.firstSeedInRange) - } - .toMutableList() } return ranges - .map{it.range.first} + .map{it.first} .min() } } @@ -177,13 +165,3 @@ data class Mapper(val sourceRangeStart: Long, val destRangeStart: Long, val rang return num >= sourceRangeStart && num < sourceRangeStart + rangeLength } } - -data class RangeWithFirstSeed(val range: LongRange, val firstSeedInRange: Long) { -} - -data class ListOfRanges(val ranges: MutableList) { - fun findIntersectingRanges(other: LongRange): List { - return ranges - .filter{it.last >= other.first && it.first <= other.last} - } -} diff --git a/advent-of-code-2023/src/test/kotlin/Solution05Test.kt b/advent-of-code-2023/src/test/kotlin/Solution05Test.kt index 6eb58de..6a4edbd 100644 --- a/advent-of-code-2023/src/test/kotlin/Solution05Test.kt +++ b/advent-of-code-2023/src/test/kotlin/Solution05Test.kt @@ -20,13 +20,20 @@ class Solution05Test { fun testGetLowestLocationForRangeOfSeeds() { val text = ResourceReader().readFile("day-05/test.txt") val res = Solution05(text).getLowestLocationNumberForRangeOfSeeds() - assertEquals(46L, res) + assertEquals(46, res) } @Test fun solvePart2() { val text = ResourceReader().readFile("day-05/input.txt") val res = Solution05(text).getLowestLocationNumberForRangeOfSeeds() - println(res) + println("Your answer: $res") + } + + @Test + fun testPart2() { + val text = ResourceReader().readFile("day-05/input.txt") + val res = Solution05(text).getLowestLocationNumberForRangeOfSeeds() + assertEquals(15880236, res) } }