1
0

149 lines
4.7 KiB
Kotlin

package be.vandewalleh.aoc.days
import be.vandewalleh.aoc.utils.input.Day
import be.vandewalleh.aoc.utils.input.Groups
import org.eclipse.collections.api.multimap.list.ListMultimap
import org.eclipse.collections.api.multimap.list.MutableListMultimap
import org.eclipse.collections.api.multimap.set.MutableSetMultimap
import org.eclipse.collections.impl.factory.Multimaps
import org.eclipse.collections.impl.multimap.list.FastListMultimap
@Day(16)
class Day16(@Groups val input: List<List<String>>) {
private val rangesGroup = input[0]
private val myTicket = input[1][1]
private val nearbyTicketsGroup = input[2].drop(1)
private val rangeRe = "(\\d+)-(\\d+)".toRegex()
private fun extractRanges(line: String): Sequence<IntRange> = rangeRe.findAll(line)
.map {
val (min, max) = it.destructured
min.toInt()..max.toInt()
}
fun part1(): Int {
val minMax = mutableListOf<IntRange>()
for (line in rangesGroup) {
minMax.addAll(extractRanges(line))
}
val nearbyTickets = mutableListOf<Int>()
for (line in nearbyTicketsGroup) {
line.splitToSequence(",").forEach { nearbyTickets.add(it.toInt()) }
}
val invalid = mutableListOf<Int>()
nearbyTickets.forEach { number ->
if (minMax.none { number in it }) invalid.add(number)
}
return invalid.sum()
}
private fun isTicketValid(ticket: List<Int>, ranges: Iterable<IntRange>): Boolean {
for (number in ticket) {
if (!ranges.any { number in it }) return false
}
return true
}
private fun inRanges(value: Int, ranges: List<IntRange>) = ranges.any { value in it }
fun part2(): Long {
val rangesByName = namedRanges()
val validTickets = validTickets(rangesByName)
val indexesByCategory = indexesByCategory(rangesByName, validTickets)
removeDuplicates(indexesByCategory)
val myTicketValues = myTicket.split(",").map { it.toInt() }
var mult = 1L
indexesByCategory.forEachKeyValue { category, index ->
if (category.startsWith("departure")) {
mult *= myTicketValues[index]
}
}
return mult
}
private fun removeDuplicates(indexesByCategory: MutableSetMultimap<String, Int>) {
val toBeRemoved = HashSet<Int>()
val queue = ArrayDeque<Int>()
indexesByCategory.multiValuesView()
.toSortedListBy { it.size() }
.forEach { it.forEach { if (toBeRemoved.add(it)) queue.add(it) } }
queue.removeLast()
val categoriesToRemove = mutableListOf<String>()
while (queue.isNotEmpty()) {
val duplicate = queue.removeFirst()
categoriesToRemove.clear()
for (entry in indexesByCategory.keyMultiValuePairsView()) {
if (entry.two.size() < 2) continue
categoriesToRemove.add(entry.one)
}
for (category in categoriesToRemove) {
indexesByCategory.remove(category, duplicate)
}
}
}
private fun indexesByCategory(
rangesByName: ListMultimap<String, IntRange>,
validTickets: List<List<Int>>,
): MutableSetMultimap<String, Int> {
val indexesByCategory = Multimaps.mutable.set.empty<String, Int>()
for (entry in rangesByName.keyMultiValuePairsView()) {
val category = entry.one
val ranges = entry.two.toList()
for (i in validTickets.first().indices) {
var allInRange = true
for(ticket in validTickets){
val current = ticket[i]
if(!inRanges(current, ranges)){
allInRange = false
break
}
}
if (allInRange) indexesByCategory.put(category, i)
}
}
return indexesByCategory
}
private fun validTickets(rangesByName: MutableListMultimap<String, IntRange>): List<List<Int>> {
val validTickets = mutableListOf<List<Int>>()
val ranges = rangesByName.valuesView().toList()
for (line in nearbyTicketsGroup) {
val ticket = line.split(",").map { it.toInt() }
if (isTicketValid(ticket, ranges)) {
validTickets.add(ticket)
}
}
return validTickets
}
private fun namedRanges(): MutableListMultimap<String, IntRange> {
val rangesByName = FastListMultimap<String, IntRange>()
for (line in rangesGroup) {
val name = line.substringBefore(":")
extractRanges(line).forEach {
rangesByName.put(name, it)
}
}
return rangesByName
}
}