class Day3 { fun solvePart1(input: String) = getOverlappingCells(parse(input)) .count() .toString() fun solvePart2(input: String): String { val claims = parse(input) val disqualified = getOverlappingCells(claims) return claims.first { claim -> claim.cells.none { it in disqualified } }.id.toString() } private fun parse(input: String) = input.lines().map(Claim.Companion::parse) private fun getOverlappingCells(claims: List): Set> = claims .flatMap { it.cells } .groupingBy { it } .eachCount() .filterValues { it > 1 } .keys } private data class Claim( val id: Int, val left: Int, val top: Int, val width: Int, val height: Int ) { val cells = (left until left + width).flatMap { x -> (top until top + height).map { y -> Pair(x, y) } } companion object { val pattern = Regex("""#(\d+) @ (\d+),(\d+): (\d+)x(\d+)""") fun parse(line: String): Claim { return (pattern .matchEntire(line) ?: throw IllegalArgumentException("Invalid input: $line")) .groupValues .drop(1) .map(String::toInt) .let { groups -> Claim(groups[0], groups[1], groups[2], groups[3], groups[4]) } } } }