Skip to content

Instantly share code, notes, and snippets.

@saidaspen
Last active August 29, 2020 22:05
Show Gist options
  • Save saidaspen/3f5312f78f3ac222555797c391afa1bd to your computer and use it in GitHub Desktop.
Save saidaspen/3f5312f78f3ac222555797c391afa1bd to your computer and use it in GitHub Desktop.
Advent of Code 2017 Day 21
fun main() {
val input = java.io.File(ClassLoader.getSystemResource("201721").file).readText().trim()
println("Part 1: " + Day21().part1(input, 5))
println("Part 2: " + Day21().part1(input, 18))
}
typealias G = Grid<Char>
typealias P<A, B> = Pair<A, B>
class Day21 {
fun part1(input: String, iterations: Int): Int {
val rules = input.lines()
.filter { it != "" }
.map { it.split("=>") }
.map { P(toGrid(it[0].trim()), toGrid(it[1].trim())) }
.flatMap { variants(it.first).map { r -> Pair(r, it.second) } }
.map { it.first.toString() to it.second }
.toMap()
return (0 until iterations).fold(toGrid(".#./..#/###")) { acc, _ ->
reconstruct(deconstruct(acc).mapElements { rules[it.toString()]!! })
}.map { if (it == '#') 1 else 0 }.sum()
}
private fun reconstruct(parts: Grid<G>): G {
if (parts.size() == 1) return parts[0, 0]
return (0 until parts.height).fold(G()) { acc, rowIdx ->
acc.appendBelow(parts[rowIdx].reduce { row, elem -> row.appendRight(elem) })
}
}
fun deconstruct(grid: G): Grid<G> {
val chunkSize = if (grid.width % 2 == 0) 2 else 3
val numChunks = grid.width / chunkSize
return if (numChunks == 1) gridFrom(grid)
else Grid((0 until numChunks).map { row ->
(0 until numChunks).map { col ->
grid.subGrid(row * chunkSize, col * chunkSize, chunkSize, chunkSize)
}.toList()
}.toList())
}
private fun variants(g: G): List<G> = listOf(g, g.flipH(), g.flipV()).flatMap { rotations(it) }.distinct()
private fun rotations(g: G): List<G> = listOf(g, g.rotateCw(1), g.rotateCw(2), g.rotateCw(3))
private fun toGrid(s: String): G = G(s.split("/").map { it.toCharArray().toList() })
}
class Grid<T> {
val width by lazy { if (grid.size == 0) 0 else grid[0].size }
val height by lazy { grid.size }
fun size(): Int = width * height
constructor() { grid = mutableListOf() }
private var grid: MutableList<MutableList<T>>
constructor(map: List<List<T>>) { grid = map.map { it.toMutableList() }.toMutableList() }
fun flipV(): Grid<T> = Grid(grid.reversed().map { it })
fun flipH(): Grid<T> = Grid(grid.map { it.reversed() })
operator fun get(row: Int, col: Int): T = grid[row][col]
operator fun get(i: Int): List<T> = grid[i]
fun <G> map(function: (T) -> G): List<G> = grid.flatten().map(function)
fun <G> mapElements(function: (T) -> G): Grid<G> = Grid(grid.map { it.map(function) })
override fun toString(): String = "[" + grid.joinToString("/") { it.joinToString("") } + "]"
fun rotateCw(i: Int): Grid<T> = Grid(rotateCw(grid, i))
private fun rotateCw(rows: MutableList<MutableList<T>>, rotations: Int): MutableList<MutableList<T>> {
if (grid.size == 0) return grid
val nGrid = mutableListOf<MutableList<T>>()
for (iCol in 0 until grid[0].size) {
val row = mutableListOf<T>()
for (iRow in grid.size - 1 downTo 0) row.add(rows[iRow][iCol])
nGrid.add(row)
}
return if (rotations == 1) nGrid else rotateCw(nGrid, rotations - 1)
}
fun subGrid(y: Int, x: Int, height: Int, width: Int): Grid<T> = Grid((y until y + height).map { grid[it].subList(x, x + width) }.toList())
fun appendRight(right: Grid<T>): Grid<T> = Grid((0 until grid.size).map { listOf(grid[it], right[it]).flatten() }.toList())
fun appendBelow(below: Grid<T>): Grid<T> = Grid(listOf(grid, below.grid).flatten())
}
fun <T> gridFrom(map: T): Grid<T> = Grid(listOf(listOf(map)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment