package aoc2024

import CharGrid
import RelPos
import println
import readInput

/*
--- Day 12: Garden Groups ---
https://adventofcode.com/2024/day/12
*/
fun main() {

    val inlineTestInput = """
RRRRIICCFF
RRRRIICCCF
VVRRRCCFFF
VVRCCCJFFF
VVVVCJJCFE
VVIVCCJJEE
VVIIICJJEE
MIIIIIJJEE
MIIISIJEEE
MMMISSJEEE
    """

    fun part1(input: List<String>): Int {
        val grid = CharGrid(input)
        var sum = 0
        for (p in grid.generateGridPos()) {
            val c = grid[p]
            if (c != '.') {
                val areaPos = HashSet<RelPos>()
                val fence = HashSet<RelPos>()
                var newPos = setOf(p)
                while (newPos.isNotEmpty()) {
                    val newSeeds = HashSet<RelPos>()
                    for (np in newPos) {
                        areaPos.add(np)
                        grid[np] = c.lowercaseChar()
                        newSeeds.addAll(grid.matchAbsoluteRelPos(np, CharGrid.PLUS_POS) { it == c })
                        fence.addAll(grid.matchRelative(np.dc, np.dr, CharGrid.PLUS_POS) { it.uppercaseChar() != c }
                            .map { RelPos(np.dc * 2 + it.dc, np.dr * 2 + it.dr) })
                    }
                    newPos = newSeeds
                }
                areaPos.forEach { grid[it] = '.' }
                sum += areaPos.size * fence.size
            }
        }
        return sum
    }

    fun part2(input: List<String>): Int {
        val grid = CharGrid(input)
        var sum = 0
        val magic = listOf(3, 5, 10, 12)
        val star = CharGrid.PLUS_POS.plus(CharGrid.CROSS_POS)
        for (p in grid.generateGridPos()) {
            val c = grid[p]
            if (c != '.') {
                val areaPos = HashSet<RelPos>()
                var newPos = setOf(p)
                while (newPos.isNotEmpty()) {
                    val newSeeds = HashSet<RelPos>()
                    for (np in newPos) {
                        areaPos.add(np)
                        grid[np] = '.'
                        newSeeds.addAll(grid.matchAbsoluteRelPos(np, CharGrid.PLUS_POS) { it == c })
                    }
                    newPos = newSeeds
                }
                val corners = areaPos.sumOf {
                    val b = star.mapIndexed { i, rp -> if (areaPos.contains(it.translate(rp))) 1 shl i else 0 }.sum()
                    val outer = magic.count { b and it == 0 }
                    val inner = magic.filterIndexed { i, m -> (b and m == m) && (b and (16 shl i) == 0) }.count()
                    outer + inner
                }
                sum += areaPos.size * corners
            }
        }
        return sum
    }

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

    val input = readInput("aoc2024/Day12")
    part1(input).println()
    part2(input).println()
}
