From d2487ed762b53e113b92a091d3c74b572674b3bb Mon Sep 17 00:00:00 2001 From: Chris Hodges Date: Fri, 6 Dec 2024 12:31:43 +0100 Subject: [PATCH] Day 6 part 2 :'-( Change-Id: I04ba157f88233e3f57632f632c4a60267e486058 --- src/Utils.kt | 20 ++++++++--- src/aoc2024/Day06.kt | 83 +++++++++++++++++++++++--------------------- 2 files changed, 59 insertions(+), 44 deletions(-) diff --git a/src/Utils.kt b/src/Utils.kt index 349a3a4..0102cf3 100644 --- a/src/Utils.kt +++ b/src/Utils.kt @@ -94,6 +94,9 @@ class CharGrid { if (newChar != null && isInside(col, row)) data[row][col] = newChar } + operator fun get(pos: RelPos) = get(pos.dc, pos.dr) + operator fun set(pos: RelPos, newChar: Char?) = set(pos.dc, pos.dr, newChar) + fun setOrThrow(col: Int, row: Int, newChar: Char) { if (!isInside(col, row)) throw IndexOutOfBoundsException("$col, $row out of bounds") set(col, row, newChar) @@ -101,12 +104,14 @@ class CharGrid { fun getOrNull(col: Int, row: Int) = if (isInside(col, row)) data[row][col] else null fun isInside(col: Int, row: Int) = (col in 0 until width) && (row in 0 until height) + fun isInside(pos: RelPos) = isInside(pos.dc, pos.dr) fun getHorizNumberValueAt(rp: RelPos) = getHorizNumberValueAt(rp.dc, rp.dr) fun getHorizNumberValueAt(col: Int, row: Int): Pair> { val relPos = getHorizNumberRelPosAt(col, row) - return relPos.map { this[col + it.dc, row + it.dr].digitToInt() }.reduce { acc, digit -> acc * 10 + digit } to relPos + return relPos.map { this[col + it.dc, row + it.dr].digitToInt() } + .reduce { acc, digit -> acc * 10 + digit } to relPos } fun getHorizNumberRelPosAt(col: Int, row: Int): List { @@ -159,9 +164,13 @@ class CharGrid { relposes.map { get(c + it.dc, r + it.dr) } } - fun countStrings(relposeList: Iterable>, string: String) = findMatches(relposeList) { it == string }.count() + fun countStrings(relposeList: Iterable>, string: String) = + findMatches(relposeList) { it == string }.count() - fun findMatches(relposeList: Iterable>, predicate: (content: String) -> Boolean): List> { + fun findMatches( + relposeList: Iterable>, + predicate: (content: String) -> Boolean + ): List> { val matches = ArrayList>() for (r in 0 until height) { for (c in 0 until width) { @@ -176,7 +185,10 @@ class CharGrid { return matches } - fun findCombinedMatches(relposeList: Iterable>, predicate: (content: List) -> Boolean): List { + fun findCombinedMatches( + relposeList: Iterable>, + predicate: (content: List) -> Boolean + ): List { val matches = ArrayList() for (r in 0 until height) { for (c in 0 until width) { diff --git a/src/aoc2024/Day06.kt b/src/aoc2024/Day06.kt index d91dfa4..ed73808 100644 --- a/src/aoc2024/Day06.kt +++ b/src/aoc2024/Day06.kt @@ -109,67 +109,70 @@ fun main() { fun part1(input: List): Int { val grid = CharGrid(input, '.') - var (c, r) = grid.findMatches { it == '^' }[0] + val (oc, or) = grid.findMatches { it == '^' }[0] + + var pos = RelPos(oc, or) var dir = RelPos(0, -1) - while (grid.isInside(c, r)) { - grid[c, r] = 'X' - if (grid[c + dir.dc, r + dir.dr] == '#') { + while (grid.isInside(pos)) { + grid[pos] = 'X' + while (grid[pos.translate(dir)] == '#') { dir = RelPos(-dir.dr, dir.dc) } - c += dir.dc - r += dir.dr + pos = pos.translate(dir) } return grid.findMatches { it == 'X' }.count() } - fun looping(grid: CharGrid, origDirGrid: Array, oc: Int, or: Int, origDir: RelPos, origDirBit: Int): Boolean { - val dirgrid = Array(grid.height) { origDirGrid[it].copyOf() } - var dir = origDir - var c = oc - var r = or - var dirBit = origDirBit - while (grid.isInside(c, r)) { - if ((dirgrid[c][r] and (1 shl dirBit)) != 0) { - return true + fun checkLooping(grid: CharGrid, origPos: RelPos): Boolean { + val dirgrid = Array(grid.height) { IntArray(grid.width) } + var dir = RelPos(0, -1) + var dirBit = 0 + var looping = false + var pos = origPos + while (grid.isInside(pos)) { + if ((dirgrid[pos.dc][pos.dr] and (1 shl dirBit)) != 0) { + looping = true + break } - dirgrid[c][r] = dirgrid[c][r] or (1 shl dirBit) - if (grid[c + dir.dc, r + dir.dr] == '#') { + dirgrid[pos.dc][pos.dr] = dirgrid[pos.dc][pos.dr] or (1 shl dirBit) + while (grid[pos.translate(dir)] == '#') { dir = RelPos(-dir.dr, dir.dc) dirBit = (dirBit + 1) and 3 } - c += dir.dc - r += dir.dr + pos = pos.translate(dir) } - return false + return looping } fun part2(input: List): Int { val grid = CharGrid(input, '.') - val dirgrid = Array(grid.height) { IntArray(grid.width) } - var (oc, or) = grid.findMatches { it == '^' }[0] - var c = oc - var r = or + val obstaclePos = HashSet() + val solutions = HashSet() + val (oc, or) = grid.findMatches { it == '^' }[0] + + var pos = RelPos(oc, or) var dir = RelPos(0, -1) - var dirBit = 0 - while (grid.isInside(c, r)) { - val newDir = RelPos(-dir.dr, dir.dc) - //grid[c, r] = 'X' - dirgrid[c][r] = dirgrid[c][r] or (1 shl dirBit) - if (grid[c + dir.dc, r + dir.dr] == '#') { - dir = newDir - dirBit = (dirBit + 1) and 3 + while (true) { + grid[pos] = 'X' + val newPos = pos.translate(dir) + if (!grid.isInside(newPos)) break + if (grid[newPos] == '#') { + dir = RelPos(-dir.dr, dir.dc) } else { - if (looping(grid, dirgrid, c + newDir.dc, r + newDir.dr, newDir, (dirBit + 1) and 3)) { - if ((c + dir.dc != oc) || (r + dir.dr != or)) { - grid[c + dir.dc, r + dir.dr] = 'O' - } + if (grid[newPos] == '.') { + obstaclePos.add(newPos) } + pos = newPos } - c += dir.dc - r += dir.dr } - grid.debug() - return grid.findMatches { it == 'O' }.count() + for (op in obstaclePos) { + grid[op] = '#' + if (checkLooping(grid, RelPos(oc, or))) { + solutions.add(op) + } + grid[op] = '.' + } + return solutions.size } // test if implementation meets criteria from the description, like: