This commit is contained in:
Chris Hodges 2023-12-08 06:40:24 +01:00
parent ba3d677880
commit 9d314599e4

144
src/aoc2023/Day08.kt Normal file
View File

@ -0,0 +1,144 @@
package aoc2023
import println
import readInput
/*
--- Day 8: Haunted Wasteland ---
You're still riding a camel across Desert Island when you spot a sandstorm quickly approaching. When you turn to warn the Elf, she disappears before your eyes! To be fair, she had just finished warning you about ghosts a few minutes ago.
One of the camel's pouches is labeled "maps" - sure enough, it's full of documents (your puzzle input) about how to navigate the desert. At least, you're pretty sure that's what they are; one of the documents contains a list of left/right instructions, and the rest of the documents seem to describe some kind of network of labeled nodes.
It seems like you're meant to use the left/right instructions to navigate the network. Perhaps if you have the camel follow the same instructions, you can escape the haunted wasteland!
After examining the maps for a bit, two nodes stick out: AAA and ZZZ. You feel like AAA is where you are now, and you have to follow the left/right instructions until you reach ZZZ.
This format defines each node of the network individually. For example:
RL
AAA = (BBB, CCC)
BBB = (DDD, EEE)
CCC = (ZZZ, GGG)
DDD = (DDD, DDD)
EEE = (EEE, EEE)
GGG = (GGG, GGG)
ZZZ = (ZZZ, ZZZ)
Starting with AAA, you need to look up the next element based on the next left/right instruction in your input. In this example, start with AAA and go right (R) by choosing the right element of AAA, CCC. Then, L means to choose the left element of CCC, ZZZ. By following the left/right instructions, you reach ZZZ in 2 steps.
Of course, you might not find ZZZ right away. If you run out of left/right instructions, repeat the whole sequence of instructions as necessary: RL really means RLRLRLRLRLRLRLRL... and so on. For example, here is a situation that takes 6 steps to reach ZZZ:
LLR
AAA = (BBB, BBB)
BBB = (AAA, ZZZ)
ZZZ = (ZZZ, ZZZ)
Starting at AAA, follow the left/right instructions. How many steps are required to reach ZZZ?
*/
fun main() {
val inlineTestInput = """
RL
AAA = (BBB, CCC)
BBB = (DDD, EEE)
CCC = (ZZZ, GGG)
DDD = (DDD, DDD)
EEE = (EEE, EEE)
GGG = (GGG, GGG)
ZZZ = (ZZZ, ZZZ)
"""
val inlineTestInput2 = """
LR
ABA = (ABB, XXX)
ABB = (XXX, ABZ)
ABZ = (ABB, XXX)
BAA = (BAB, XXX)
BAB = (BAC, BAC)
BAC = (BAZ, BAZ)
BAZ = (BAB, BAB)
XXX = (XXX, XXX)
"""
fun part1(input: List<String>): Int {
val cmd = input[0]
val map = HashMap<String, Pair<String, String>>()
for (i in input.drop(2)) {
"([A-Z][A-Z][A-Z]) = .([A-Z][A-Z][A-Z]), ([A-Z][A-Z][A-Z]).".toRegex().matchEntire(i)!!.destructured
.let { (id, left, right) -> map[id] = left to right }
}
var instPos = 0
var steps = 0
var loc = "AAA"
do {
loc = if (cmd[instPos] == 'L') map[loc]!!.first else map[loc]!!.second
instPos = (instPos + 1) % cmd.length
steps++
} while (loc != "ZZZ")
return steps
}
fun part2(input: List<String>): Long {
val cmd = input[0]
val map = HashMap<String, Pair<String, String>>()
for (i in input.drop(2)) {
"([A-Z][A-Z][A-Z]) = .([A-Z][A-Z][A-Z]), ([A-Z][A-Z][A-Z]).".toRegex().matchEntire(i)!!.destructured
.let { (id, left, right) -> map[id] = left to right }
}
val starts = map.keys.filter { it.endsWith("A") }
var ggts = HashSet<Int>()
for (start in starts) {
var instPos = 0
var steps = 0
var loc = start
do {
loc = if (cmd[instPos] == 'L') map[loc]!!.first else map[loc]!!.second
instPos = (instPos + 1) % cmd.length
steps++
} while (!loc.endsWith("Z"))
ggts.add(steps)
}
val primes = ArrayList<Int>()
val factors = HashSet<Int>()
var prime = 2
primes.add(2)
do {
val newGgts = HashSet<Int>()
for (f in ggts) {
if (prime * 2 - 1 > f) {
factors.add(f)
} else {
if (f % prime == 0) {
newGgts.add(f / prime)
factors.add(prime)
} else {
newGgts.add(f)
}
}
}
ggts = newGgts
if (prime == 2) {
prime = 3
} else {
do {
prime += 2
} while (primes.any { (prime % it) == 0 })
}
primes.add(prime)
} while (ggts.isNotEmpty())
return factors.map { it.toLong() }.reduce(Long::times)
}
// test if implementation meets criteria from the description, like:
val testInput = inlineTestInput.trim().reader().readLines()
val testInput2 = inlineTestInput2.trim().reader().readLines()
//val testInput = readInput("aoc2023/Day08_test")
val testInputPart1Result = part1(testInput)
println("Part 1 Test: $testInputPart1Result")
val testInputPart2Result = part2(testInput2)
println("Part 2 Test: $testInputPart2Result")
check(testInputPart1Result == 2)
check(testInputPart2Result == 6L)
val input = readInput("aoc2023/Day08")
part1(input).println()
part2(input).println()
}