Coverage Summary for Class: Schematic (day03p1)

Class Method, % Line, %
Schematic 100% (5/5) 100% (17/17)
Schematic$Companion 100% (1/1) 100% (1/1)
Schematic$partNumbers$1 100% (1/1) 100% (3/3)
Schematic$partNumbers$1$1 100% (1/1) 100% (4/4)
Schematic$partNumbers$1$2 100% (1/1) 100% (1/1)
Total 100% (9/9) 100% (26/26)


 package day03p1
 
 data class Schematic(val lines: List<String>) {
     private val numberRegex = """(\d+)""".toRegex()
 
     fun partNumbers(): List<PartNumber> {
         return lines.asSequence().flatMapIndexed { row, line ->
             numberRegex.findAll(line)
                 .mapNotNull {
                     val group = it.groups[1] ?: error("no group")
                     if (!isAdjacentToSymbol(row, group.range)) {
                         return@mapNotNull null
                     }
                     group.value.toInt()
                 }
                 .map { PartNumber(it) }
         }
             .toList()
     }
 
     fun isAdjacentToSymbol(row: Int, indexRange: IntRange): Boolean {
         val encapsulatingRange = indexRange.first - 1..indexRange.last + 1
         if (isSymbol(row, encapsulatingRange.first) || isSymbol(row, encapsulatingRange.last)) {
             return true
         }
         if (row != 0 && encapsulatingRange.any { isSymbol(row - 1, it) }) {
             return true
         }
         if (row < lines.size && encapsulatingRange.any { isSymbol(row + 1, it) }) {
             return true
         }
         return false
     }
 
     private fun isSymbol(row: Int, col: Int): Boolean {
         if (row < 0 || row >= lines.size) return false
         val line = lines[row]
         if (col < 0 || col >= line.length) return false
         val char = line[col]
         return char != '.' && !char.isDigit()
     }
 
     companion object {
         fun fromString(raw: String): Schematic {
             return Schematic(raw.lines())
         }
     }
 
 }
 
 data class PartNumber(val value: Int)