object Day13 { private sealed class FoldCommand { abstract fun dispatch(dots: Set): Set class AlongX(private val x: Int) : FoldCommand() { override fun dispatch(dots: Set): Set = dots .filter { it.x != x } .map { if (it.x < x) { it } else { Pos2D(2 * x - it.x, it.y) } } .toSet() } class AlongY(private val y: Int) : FoldCommand() { override fun dispatch(dots: Set): Set = dots .filter { it.y != y } .map { if (it.y < y) { it } else { Pos2D(it.x, 2 * y - it.y) } } .toSet() } } private fun parseOrigami(input: List): Pair, Sequence> { val dots = mutableSetOf() val commands = mutableListOf() for (line in input) { if (line.matches("\\d+,\\d+".toRegex())) { val (x, y) = line.split(",").map(String::toInt) dots.add(Pos2D(x, y)) } if (line.matches("fold along [xy]=\\d+".toRegex())) { val equation = line.substring(11) val (axis, valueString) = equation.split("=") val value = valueString.toInt() commands.add( when (axis) { "x" -> FoldCommand.AlongX(value) "y" -> FoldCommand.AlongY(value) else -> throw IllegalStateException("Illegal axis given!") } ) } } return dots to commands.asSequence() } fun part1(input: List): Int { val (dots, commands) = parseOrigami(input) return commands.first().dispatch(dots).size } fun part2(input: List): String { val origami = parseOrigami(input) var dots = origami.first val commands = origami.second commands.forEach { dots = it.dispatch(dots) } val bounds = dots.reduce { max, pos -> when ((pos.x > max.x) to (pos.y > max.y)) { true to true -> pos true to false -> Pos2D(pos.x, max.y) false to true -> Pos2D(max.x, pos.y) else -> max } } val lines = Array(bounds.y + 1) { Array(bounds.x + 1) { ' ' } } dots.forEach { lines[it.y][it.x] = '#' } return lines.joinToString("\n") { it.joinToString("") } } } fun main() { val testInput = readInputAsLines("Day13_test") check(Day13.part1(testInput) == 17) val input = readInputAsLines("Day13") println(Day13.part1(input)) println(Day13.part2(input)) }