From 6689a739bc16c52bce9ef7c1aae0f53f58cb088b Mon Sep 17 00:00:00 2001 From: Hubert Van De Walle Date: Sun, 20 Dec 2020 22:35:23 +0100 Subject: [PATCH] ok ? --- days/src/main/kotlin/Day20.kt | 167 ++++++++---------- days/src/main/kotlin/geometry/Grid.kt | 71 ++++++++ days/src/main/kotlin/geometry/GridFactory.kt | 7 + .../kotlin/geometry/GridTranformations.kt | 53 ++++++ 4 files changed, 204 insertions(+), 94 deletions(-) create mode 100644 days/src/main/kotlin/geometry/Grid.kt create mode 100644 days/src/main/kotlin/geometry/GridFactory.kt create mode 100644 days/src/main/kotlin/geometry/GridTranformations.kt diff --git a/days/src/main/kotlin/Day20.kt b/days/src/main/kotlin/Day20.kt index e158fe7..f72035d 100644 --- a/days/src/main/kotlin/Day20.kt +++ b/days/src/main/kotlin/Day20.kt @@ -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 -private typealias Grid = Array> +private typealias Tile = Grid + +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>>) { - - private val tiles = input.value + private val tiles: Map = 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) = sequence { @@ -59,12 +52,12 @@ class Day20(@Groups val input: Input>>) { } } - private fun neighbours(): Map> { - val neighbours = combine(tiles.map { it.second }) - .filter { (a, b) -> sideMatches(a, b) } + private fun neighbours(): MutableMap> { + 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>() @@ -75,67 +68,52 @@ class Day20(@Groups val input: Input>>) { } fun part1() = neighbours()[2]!! - .map { tile -> tiles.find { it.second === tile }!! } - .map { it.first.toLong() } - .reduce { acc, id -> acc * id } - - operator fun Array>.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>.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(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(size) { Array(size) { null } } + val grid: Grid = Grid(gridArray) + + val left = groups.filterIsInstance().map { it.left }.toSet() + val right = groups.filterIsInstance().map { it.right }.toSet() + val top = groups.filterIsInstance().map { it.top }.toSet() + val bottom = groups.filterIsInstance().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()) } } diff --git a/days/src/main/kotlin/geometry/Grid.kt b/days/src/main/kotlin/geometry/Grid.kt new file mode 100644 index 0000000..6f7ebb8 --- /dev/null +++ b/days/src/main/kotlin/geometry/Grid.kt @@ -0,0 +1,71 @@ +@file:Suppress("MemberVisibilityCanBePrivate") + +package be.vandewalleh.aoc.days.geometry + +class Grid { + val data: ArrayList> = ArrayList() + + constructor(data: Iterable>) { + data.forEach { + this.data.add(ArrayList(it.toList())) + } + } + + constructor(data: Array>) { + 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 = data[y] + fun firstRow() = row(0) + fun lastRow() = row(height - 1) + + fun column(x: Int): List = 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() + +} diff --git a/days/src/main/kotlin/geometry/GridFactory.kt b/days/src/main/kotlin/geometry/GridFactory.kt new file mode 100644 index 0000000..7488d47 --- /dev/null +++ b/days/src/main/kotlin/geometry/GridFactory.kt @@ -0,0 +1,7 @@ +package be.vandewalleh.aoc.days.geometry + +fun gridOf(lines: List): Grid = + Grid(lines.map { it.toCharArray().toList() }) + +fun gridOf(lines: String): Grid = + gridOf(lines.lines()) diff --git a/days/src/main/kotlin/geometry/GridTranformations.kt b/days/src/main/kotlin/geometry/GridTranformations.kt new file mode 100644 index 0000000..8dccba5 --- /dev/null +++ b/days/src/main/kotlin/geometry/GridTranformations.kt @@ -0,0 +1,53 @@ +package be.vandewalleh.aoc.days.geometry + +import java.util.* + +private fun ArrayList.reversed(): ArrayList { + val out = ArrayList(this.size) + asReversed().forEach { out.add(it) } + return out +} + +fun Grid.flipVertically(): Grid = Grid(data.reversed()) + +fun Grid.flipHorizontally(): Grid { + val out = ArrayList>(height) + for (y in 0 until height) { + out.add(data[y].reversed()) + } + return Grid(out) +} + +fun Grid.rotateRight(): Grid { + val out = ArrayList>(width) + for (x in 0 until width) { + out.add(ArrayList(height)) + for (y in 0 until height) { + out[x].add(data[y][x]) + } + } + return Grid(out).flipHorizontally() +} + +fun Grid.rotateLeft(): Grid { + val data = flipHorizontally().data + val out = ArrayList>(width) + for (x in 0 until width) { + out.add(ArrayList(height)) + for (y in 0 until height) { + out[x].add(data[y][x]) + } + } + return Grid(out).flipHorizontally() +} + +fun Grid.transformations(): Sequence> = sequence { + yield(this@transformations) + yield(flipHorizontally()) + yield(flipVertically()) + yield(rotateLeft()) + yield(rotateRight()) + yield(flipHorizontally().flipVertically()) + yield(flipVertically().rotateRight()) + yield(flipVertically().rotateLeft()) +}