package aoc2023

import println
import readInput
import splitInts

/*
--- Day 4: Scratchcards ---
https://adventofcode.com/2023/day/4
*/
fun main() {

    val inlineTestInput = """
Card 1: 41 48 83 86 17 | 83 86  6 31 17  9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
Card 3:  1 21 53 59 44 | 69 82 63 72 16 21 14  1
Card 4: 41 92 73 84 69 | 59 84 76 51 58  5 54 83
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
"""

    fun part1(input: List<String>): Int {
        return input.map {
            "Card +(\\d+): (.*)".toRegex().matchEntire(it)?.destructured
                ?.let { (id, game) ->
                    val (wins, numbers) = game.split(" | ")
                    val winSet = wins.splitInts().toSet()
                    val numberSet = numbers.splitInts().toSet()
                    val matches = winSet.intersect(numberSet)
                    1 shl (matches.size - 1)
                }
        }.sumOf { it!! }
    }

    fun part2(input: List<String>): Int {
        val winTable = input.map {
            "Card +(\\d+): (.*)".toRegex().matchEntire(it)?.destructured
                ?.let { (id, game) ->
                    val (wins, numbers) = game.split(" | ")
                    val winSet = wins.splitInts().toSet()
                    val numberSet = numbers.splitInts().toSet()
                    val matches = winSet.intersect(numberSet)
                    matches.size
                }!!
        }.toList()
        val accTable = IntArray(winTable.size)
        for (i in winTable.indices.reversed()) {
            var sum = 1
            for (j in i + 1 until (i + winTable[i] + 1).coerceAtMost(winTable.size)) {
                sum += accTable[j]
            }
            accTable[i] = sum
        }
        return accTable.sum()
    }

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

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