// --- Part Two ---
// The engineer finds the missing part and installs it in the engine! As the
// engine springs to life, you jump in the closest gondola, finally ready to
// ascend to the water source.
//
// You don't seem to be going very fast, though. Maybe something is still
// wrong? Fortunately, the gondola has a phone labeled "help", so you pick it
// up and the engineer answers.
//
// Before you can explain the situation, she suggests that you look out the
// window. There stands the engineer, holding a phone in one hand and waving
// with the other. You're going so slowly that you haven't even left the
// station. You exit the gondola.
//
// The missing part wasn't the only issue - one of the gears in the engine is
// wrong. A gear is any * symbol that is adjacent to exactly two part numbers.
// Its gear ratio is the result of multiplying those two numbers together.
//
// This time, you need to find the gear ratio of every gear and add them all up
// so that the engineer can figure out which gear needs to be replaced.
//
// Consider the same engine schematic again:
//
// 467..114..
// ...*......
// ..35..633.
// ......#...
// 617*......
// .....+.58.
// ..592.....
// ......755.
// ...$.*....
// .664.598..
//
// In this schematic, there are two gears. The first is in the top left; it has
// part numbers 467 and 35, so its gear ratio is 16345. The second gear is in
// the lower right; its gear ratio is 451490. (The * adjacent to 617 is not a
// gear because it is only adjacent to one part number.) Adding up all of the
// gear ratios produces 467835.
//
// What is the sum of all of the gear ratios in your engine schematic?

import java.io.*
import kotlin.io.*
import kotlin.math.*

val RGX = Regex("(\\d+)")
val lines = File("input.txt").readLines()
val lineLength = lines[0].length
val result = lines.withIndex().flatMap { (lineIdx, line) ->
  RGX.findAll(line).flatMap {
    val num = it.groupValues[1].toInt()
    val range = it.range
    val expandedRange = max(range.start - 1, 0)..min(range.endInclusive + 1, line.length - 1)
    sequence {
      if (lineIdx > 0) {
        val prevLine = lines[lineIdx - 1]
        yieldAll(expandedRange.filter { prevLine[it] == '*' }.map { lineLength * (lineIdx - 1) + it })
      }
      if (lineIdx + 1 < lines.size) {
        val nextLine = lines[lineIdx + 1]
        yieldAll(expandedRange.filter { nextLine[it] == '*' }.map { lineLength * (lineIdx + 1) + it })
      }
      if (line[expandedRange.start] == '*') {
        yield(lineLength * lineIdx + expandedRange.start)
      }
      if (line[expandedRange.endInclusive] == '*') {
        yield(lineLength * lineIdx + expandedRange.endInclusive)
      }
    }.map { it to num }
  }
}.groupBy({ it.first }, { it.second }).values.filter { it.size == 2 }.sumOf { it.reduce(Int::times) }
println(result)
