Compare commits

...

3 Commits

2 changed files with 243 additions and 0 deletions

105
src/aoc2023/Day15.kt Normal file
View File

@ -0,0 +1,105 @@
package aoc2023
import println
import readInput
/*
--- Day 15: Lens Library ---
The newly-focused parabolic reflector dish is sending all of the collected light to a point on the side of yet another mountain - the largest mountain on Lava Island. As you approach the mountain, you find that the light is being collected by the wall of a large facility embedded in the mountainside.
You find a door under a large sign that says "Lava Production Facility" and next to a smaller sign that says "Danger - Personal Protective Equipment required beyond this point".
As you step inside, you are immediately greeted by a somewhat panicked reindeer wearing goggles and a loose-fitting hard hat. The reindeer leads you to a shelf of goggles and hard hats (you quickly find some that fit) and then further into the facility. At one point, you pass a button with a faint snout mark and the label "PUSH FOR HELP". No wonder you were loaded into that trebuchet so quickly!
You pass through a final set of doors surrounded with even more warning signs and into what must be the room that collects all of the light from outside. As you admire the large assortment of lenses available to further focus the light, the reindeer brings you a book titled "Initialization Manual".
"Hello!", the book cheerfully begins, apparently unaware of the concerned reindeer reading over your shoulder. "This procedure will let you bring the Lava Production Facility online - all without burning or melting anything unintended!"
"Before you begin, please be prepared to use the Holiday ASCII String Helper algorithm (appendix 1A)." You turn to appendix 1A. The reindeer leans closer with interest.
The HASH algorithm is a way to turn any string of characters into a single number in the range 0 to 255. To run the HASH algorithm on a string, start with a current value of 0. Then, for each character in the string starting from the beginning:
Determine the ASCII code for the current character of the string.
Increase the current value by the ASCII code you just determined.
Set the current value to itself multiplied by 17.
Set the current value to the remainder of dividing itself by 256.
After following these steps for each character in the string in order, the current value is the output of the HASH algorithm.
So, to find the result of running the HASH algorithm on the string HASH:
The current value starts at 0.
The first character is H; its ASCII code is 72.
The current value increases to 72.
The current value is multiplied by 17 to become 1224.
The current value becomes 200 (the remainder of 1224 divided by 256).
The next character is A; its ASCII code is 65.
The current value increases to 265.
The current value is multiplied by 17 to become 4505.
The current value becomes 153 (the remainder of 4505 divided by 256).
The next character is S; its ASCII code is 83.
The current value increases to 236.
The current value is multiplied by 17 to become 4012.
The current value becomes 172 (the remainder of 4012 divided by 256).
The next character is H; its ASCII code is 72.
The current value increases to 244.
The current value is multiplied by 17 to become 4148.
The current value becomes 52 (the remainder of 4148 divided by 256).
So, the result of running the HASH algorithm on the string HASH is 52.
The initialization sequence (your puzzle input) is a comma-separated list of steps to start the Lava Production Facility. Ignore newline characters when parsing the initialization sequence. To verify that your HASH algorithm is working, the book offers the sum of the result of running the HASH algorithm on each step in the initialization sequence.
For example:
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
This initialization sequence specifies 11 individual steps; the result of running the HASH algorithm on each of the steps is as follows:
rn=1 becomes 30.
cm- becomes 253.
qp=3 becomes 97.
cm=2 becomes 47.
qp- becomes 14.
pc=4 becomes 180.
ot=9 becomes 9.
ab=5 becomes 197.
pc- becomes 48.
pc=6 becomes 214.
ot=7 becomes 231.
In this example, the sum of these results is 1320. Unfortunately, the reindeer has stolen the page containing the expected verification number and is currently running around the facility with it excitedly.
Run the HASH algorithm on each step in the initialization sequence. What is the sum of the results? (The initialization sequence is one long line; be careful when copy-pasting it.)
*/
fun main() {
val inlineTestInput = """
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
"""
fun hash(str: String): Int = str.map { it.code }.fold(0) { acc, c -> ((acc + c) * 17) and 0xff }
fun part1(input: List<String>): Int {
return input.first().split(",").sumOf { hash(it) }
}
fun part2(input: List<String>): Int {
val boxes = Array<MutableMap<String, Int>>(256) { LinkedHashMap() }
input.first().split(",").forEach { it ->
if (it.contains('=')) {
val (label, value) = it.split("=")
boxes[hash(label)][label] = value.toInt()
} else {
val label = it.removeSuffix("-")
boxes[hash(label)].remove(label)
}
}
return boxes.mapIndexed { boxno, contents ->
contents.map { entry -> entry.value }
.mapIndexed { index, value -> (boxno + 1) * value * (index + 1) }.sum()
}.sum()
}
// test if implementation meets criteria from the description, like:
val testInput = inlineTestInput.trim().reader().readLines()
//val testInput = readInput("aoc2023/Day15_test")
val testInputPart1Result = part1(testInput)
println("Part 1 Test: $testInputPart1Result")
val testInputPart2Result = part2(testInput)
println("Part 2 Test: $testInputPart2Result")
check(testInputPart1Result == 1320)
check(testInputPart2Result == 145)
val input = readInput("aoc2023/Day15")
part1(input).println()
part2(input).println()
}

138
src/aoc2023/Day16.kt Normal file
View File

@ -0,0 +1,138 @@
package aoc2023
import CharGrid
import RelPos
import println
import readInput
/*
--- Day 16: The Floor Will Be Lava ---
With the beam of light completely focused somewhere, the reindeer leads you deeper still into the Lava Production Facility. At some point, you realize that the steel facility walls have been replaced with cave, and the doorways are just cave, and the floor is cave, and you're pretty sure this is actually just a giant cave.
Finally, as you approach what must be the heart of the mountain, you see a bright light in a cavern up ahead. There, you discover that the beam of light you so carefully focused is emerging from the cavern wall closest to the facility and pouring all of its energy into a contraption on the opposite side.
Upon closer inspection, the contraption appears to be a flat, two-dimensional square grid containing empty space (.), mirrors (/ and \), and splitters (| and -).
The contraption is aligned so that most of the beam bounces around the grid, but each tile on the grid converts some of the beam's light into heat to melt the rock in the cavern.
You note the layout of the contraption (your puzzle input). For example:
.|...\....
|.-.\.....
.....|-...
........|.
..........
.........\
..../.\\..
.-.-/..|..
.|....-|.\
..//.|....
The beam enters in the top-left corner from the left and heading to the right. Then, its behavior depends on what it encounters as it moves:
If the beam encounters empty space (.), it continues in the same direction.
If the beam encounters a mirror (/ or \), the beam is reflected 90 degrees depending on the angle of the mirror. For instance, a rightward-moving beam that encounters a / mirror would continue upward in the mirror's column, while a rightward-moving beam that encounters a \ mirror would continue downward from the mirror's column.
If the beam encounters the pointy end of a splitter (| or -), the beam passes through the splitter as if the splitter were empty space. For instance, a rightward-moving beam that encounters a - splitter would continue in the same direction.
If the beam encounters the flat side of a splitter (| or -), the beam is split into two beams going in each of the two directions the splitter's pointy ends are pointing. For instance, a rightward-moving beam that encounters a | splitter would split into two beams: one that continues upward from the splitter's column and one that continues downward from the splitter's column.
Beams do not interact with other beams; a tile can have many beams passing through it at the same time. A tile is energized if that tile has at least one beam pass through it, reflect in it, or split in it.
In the above example, here is how the beam of light bounces around the contraption:
>|<<<\....
|v-.\^....
.v...|->>>
.v...v^.|.
.v...v^...
.v...v^..\
.v../2\\..
<->-/vv|..
.|<<<2-|.\
.v//.|.v..
Beams are only shown on empty tiles; arrows indicate the direction of the beams. If a tile contains beams moving in multiple directions, the number of distinct directions is shown instead. Here is the same diagram but instead only showing whether a tile is energized (#) or not (.):
######....
.#...#....
.#...#####
.#...##...
.#...##...
.#...##...
.#..####..
########..
.#######..
.#...#.#..
Ultimately, in this example, 46 tiles become energized.
The light isn't energizing enough tiles to produce lava; to debug the contraption, you need to start by analyzing the current situation. With the beam starting in the top-left heading right, how many tiles end up being energized?
*/
fun main() {
val inlineTestInput = """
.|...\....
|.-.\.....
.....|-...
........|.
..........
.........\
..../.\\..
.-.-/..|..
.|....-|.\
..//.|....
"""
fun runBeam(origGrid: CharGrid, startPos: Pair<RelPos, RelPos>): Int {
val grid = origGrid.copyOf()
val rays = ArrayDeque<Pair<RelPos, RelPos>>()
rays.add(startPos)
while (rays.isNotEmpty()) {
var (rayPos, rayDir) = rays.removeFirst()
while (origGrid[rayPos.dc, rayPos.dr] == '/' || origGrid[rayPos.dc, rayPos.dr] == '\\' || !grid[rayPos.dc, rayPos.dr].isDigit() || (grid[rayPos.dc, rayPos.dr].code and (if (rayDir.dc != 0) 1 else 2) == 0)) {
grid[rayPos.dc, rayPos.dr] =
((if (grid[rayPos.dc, rayPos.dr].isDigit()) grid[rayPos.dc, rayPos.dr] else '0').code or (if (rayDir.dc != 0) 1 else 2)).toChar()
when (origGrid[rayPos.dc, rayPos.dr]) {
'\\' -> rayDir = RelPos(rayDir.dr, rayDir.dc)
'/' -> rayDir = RelPos(-rayDir.dr, -rayDir.dc)
'|' -> if (rayDir.dc != 0) {
rayDir = RelPos(0, -1)
rays.add(rayPos.translate(0, 1) to RelPos(0, 1))
}
'-' -> if (rayDir.dr != 0) {
rayDir = RelPos(-1, 0)
rays.add(rayPos.translate(1, 0) to RelPos(1, 0))
}
}
rayPos = rayPos.translate(rayDir)
}
}
return grid.findMatches { it.isDigit() }.count()
}
fun part1(input: List<String>): Int {
val origGrid = CharGrid(input, '3')
return runBeam(origGrid, RelPos(0, 0) to RelPos(1, 0))
}
fun part2(input: List<String>): Int {
val origGrid = CharGrid(input, '3')
var result = 0
for (tc in 0 until origGrid.width) {
result = result.coerceAtLeast(runBeam(origGrid, RelPos(tc, 0) to RelPos(0, 1)))
result = result.coerceAtLeast(runBeam(origGrid, RelPos(tc, origGrid.height - 1) to RelPos(0, -1)))
}
for (tr in 0 until origGrid.height) {
result = result.coerceAtLeast(runBeam(origGrid, RelPos(0, tr) to RelPos(1, 0)))
result = result.coerceAtLeast(runBeam(origGrid, RelPos(origGrid.width - 1, tr) to RelPos(-1, 0)))
}
return result
}
// test if implementation meets criteria from the description, like:
val testInput = inlineTestInput.trim().reader().readLines()
//val testInput = readInput("aoc2023/Day16_test")
val testInputPart1Result = part1(testInput)
println("Part 1 Test: $testInputPart1Result")
val testInputPart2Result = part2(testInput)
println("Part 2 Test: $testInputPart2Result")
check(testInputPart1Result == 46)
check(testInputPart2Result == 51)
val input = readInput("aoc2023/Day16")
part1(input).println()
part2(input).println()
}