import java.io.File fun main(args: Array) { println("Hello day16!") solve() } data class RuleRange(val min: Int, val max: Int) data class Rule(val name: String, val ruleRangeFirst: RuleRange, val ruleRangeSecond: RuleRange) val numbersRegex = Regex("\\d+") fun readRules(ruleAsString: String): Rule { val (ruleName, rest) = ruleAsString.split(":") val ruleRanges = numbersRegex.findAll(rest) .flatMap { match -> match.groupValues.map { it.toInt() } } .chunked(2) .map { pair -> RuleRange(pair[0], pair[1]) } .toList() return Rule(ruleName, ruleRanges[0], ruleRanges[1]) } fun checkRuleRange(v: Int, ruleRange: RuleRange): Boolean = v >= ruleRange.min && v <= ruleRange.max fun errorRateIfViolated(v: Int, rules: List): Long { if (rules.any { rule -> checkRuleRange(v, rule.ruleRangeFirst) || checkRuleRange(v, rule.ruleRangeSecond) }) { return 0 } return v.toLong() } fun sumErrorRateOfViolations(ticket: String, rules: List): Long = ticket.split(",") .map { it.toInt() } .map { errorRateIfViolated(it, rules) } .sum() fun isValidValue(v: Int, rules: List): Boolean = rules.any { rule -> checkRuleRange(v, rule.ruleRangeFirst) || checkRuleRange(v, rule.ruleRangeSecond) } fun isValidTicket(ticket: String, rules: List): Boolean = ticket.split(",") .map { it.toInt() } .all { isValidValue(it, rules) } fun isRuleValidFor(rule: Rule, values: List): Boolean = values.all { v -> checkRuleRange(v, rule.ruleRangeFirst) || checkRuleRange(v, rule.ruleRangeSecond) } fun valuesForColumn(ticketValues: List>, col: Int) = ticketValues.map { it[col] } fun rulesPerColumn(ticketValues: List>, rules: List): MutableMap> { val ruleValidForColumn = mutableMapOf>() for (i in ticketValues[0].indices) { val columnValues = valuesForColumn(ticketValues, i) for (rule in rules) { if (isRuleValidFor(rule, columnValues)) { if (!ruleValidForColumn.containsKey(rule)) { ruleValidForColumn[rule] = mutableSetOf() } ruleValidForColumn[rule]?.add(i) } } } return ruleValidForColumn } fun ruleToColumnAssignment(validRulesPerColumns: MutableMap>): Map { val rulePerColumnAssignment = mutableMapOf() while (validRulesPerColumns.isNotEmpty()) { val currentAssignment = validRulesPerColumns.asSequence().filter { it.value.size == 1 }.first() val column = currentAssignment.value.first() rulePerColumnAssignment[currentAssignment.key] = column validRulesPerColumns.remove(currentAssignment.key) for (validRulPerColumns in validRulesPerColumns) { validRulPerColumns.value.remove(column) } } return rulePerColumnAssignment } fun solve() { val readLines = File("day16/input").readText(); val (ruleText, ticketWithLabel, nearbyWithLabel) = readLines.split("\n\n") val ticket = ticketWithLabel.split("\n")[1] val nearby = nearbyWithLabel.split("\n").drop(1).filter { it.isNotEmpty() } val rules = ruleText.split("\n").filter { it.isNotEmpty() }.map { readRules(it) } val errorRate = nearby.map { sumErrorRateOfViolations(it, rules) }.sum() println(errorRate) // part 2 val nearbyValidTickets = nearby.filter { isValidTicket(it, rules) } val nearbyTicketValues = nearbyValidTickets.map { t -> t.split(",").map { it.toInt() } } val validRulesPerColumns = rulesPerColumn(nearbyTicketValues, rules) val ruleToColumnAssignment = ruleToColumnAssignment(validRulesPerColumns) val ticketValues = ticket.split(",").map { it.toLong() } val departureProduct = ruleToColumnAssignment.entries.filter { it.key.name.startsWith("departure") }.map { ticketValues[it.value] }.reduce { acc, i -> acc * i } println(departureProduct) }