149 lines
4.7 KiB
Kotlin
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
|
|
}
|
|
}
|