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
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.Groups
import be.vandewalleh.aoc.utils.input.Input
import be.vandewalleh.aoc.utils.input.createDay
import kotlin.math.sqrt
private typealias Tile = Array<CharArray>
private typealias Grid = Array<Array<Tile>>
private typealias Tile = Grid<Char>
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)
class Day20(@Groups val input: Input<List<List<String>>>) {
private val tiles = input.value
private val tiles: Map<Int, Tile> = input.value
.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 {
val tile = this@sides
yield(tile[0])
yield(tile[tile.lastIndex])
yield(tile[0].reversedArray())
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 Tile.allEdges() = transformations().flatMap { it.edges() }
private fun edgesMatch(a: Tile, b: Tile): Boolean {
val edges = b.allEdges()
return a.allEdges().any { a -> edges.any { a == it } }
}
private fun sideMatches(a: Tile, b: Tile): Boolean {
val aSides = a.sides()
val bSides = b.sides().toList()
aSides.forEach { aSide ->
bSides.forEach { bSide ->
if (aSide contentEquals bSide) return true
private fun group(a: Tile, b: Tile): TileGroup? {
for (a: Tile in a.transformations()) {
for (b: Tile in b.transformations()) {
when {
a.firstRow() == b.lastRow() -> return VerticalGroup(top = b, bottom = a)
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
}
private fun Tile.prettyPrint() {
val str = buildString {
this@prettyPrint.forEach { line ->
append(line.joinToString(""))
appendLine()
}
}
print(str)
return null
}
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>> {
val neighbours = combine(tiles.map { it.second })
.filter { (a, b) -> sideMatches(a, b) }
private fun neighbours(): MutableMap<Int, MutableList<Tile>> {
val neighbours = combine(tiles.values.toList())
.filter { (a, b) -> edgesMatch(a, b) }
.map { it.toList() }
.flatten()
.groupBy { it.map { it.toList() } }
.groupBy { it }
.values
val map = mutableMapOf<Int, MutableList<Tile>>()
@ -75,67 +68,52 @@ class Day20(@Groups val input: Input<List<List<String>>>) {
}
fun part1() = neighbours()[2]!!
.map { tile -> tiles.find { it.second === tile }!! }
.map { it.first.toLong() }
.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")
}
.map { tile -> tiles.entries.find { it.value == tile }!!.key.toLong() }
.reduce { acc, id -> acc * id }
fun part2() {
val grid = correctGrid(fillGrid())
TODO("remove tile edges")
val size = sqrt(tiles.size.toDouble()).toInt()
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() {
@ -250,6 +228,7 @@ fun main() {
..#.###...
""".trimIndent()
)) {
println(part1())
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())
}