diff --git a/src/aoc2022/Day08.kt b/src/aoc2022/Day08.kt new file mode 100644 index 0000000..aa98478 --- /dev/null +++ b/src/aoc2022/Day08.kt @@ -0,0 +1,183 @@ +package aoc2022 + +import println +import readInput +import kotlin.math.max +import kotlin.math.min + +/* +--- Day 8: Treetop Tree House --- +The expedition comes across a peculiar patch of tall trees all planted carefully in a grid. The Elves explain that a previous expedition planted these trees as a reforestation effort. Now, they're curious if this would be a good location for a tree house. +First, determine whether there is enough tree cover here to keep a tree house hidden. To do this, you need to count the number of trees that are visible from outside the grid when looking directly along a row or column. +The Elves have already launched a quadcopter to generate a map with the height of each tree (your puzzle input). For example: +30373 +25512 +65332 +33549 +35390 + +Each tree is represented as a single digit whose value is its height, where 0 is the shortest and 9 is the tallest. +A tree is visible if all of the other trees between it and an edge of the grid are shorter than it. Only consider trees in the same row or column; that is, only look up, down, left, or right from any given tree. +All of the trees around the edge of the grid are visible - since they are already on the edge, there are no trees to block the view. In this example, that only leaves the interior nine trees to consider: + +The top-left 5 is visible from the left and top. (It isn't visible from the right or bottom since other trees of height 5 are in the way.) +The top-middle 5 is visible from the top and right. +The top-right 1 is not visible from any direction; for it to be visible, there would need to only be trees of height 0 between it and an edge. +The left-middle 5 is visible, but only from the right. +The center 3 is not visible from any direction; for it to be visible, there would need to be only trees of at most height 2 between it and an edge. +The right-middle 3 is visible from the right. +In the bottom row, the middle 5 is visible, but the 3 and 4 are not. + +With 16 trees visible on the edge and another 5 visible in the interior, a total of 21 trees are visible in this arrangement. +Consider your map; how many trees are visible from outside the grid? + +--- Part Two --- +Content with the amount of tree cover available, the Elves just need to know the best spot to build their tree house: they would like to be able to see a lot of trees. + +To measure the viewing distance from a given tree, look up, down, left, and right from that tree; stop if you reach an edge or at the first tree that is the same height or taller than the tree under consideration. (If a tree is right on the edge, at least one of its viewing distances will be zero.) + +The Elves don't care about distant trees taller than those found by the rules above; the proposed tree house has large eaves to keep it dry, so they wouldn't be able to see higher than the tree house anyway. + +In the example above, consider the middle 5 in the second row: + +30373 +25512 +65332 +33549 +35390 +Looking up, its view is not blocked; it can see 1 tree (of height 3). +Looking left, its view is blocked immediately; it can see only 1 tree (of height 5, right next to it). +Looking right, its view is not blocked; it can see 2 trees. +Looking down, its view is blocked eventually; it can see 2 trees (one of height 3, then the tree of height 5 that blocks its view). +A tree's scenic score is found by multiplying together its viewing distance in each of the four directions. For this tree, this is 4 (found by multiplying 1 * 1 * 2 * 2). + +However, you can do even better: consider the tree of height 5 in the middle of the fourth row: + +30373 +25512 +65332 +33549 +35390 +Looking up, its view is blocked at 2 trees (by another tree with a height of 5). +Looking left, its view is not blocked; it can see 2 trees. +Looking down, its view is also not blocked; it can see 1 tree. +Looking right, its view is blocked at 2 trees (by a massive tree of height 9). +This tree's scenic score is 8 (2 * 2 * 1 * 2); this is the ideal spot for the tree house. + +Consider each tree on your map. What is the highest scenic score possible for any tree? + +*/ +fun main() { + + val inlineTestInput = """ +30373 +25512 +65332 +33549 +35390 +""" + + fun parseInput(input: List): IntArray { + val dim = input.first().length + val out = IntArray(dim * dim) + for ((rnum, r) in input.withIndex()) { + r.forEachIndexed { index, c -> out[rnum * dim + index] = c - '0' } + } + return out + } + + fun createMaxArray(trees: IntArray, dim: Int, tcurr: (row: Int, col: Int) -> Int): IntArray { + val maxArray = IntArray(dim * dim) + for (row in 0 until dim) { + var maxValue = -1 + for (col in 0 until dim) { + val posCurr = tcurr(row, col) + maxArray[posCurr] = maxValue + maxValue = max(trees[posCurr], maxValue) + } + } + return maxArray + } + + fun part1(input: List): Int { + val dim = input.first().length + val trees = parseInput(input) + val west = createMaxArray(trees, dim) { row, col -> row * dim + col } + val east = createMaxArray(trees, dim) { row, col -> row * dim + (dim - col - 1) } + val north = createMaxArray(trees, dim) { row, col -> col * dim + row } + val south = createMaxArray(trees, dim) { row, col -> (dim - col - 1) * dim + row } + return trees.withIndex().count { (i, v) -> v > min(min(north[i], south[i]), min(west[i], east[i])) } + } + + fun createVisArray(trees: IntArray, dim: Int, tcurr: (row: Int, col: Int) -> Int): IntArray { + val visArray = IntArray(dim * dim) + for (row in 0 until dim) { + var visTrees = 0 + var posLast = -1 + for (col in 0 until dim) { + val posCurr = tcurr(row, col) +// should have been possible with dynamic programming, but I'm too stupid to get it right, so brute force instead +// visArray[posCurr] = if (posLast < 0) { +// 0 +// } else if (trees[posCurr] > trees[posLast]) { +// ++visTrees +// } else if (trees[posCurr] == trees[posLast]) { +// ++visTrees +// 1 +// } else { +// visTrees = 1 +// 1 +// } +// posLast = posCurr + for (col2 in col - 1 downTo 0) { + val t = trees[tcurr(row, col2)] + visArray[posCurr]++ + if (trees[posCurr] <= t) break + } + } + } + return visArray + } + + fun part2(input: List): Int { + val dim = input.first().length + val trees = parseInput(input) + val west = createVisArray(trees, dim) { row, col -> row * dim + col } + val east = createVisArray(trees, dim) { row, col -> row * dim + (dim - col - 1) } + val north = createVisArray(trees, dim) { row, col -> col * dim + row } + val south = createVisArray(trees, dim) { row, col -> (dim - col - 1) * dim + row } + +// println("West:") +// west.asSequence().chunked(dim).joinToString("\n") { it.joinToString(",") }.println() +// +// println("East:") +// east.asSequence().chunked(dim).joinToString("\n") { it.joinToString(",") }.println() +// +// println("North:") +// north.asSequence().chunked(dim).joinToString("\n") { it.joinToString(",") }.println() +// +// println("South:") +// south.asSequence().chunked(dim).joinToString("\n") { it.joinToString(",") }.println() + + var maxValue = -1 + for (row in 0 until dim) { + for (col in 0 until dim) { + val p = row * dim + col + maxValue = max(north[p] * south[p] * west[p] * east[p], maxValue) + } + } + return maxValue + } + + // test if implementation meets criteria from the description, like: + val testInput = inlineTestInput.trim().reader().readLines() + //val testInput = readInput("aoc2022/Day08_test") + println("Part 1 Test: " + part1(testInput)) + println("Part 2 Test: " + part2(testInput)) + check(part1(testInput) == 21) + check(part2(testInput) == 8) + + val input = readInput("aoc2022/Day08") + part1(input).println() + part2(input).println() +}