package aoc2023

import CharGrid
import println
import readInput

/*
--- Day 3: Gear Ratios ---
https://adventofcode.com/2023/day/3
*/
fun main() {

    val inlineTestInput = """
467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..
"""

    fun part1(input: List<String>): Int {
        val wholeGrid = CharGrid(input)
        val symbolGrid = wholeGrid.copyOf()
        symbolGrid.apply { c -> if (!c.isDigit()) c else '.' }
        val grownGrid = symbolGrid.grow(CharGrid.BOX_POS, { c -> c != '.' })
        var sum = 0
        wholeGrid.applyWithPos { grid, pos ->
            if (grid[pos].isDigit() && grownGrid[pos] != '.') {
                val (num, relpos) = grid.getHorizNumberValueAt(pos)
                relpos.forEach { grid[pos.translate(it)] = '.' }
                sum += num
            }
            null
        }
        return sum
    }

    fun part2(input: List<String>): Int {
        val wholeGrid = CharGrid(input)
        var sum = 0
        wholeGrid.applyWithPos { grid, col, row ->
            if (grid[col, row] == '*') {
                val numbers = CharGrid.BOX_POS.asSequence()
                    .map { it.translate(col, row) }
                    .filter { grid[it.dc, it.dr].isDigit() }
                    .map { grid.getHorizNumberRelPosAt(it.dc, it.dr)[0].translate(it.dc, it.dr) }
                    .distinct()
                    .toList()
                if (numbers.size == 2) {
                    sum += grid.getHorizNumberValueAt(numbers[0]).first * grid.getHorizNumberValueAt(numbers[1]).first
                }
            }
            null
        }
        return sum
    }

    // test if implementation meets criteria from the description, like:
    val testInput = inlineTestInput.trim().reader().readLines()
    //val testInput = readInput("aoc2023/Day03_test")
    val testInputPart1Result = part1(testInput)
    println("Part 1 Test: $testInputPart1Result")
    val testInputPart2Result = part2(testInput)
    println("Part 2 Test: $testInputPart2Result")
    check(testInputPart1Result == 4361)
    check(testInputPart2Result == 467835)

    val input = readInput("aoc2023/Day03")
    part1(input).println()
    part2(input).println()
}
