ok ?
This commit is contained in:
parent
dac50700b6
commit
6689a739bc
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
71
days/src/main/kotlin/geometry/Grid.kt
Normal file
71
days/src/main/kotlin/geometry/Grid.kt
Normal 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()
|
||||
|
||||
}
|
||||
7
days/src/main/kotlin/geometry/GridFactory.kt
Normal file
7
days/src/main/kotlin/geometry/GridFactory.kt
Normal 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())
|
||||
53
days/src/main/kotlin/geometry/GridTranformations.kt
Normal file
53
days/src/main/kotlin/geometry/GridTranformations.kt
Normal 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())
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user