1
0
This commit is contained in:
Hubert Van De Walle 2020-12-20 22:35:23 +01:00
parent dac50700b6
commit 6689a739bc
4 changed files with 204 additions and 94 deletions

View File

@ -1,52 +1,45 @@
package be.vandewalleh.aoc.days package be.vandewalleh.aoc.days
import be.vandewalleh.aoc.days.geometry.Grid
import be.vandewalleh.aoc.days.geometry.gridOf
import be.vandewalleh.aoc.days.geometry.transformations
import be.vandewalleh.aoc.utils.input.Day import be.vandewalleh.aoc.utils.input.Day
import be.vandewalleh.aoc.utils.input.Groups import be.vandewalleh.aoc.utils.input.Groups
import be.vandewalleh.aoc.utils.input.Input import be.vandewalleh.aoc.utils.input.Input
import be.vandewalleh.aoc.utils.input.createDay import be.vandewalleh.aoc.utils.input.createDay
import kotlin.math.sqrt import kotlin.math.sqrt
private typealias Tile = Array<CharArray> private typealias Tile = Grid<Char>
private typealias Grid = Array<Array<Tile>>
sealed class TileGroup
data class HorizontalGroup(val left: Tile, val right: Tile) : TileGroup()
data class VerticalGroup(val top: Tile, val bottom: Tile) : TileGroup()
@Day(20) @Day(20)
class Day20(@Groups val input: Input<List<List<String>>>) { class Day20(@Groups val input: Input<List<List<String>>>) {
private val tiles: Map<Int, Tile> = input.value
private val tiles = input.value
.map { it[0].let { it.substring(5 until it.indexOf(':')).toInt() } to it.drop(1) } .map { it[0].let { it.substring(5 until it.indexOf(':')).toInt() } to it.drop(1) }
.map { (id, tile) -> id to tile.map { it.toCharArray() }.toTypedArray() } .associate { (id, tile) -> id to gridOf(tile) }
private fun Tile.sides() = sequence { private fun Tile.allEdges() = transformations().flatMap { it.edges() }
val tile = this@sides
yield(tile[0]) private fun edgesMatch(a: Tile, b: Tile): Boolean {
yield(tile[tile.lastIndex]) val edges = b.allEdges()
yield(tile[0].reversedArray()) return a.allEdges().any { a -> edges.any { a == it } }
yield(tile[tile.lastIndex].reversedArray())
yield(tile.map { it[0] }.toCharArray())
yield(tile.map { it[tile.lastIndex] }.toCharArray())
yield(tile.map { it[0] }.asReversed().toCharArray())
yield(tile.map { it[tile.lastIndex] }.asReversed().toCharArray())
} }
private fun sideMatches(a: Tile, b: Tile): Boolean { private fun group(a: Tile, b: Tile): TileGroup? {
val aSides = a.sides() for (a: Tile in a.transformations()) {
val bSides = b.sides().toList() for (b: Tile in b.transformations()) {
aSides.forEach { aSide -> when {
bSides.forEach { bSide -> a.firstRow() == b.lastRow() -> return VerticalGroup(top = b, bottom = a)
if (aSide contentEquals bSide) return true a.firstColumn() == b.lastColumn() -> return HorizontalGroup(left = b, right = a)
b.firstRow() == a.lastRow() -> return VerticalGroup(top = a, bottom = b)
b.firstColumn() == a.lastColumn() -> return HorizontalGroup(left = a, right = b)
}
} }
} }
return false return null
}
private fun Tile.prettyPrint() {
val str = buildString {
this@prettyPrint.forEach { line ->
append(line.joinToString(""))
appendLine()
}
}
print(str)
} }
private fun combine(tiles: List<Tile>) = sequence { private fun combine(tiles: List<Tile>) = sequence {
@ -59,12 +52,12 @@ class Day20(@Groups val input: Input<List<List<String>>>) {
} }
} }
private fun neighbours(): Map<Int, List<Tile>> { private fun neighbours(): MutableMap<Int, MutableList<Tile>> {
val neighbours = combine(tiles.map { it.second }) val neighbours = combine(tiles.values.toList())
.filter { (a, b) -> sideMatches(a, b) } .filter { (a, b) -> edgesMatch(a, b) }
.map { it.toList() } .map { it.toList() }
.flatten() .flatten()
.groupBy { it.map { it.toList() } } .groupBy { it }
.values .values
val map = mutableMapOf<Int, MutableList<Tile>>() val map = mutableMapOf<Int, MutableList<Tile>>()
@ -75,67 +68,52 @@ class Day20(@Groups val input: Input<List<List<String>>>) {
} }
fun part1() = neighbours()[2]!! fun part1() = neighbours()[2]!!
.map { tile -> tiles.find { it.second === tile }!! } .map { tile -> tiles.entries.find { it.value == tile }!!.key.toLong() }
.map { it.first.toLong() } .reduce { acc, id -> acc * id }
.reduce { acc, id -> acc * id }
operator fun Array<Array<Tile?>>.get(x: Int, y: Int): Tile? {
if (y < 0 || y > lastIndex) return null
val row = get(y)
return if (x < 0 || x > row.lastIndex) null
else row[x]
}
operator fun Array<Array<Tile?>>.set(x: Int, y: Int, tile: Tile) {
this[y][x] = tile
}
private fun fillGrid(): Grid {
val neighboursSet = neighbours().entries.associate { it.key to it.value.toMutableSet() }
val size = sqrt(tiles.size.toDouble()).toInt()
val grid: Array<Array<Tile?>> = Array(size) { Array(size) { null } }
for (x in 0 until size) {
for (y in 0 until size) {
var neighboursCount = 2
if (x != 0 && x != size - 1) neighboursCount++
if (y != 0 && y != size - 1) neighboursCount++
val pos = listOf(
x to y - 1,
x + 1 to y,
x to y + 1,
x - 1 to y,
)
val a = pos.mapNotNull { (x, y) -> grid[x, y] }
val iterator = neighboursSet[neighboursCount]!!.iterator()
while (iterator.hasNext()) {
val possibility = iterator.next()
val matches = a.count { sideMatches(it, possibility) }
if (matches == a.size) {
grid[x, y] = possibility
iterator.remove()
break
}
}
}
}
return grid as Grid
}
private fun correctGrid(grid: Grid): Grid {
TODO("flip / rotate every tile")
}
fun part2() { fun part2() {
val grid = correctGrid(fillGrid()) val size = sqrt(tiles.size.toDouble()).toInt()
TODO("remove tile edges")
val n = neighbours()
val corners = n[2]!!
.map { tile -> tiles.entries.find { it.value == tile }!!.key.toLong() }
check(corners.size == 4)
val borders = n[3]!!
.map { tile -> tiles.entries.find { it.value == tile }!!.key.toLong() }
val insides = n[4]!!
.map { tile -> tiles.entries.find { it.value == tile }!!.key.toLong() }
val groups = combine(tiles.values.toList())
.mapNotNull { (a, b) -> group(a, b) }
.toList()
val groupValues = groups.flatMap {
when (it) {
is HorizontalGroup -> listOf(it.left, it.right)
is VerticalGroup -> listOf(it.top, it.bottom)
}
}
val gridArray: Array<Array<Tile?>> = Array(size) { Array(size) { null } }
val grid: Grid<Tile?> = Grid(gridArray)
val left = groups.filterIsInstance<HorizontalGroup>().map { it.left }.toSet()
val right = groups.filterIsInstance<HorizontalGroup>().map { it.right }.toSet()
val top = groups.filterIsInstance<VerticalGroup>().map { it.top }.toSet()
val bottom = groups.filterIsInstance<VerticalGroup>().map { it.bottom }.toSet()
println("L:${left.size}")
println("R:${right.size}")
println("T:${top.size}")
println("B:${bottom.size}")
println(groupValues.filter { it in left && it in top && it !in right && it !in bottom })
} }
} }
fun main() { fun main() {
@ -250,6 +228,7 @@ fun main() {
..#.###... ..#.###...
""".trimIndent() """.trimIndent()
)) { )) {
println(part1())
println(part2()) println(part2())
} }
} }

View File

@ -0,0 +1,71 @@
@file:Suppress("MemberVisibilityCanBePrivate")
package be.vandewalleh.aoc.days.geometry
class Grid<T> {
val data: ArrayList<ArrayList<T>> = ArrayList()
constructor(data: Iterable<Iterable<T>>) {
data.forEach {
this.data.add(ArrayList(it.toList()))
}
}
constructor(data: Array<Array<T>>) {
data.forEach {
this.data.add(ArrayList(it.toList()))
}
}
val width get() = data[0].size
val lastColumnIndex get() = data[0].size - 1
val height get() = data.size
val lastRowIndex get() = data.size - 1
operator fun get(x: Int, y: Int): T? {
if (y !in 0..lastRowIndex) return null
val row = data[y]
return if (x !in 0..lastColumnIndex) null
else row[x]
}
operator fun set(x: Int, y: Int, value: T) {
data[y][x] = value
}
fun row(y: Int): List<T> = data[y]
fun firstRow() = row(0)
fun lastRow() = row(height - 1)
fun column(x: Int): List<T> = data.map { it[x] }
fun firstColumn() = column(0)
fun lastColumn() = column(width - 1)
fun edges() = listOf(row(0), column(0), row(lastRowIndex), column(lastColumnIndex))
fun neighbours(x: Int, y: Int) = listOf(
x to y - 1,
x + 1 to y,
x to y + 1,
x - 1 to y,
).mapNotNull { this[x, y] }
override fun toString() = buildString {
data.forEach { line ->
append(line.joinToString(""))
appendLine()
}
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Grid<*>
if (data != other.data) return false
return true
}
override fun hashCode() = data.hashCode()
}

View File

@ -0,0 +1,7 @@
package be.vandewalleh.aoc.days.geometry
fun gridOf(lines: List<String>): Grid<Char> =
Grid(lines.map { it.toCharArray().toList() })
fun gridOf(lines: String): Grid<Char> =
gridOf(lines.lines())

View File

@ -0,0 +1,53 @@
package be.vandewalleh.aoc.days.geometry
import java.util.*
private fun <T> ArrayList<T>.reversed(): ArrayList<T> {
val out = ArrayList<T>(this.size)
asReversed().forEach { out.add(it) }
return out
}
fun <T> Grid<T>.flipVertically(): Grid<T> = Grid(data.reversed())
fun <T> Grid<T>.flipHorizontally(): Grid<T> {
val out = ArrayList<ArrayList<T>>(height)
for (y in 0 until height) {
out.add(data[y].reversed())
}
return Grid(out)
}
fun <T> Grid<T>.rotateRight(): Grid<T> {
val out = ArrayList<ArrayList<T>>(width)
for (x in 0 until width) {
out.add(ArrayList<T>(height))
for (y in 0 until height) {
out[x].add(data[y][x])
}
}
return Grid(out).flipHorizontally()
}
fun <T> Grid<T>.rotateLeft(): Grid<T> {
val data = flipHorizontally().data
val out = ArrayList<ArrayList<T>>(width)
for (x in 0 until width) {
out.add(ArrayList<T>(height))
for (y in 0 until height) {
out[x].add(data[y][x])
}
}
return Grid(out).flipHorizontally()
}
fun <T> Grid<T>.transformations(): Sequence<Grid<T>> = sequence {
yield(this@transformations)
yield(flipHorizontally())
yield(flipVertically())
yield(rotateLeft())
yield(rotateRight())
yield(flipHorizontally().flipVertically())
yield(flipVertically().rotateRight())
yield(flipVertically().rotateLeft())
}