Compare commits
56
Commits
abb0225d06
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cda0dccc65 | ||
|
|
5e031c27cc | ||
|
|
de3b0795e3 | ||
|
|
2190f9e1ac | ||
|
|
0192b33570 | ||
|
|
a69be406b1 | ||
|
|
7495f9e378 | ||
|
|
ccc3fdad4c | ||
|
|
bd591263f6 | ||
|
|
4982f2cb3c | ||
|
|
6299b10b85 | ||
|
|
caf82dd7a9 | ||
|
|
d2044f2d6a | ||
|
|
9a2a22805b | ||
|
|
909fb5c7d2 | ||
|
|
52af74ecd5 | ||
|
|
7a58627132 | ||
|
|
5fc0e8756a | ||
|
|
2b3549c84c | ||
|
|
128ffbb10f | ||
|
|
f51f87736b | ||
|
|
c82509468a | ||
|
|
732c69df0e | ||
|
|
1f323bcc82 | ||
|
|
17c6a9baac | ||
|
|
ab5fd24260 | ||
|
|
f399fcdea7 | ||
|
|
9a0b4af4ac | ||
|
|
79360efdf4 | ||
|
|
d49d3614c7 | ||
|
|
1a303d9632 | ||
|
|
e7d733846f | ||
|
|
e00e002cd6 | ||
|
|
0d5809398c | ||
|
|
277bc6cc8e | ||
|
|
e222975b2a | ||
|
|
343414cd04 | ||
|
|
bc542bd29c | ||
|
|
0cb180ddf3 | ||
|
|
f494b763cf | ||
|
|
aa0f1dad74 | ||
|
|
9047a97185 | ||
|
|
1b2332a49e | ||
|
|
b0e4461a7d | ||
|
|
76c01e01e5 | ||
|
|
3ee70151f1 | ||
|
|
0592f4a862 | ||
|
|
0d6bf1ca43 | ||
|
|
9e7ac0057b | ||
|
|
90cf5a8335 | ||
|
|
9a8b316122 | ||
|
|
778b38b3a4 | ||
|
|
34c0c213c4 | ||
|
|
3d17ed29c7 | ||
|
|
bc1888299b | ||
|
|
4b84ce2803 |
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.9.20"
|
kotlin("jvm") version "2.1.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@@ -8,7 +8,7 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.github.kittinunf.fuel:fuel:3.0.0-alpha1")
|
implementation("com.github.kittinunf.fuel:fuel:3.0.0-alpha1")
|
||||||
implementation("com.mohamedrejeb.ksoup:ksoup-html:0.2.1")
|
implementation("com.mohamedrejeb.ksoup:ksoup-html:0.6.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks {
|
tasks {
|
||||||
@@ -19,6 +19,6 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wrapper {
|
wrapper {
|
||||||
gradleVersion = "8.4"
|
gradleVersion = "8.12"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+101
-1
@@ -1,4 +1,5 @@
|
|||||||
import java.lang.Long.numberOfTrailingZeros
|
import java.lang.Long.numberOfTrailingZeros
|
||||||
|
import java.math.BigInteger
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
@@ -187,7 +188,106 @@ fun gcdPositive(aIn: Long, bIn: Long): Long {
|
|||||||
return a shl shift
|
return a shl shift
|
||||||
}
|
}
|
||||||
|
|
||||||
fun calcPrimeFactorsAndPhi(n: Long, primes: MutableList<Long>, allPrimes: MutableSet<Long>): Pair<List<Pair<Long, Int>>, Long> {
|
// ax + by = gcdExtendedPositive(a, b)
|
||||||
|
fun extendedGcd(a: Long, b: Long): Pair<Long, Pair<Long, Long>> {
|
||||||
|
var old_r = a
|
||||||
|
var r = b
|
||||||
|
var old_s = 1L
|
||||||
|
var s = 0L
|
||||||
|
var old_t = 0L
|
||||||
|
var t = 1L
|
||||||
|
while (r != 0L) {
|
||||||
|
val q = old_r / r
|
||||||
|
val rtmp = old_r
|
||||||
|
old_r = r
|
||||||
|
r = rtmp - q * r
|
||||||
|
val stmp = old_s
|
||||||
|
old_s = s
|
||||||
|
s = stmp - q * s
|
||||||
|
val ttmp = old_t
|
||||||
|
old_t = t
|
||||||
|
t = ttmp - q * t
|
||||||
|
}
|
||||||
|
|
||||||
|
return old_r to (old_s to old_t)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extendedGcd(a: BigInteger, b: BigInteger): Pair<BigInteger, Pair<BigInteger, BigInteger>> {
|
||||||
|
var old_r = a
|
||||||
|
var r = b
|
||||||
|
var old_s = BigInteger.ONE
|
||||||
|
var s = BigInteger.ZERO
|
||||||
|
var old_t = BigInteger.ZERO
|
||||||
|
var t = BigInteger.ONE
|
||||||
|
while (r != BigInteger.ZERO) {
|
||||||
|
val q = old_r / r
|
||||||
|
val rtmp = old_r
|
||||||
|
old_r = r
|
||||||
|
r = rtmp - q * r
|
||||||
|
val stmp = old_s
|
||||||
|
old_s = s
|
||||||
|
s = stmp - q * s
|
||||||
|
val ttmp = old_t
|
||||||
|
old_t = t
|
||||||
|
t = ttmp - q * t
|
||||||
|
}
|
||||||
|
|
||||||
|
return old_r to (old_s to old_t)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extendedGcd(v: List<Long>): Pair<Long, List<Long>> {
|
||||||
|
if (v.size < 2) throw IllegalArgumentException("Expected at least 2 elements")
|
||||||
|
|
||||||
|
val gcds = ArrayList<Long>(v.size)
|
||||||
|
val coeffs = ArrayList<Long>(v.size)
|
||||||
|
var (gcd, p1) = extendedGcd(v[0], v[1])
|
||||||
|
coeffs.add(p1.first)
|
||||||
|
coeffs.add(p1.second)
|
||||||
|
gcds.add(gcd)
|
||||||
|
gcds.add(gcd)
|
||||||
|
for (i in 2 until v.size) {
|
||||||
|
val (gcdnew, pi) = extendedGcd(gcd, v[i])
|
||||||
|
gcd = gcdnew
|
||||||
|
coeffs.add(pi.second)
|
||||||
|
gcds.add(gcd)
|
||||||
|
}
|
||||||
|
for (i in gcds.indices) {
|
||||||
|
if (gcds[i] != gcd) {
|
||||||
|
coeffs[i] *= gcds[i] / gcd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gcd to coeffs
|
||||||
|
}
|
||||||
|
|
||||||
|
fun extendedGcdBigInteger(v: List<BigInteger>): Pair<BigInteger, List<BigInteger>> {
|
||||||
|
if (v.size < 2) throw IllegalArgumentException("Expected at least 2 elements")
|
||||||
|
|
||||||
|
val gcds = ArrayList<BigInteger>(v.size)
|
||||||
|
val coeffs = ArrayList<BigInteger>(v.size)
|
||||||
|
var (gcd, p1) = extendedGcd(v[0], v[1])
|
||||||
|
coeffs.add(p1.first)
|
||||||
|
coeffs.add(p1.second)
|
||||||
|
gcds.add(gcd)
|
||||||
|
gcds.add(gcd)
|
||||||
|
for (i in 2 until v.size) {
|
||||||
|
val (gcdnew, pi) = extendedGcd(gcd, v[i])
|
||||||
|
gcd = gcdnew
|
||||||
|
coeffs.add(pi.second)
|
||||||
|
gcds.add(gcd)
|
||||||
|
}
|
||||||
|
for (i in gcds.indices) {
|
||||||
|
if (gcds[i] != gcd) {
|
||||||
|
coeffs[i] *= gcds[i] / gcd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return gcd to coeffs
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calcPrimeFactorsAndPhi(
|
||||||
|
n: Long,
|
||||||
|
primes: MutableList<Long>,
|
||||||
|
allPrimes: MutableSet<Long>
|
||||||
|
): Pair<List<Pair<Long, Int>>, Long> {
|
||||||
val factors = ArrayList<Pair<Long, Int>>()
|
val factors = ArrayList<Pair<Long, Int>>()
|
||||||
var phi = 1L
|
var phi = 1L
|
||||||
var rem = n
|
var rem = n
|
||||||
|
|||||||
+17
-3
@@ -75,6 +75,13 @@ class CharGrid {
|
|||||||
val data: Array<CharArray>
|
val data: Array<CharArray>
|
||||||
val bChar: Char
|
val bChar: Char
|
||||||
|
|
||||||
|
constructor(width: Int, height: Int, borderChar: Char = ' ', fillChar: Char = borderChar) {
|
||||||
|
bChar = borderChar
|
||||||
|
this.width = width
|
||||||
|
this.height = height
|
||||||
|
data = Array(height) { CharArray(width) { fillChar } }
|
||||||
|
}
|
||||||
|
|
||||||
constructor(input: List<String>, borderChar: Char = ' ') {
|
constructor(input: List<String>, borderChar: Char = ' ') {
|
||||||
bChar = borderChar
|
bChar = borderChar
|
||||||
width = input[0].length
|
width = input[0].length
|
||||||
@@ -89,10 +96,8 @@ class CharGrid {
|
|||||||
height = inputGrid.size
|
height = inputGrid.size
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Use RelPos version instead")
|
|
||||||
operator fun get(col: Int, row: Int) = getOrNull(col, row) ?: bChar
|
operator fun get(col: Int, row: Int) = getOrNull(col, row) ?: bChar
|
||||||
|
|
||||||
@Deprecated("Use RelPos version instead")
|
|
||||||
operator fun set(col: Int, row: Int, newChar: Char?) {
|
operator fun set(col: Int, row: Int, newChar: Char?) {
|
||||||
if (newChar != null && isInside(col, row)) data[row][col] = newChar
|
if (newChar != null && isInside(col, row)) data[row][col] = newChar
|
||||||
}
|
}
|
||||||
@@ -147,6 +152,9 @@ class CharGrid {
|
|||||||
fun applyWithPos(op: (grid: CharGrid, pos: RelPos) -> Char?) =
|
fun applyWithPos(op: (grid: CharGrid, pos: RelPos) -> Char?) =
|
||||||
generateGridPos().forEach { this[it] = op(this, it) }
|
generateGridPos().forEach { this[it] = op(this, it) }
|
||||||
|
|
||||||
|
fun applyWithPos(relposes: Iterable<RelPos>, op: (grid: CharGrid, pos: RelPos) -> Char?) =
|
||||||
|
relposes.forEach { this[it] = op(this, it) }
|
||||||
|
|
||||||
fun apply(op: (content: Char) -> Char?) =
|
fun apply(op: (content: Char) -> Char?) =
|
||||||
applyWithPos { grid: CharGrid, pos -> op(grid[pos]) }
|
applyWithPos { grid: CharGrid, pos -> op(grid[pos]) }
|
||||||
|
|
||||||
@@ -196,6 +204,12 @@ class CharGrid {
|
|||||||
fun matchRelative(c: Int, r: Int, relposes: Iterable<RelPos>, predicate: (char: Char) -> Boolean): List<RelPos> =
|
fun matchRelative(c: Int, r: Int, relposes: Iterable<RelPos>, predicate: (char: Char) -> Boolean): List<RelPos> =
|
||||||
relposes.filter { predicate(get(c + it.dc, r + it.dr)) }
|
relposes.filter { predicate(get(c + it.dc, r + it.dr)) }
|
||||||
|
|
||||||
|
fun matchRelative(pos: RelPos, relposes: Iterable<RelPos>, predicate: (char: Char) -> Boolean): List<RelPos> =
|
||||||
|
relposes.filter { predicate(get(pos.translate(it))) }
|
||||||
|
|
||||||
|
fun matchAbsoluteRelPos(pos: RelPos, relposes: Iterable<RelPos>, predicate: (char: Char) -> Boolean): List<RelPos> =
|
||||||
|
relposes.map { pos.translate(it) }.filter { predicate(get(it)) }
|
||||||
|
|
||||||
fun marchMatching(pos: RelPos, relposes: Iterable<RelPos>, predicate: (char: Char) -> Boolean): List<RelPos> =
|
fun marchMatching(pos: RelPos, relposes: Iterable<RelPos>, predicate: (char: Char) -> Boolean): List<RelPos> =
|
||||||
relposes.map { pos.translate(it) }.filter { predicate(get(it)) }
|
relposes.map { pos.translate(it) }.filter { predicate(get(it)) }
|
||||||
|
|
||||||
@@ -215,7 +229,7 @@ class CharGrid {
|
|||||||
val topBottom = CharArray(width + 2) { borderChar }
|
val topBottom = CharArray(width + 2) { borderChar }
|
||||||
return CharGrid(Array(height + 2) {
|
return CharGrid(Array(height + 2) {
|
||||||
if (it == 0 || it == height + 1) topBottom else
|
if (it == 0 || it == height + 1) topBottom else
|
||||||
CharArray(width + 2) { r -> if (r == 0 || r == width + 1) borderChar else get(it - 1, r - 1) }
|
CharArray(width + 2) { r -> if (r == 0 || r == width + 1) borderChar else get(r - 1, it - 1) }
|
||||||
}, borderChar)
|
}, borderChar)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package aoc2015
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 6: Probably a Fire Hazard ---
|
||||||
|
https://adventofcode.com/2015/day/6
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val grid = CharGrid(1000, 1000)
|
||||||
|
input.forEach {
|
||||||
|
val (op, x1, y1, x2, y2) = "(toggle|turn off|turn on) (\\d+),(\\d+) through (\\d+),(\\d+)".toRegex().matchEntire(it)!!.destructured
|
||||||
|
for (y in y1.toInt()..y2.toInt()) {
|
||||||
|
for (x in x1.toInt()..x2.toInt()) {
|
||||||
|
grid[x, y] = when (op) {
|
||||||
|
"turn on" -> '*'
|
||||||
|
"turn off" -> ' '
|
||||||
|
"toggle" -> if (grid[x, y] == '*') ' ' else '*'
|
||||||
|
else -> throw IllegalStateException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return grid.generateGridPos().count { grid[it] == '*' }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
val grid = IntArray(1000 * 1000)
|
||||||
|
input.forEach {
|
||||||
|
val (op, x1, y1, x2, y2) = "(toggle|turn off|turn on) (\\d+),(\\d+) through (\\d+),(\\d+)".toRegex().matchEntire(it)!!.destructured
|
||||||
|
for (y in y1.toInt()..y2.toInt()) {
|
||||||
|
for (x in x1.toInt()..x2.toInt()) {
|
||||||
|
grid[x + y * 1000] = (grid[x + y * 1000] + when (op) {
|
||||||
|
"turn on" -> 1
|
||||||
|
"turn off" -> -1
|
||||||
|
"toggle" -> 2
|
||||||
|
else -> throw IllegalStateException()
|
||||||
|
}).coerceAtLeast(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return grid.sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
val input = readInput("aoc2015/Day06")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
+30
-34
@@ -2,6 +2,7 @@ package aoc2024
|
|||||||
|
|
||||||
import println
|
import println
|
||||||
import readInput
|
import readInput
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
/*
|
/*
|
||||||
--- Day 9: Disk Fragmenter ---
|
--- Day 9: Disk Fragmenter ---
|
||||||
@@ -45,60 +46,55 @@ fun main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun part2(input: List<String>): Long {
|
fun part2(input: List<String>): Long {
|
||||||
val diskSize = input[0].sumOf { it - '0' }
|
|
||||||
val bitmap = IntArray(diskSize) { 0 }
|
|
||||||
var pos = 0
|
var pos = 0
|
||||||
var isFile = true
|
var isFile = true
|
||||||
var fileId = 0
|
val freeList = Array<MutableSet<Int>>(10) { TreeSet() }
|
||||||
val freeList = HashMap<Int, MutableList<Int>>()
|
val fileList = ArrayList<Pair<Int, Int>>((input[0].length + 1) / 2)
|
||||||
val fileList = ArrayList<Pair<Int, Pair<Int, Int>>>()
|
|
||||||
val newFileList = ArrayList<Pair<Int, Pair<Int, Int>>>()
|
|
||||||
for (c in input[0]) {
|
for (c in input[0]) {
|
||||||
val size = c - '0'
|
val size = c - '0'
|
||||||
if (isFile) {
|
if (isFile) {
|
||||||
if (size == 0) println("Narf!")
|
fileList.add(pos to size)
|
||||||
fileList.add(fileId to (pos to size))
|
} else if (size > 0) {
|
||||||
fileId++
|
freeList[size].add(pos)
|
||||||
} else {
|
|
||||||
if (size > 0) {
|
|
||||||
freeList.getOrPut(size) { ArrayDeque(0) }.add(pos)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pos += size
|
pos += size
|
||||||
isFile = !isFile
|
isFile = !isFile
|
||||||
}
|
}
|
||||||
|
|
||||||
for (file in fileList.reversed()) {
|
val newFileList = Array<Pair<Int, Int>?>(fileList.size) { null }
|
||||||
val (filePos, size) = file.second
|
var revPos = fileList.size
|
||||||
var bestList: MutableList<Int>? = null
|
for (file in fileList.asReversed()) {
|
||||||
|
val (filePos, size) = file
|
||||||
var bestListSize = 0
|
var bestListSize = 0
|
||||||
|
var bestListPos = 0
|
||||||
for (s in size..9) {
|
for (s in size..9) {
|
||||||
val list = freeList[s]
|
val list = freeList[s]
|
||||||
if (list?.isNotEmpty() == true && list[0] < filePos) {
|
val firstPos = list.firstOrNull()
|
||||||
if (bestList == null || list[0] < bestList[0]) {
|
if ((firstPos != null && firstPos < filePos) &&
|
||||||
bestList = list
|
(bestListSize == 0 || firstPos < bestListPos)
|
||||||
|
) {
|
||||||
bestListSize = s
|
bestListSize = s
|
||||||
|
bestListPos = firstPos
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (bestListSize != 0) {
|
||||||
if (bestList != null) {
|
val newPos = freeList[bestListSize].first()
|
||||||
val newPos = bestList.removeAt(0)
|
freeList[bestListSize].remove(newPos)
|
||||||
if (bestListSize > size) {
|
if (bestListSize > size) freeList[bestListSize - size].add(newPos + size)
|
||||||
val remBucket = freeList.getOrPut(bestListSize - size) { ArrayDeque(0) }
|
newFileList[--revPos] = newPos to size
|
||||||
remBucket.add(newPos + size)
|
|
||||||
remBucket.sort()
|
|
||||||
}
|
|
||||||
newFileList.add(file.first to (newPos to size))
|
|
||||||
} else {
|
} else {
|
||||||
newFileList.add(file)
|
newFileList[--revPos] = file
|
||||||
|
// stop if not even a size 1 file could be fit
|
||||||
|
if (size == 1) break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (file in newFileList) {
|
while (revPos >= 0) {
|
||||||
for (i in 0 until file.second.second) {
|
newFileList[revPos] = fileList[revPos]
|
||||||
bitmap[i + file.second.first] = file.first
|
revPos--
|
||||||
}
|
}
|
||||||
}
|
// Gauß to the rescue!
|
||||||
return bitmap.map(Int::toLong).reduceIndexed { index, acc, i -> acc + index * i }
|
return newFileList.mapIndexed { index, file -> index * (file!!.first.toLong() * file.second + (file.second * (file.second - 1) / 2)) }
|
||||||
|
.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
// test if implementation meets criteria from the description, like:
|
// test if implementation meets criteria from the description, like:
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import splitLongs
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 11: Plutonian Pebbles ---
|
||||||
|
https://adventofcode.com/2024/day/11
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
125 17
|
||||||
|
"""
|
||||||
|
|
||||||
|
val powTenTable = generateSequence(1L) { it * 10L }.take(20).toList().toLongArray()
|
||||||
|
val lookupTable = HashMap<Pair<Long, Int>, Long>()
|
||||||
|
|
||||||
|
fun fastCeilLog10(n: Long): Int {
|
||||||
|
for (i in 1 until powTenTable.size) {
|
||||||
|
if (powTenTable[i] > n) return i
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rec(n: Long, i: Int): Long =
|
||||||
|
if (i == 0) 1 else lookupTable.getOrPut(n to i) {
|
||||||
|
if (n == 0L) {
|
||||||
|
rec(1L, i - 1)
|
||||||
|
} else {
|
||||||
|
val size = fastCeilLog10(n)
|
||||||
|
if (size and 1 == 0) {
|
||||||
|
val left = n / powTenTable[size / 2]
|
||||||
|
val right = n % powTenTable[size / 2]
|
||||||
|
rec(left, i - 1) + rec(right, i - 1)
|
||||||
|
} else {
|
||||||
|
rec(n * 2024L, i - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Long {
|
||||||
|
val numbers = input[0].splitLongs()
|
||||||
|
return numbers.sumOf { rec(it, 25) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
val numbers = input[0].splitLongs()
|
||||||
|
return numbers.sumOf { rec(it, 75) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day11_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 55312L)
|
||||||
|
//check(testInputPart2Result == 0L)
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day11")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import RelPos
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 12: Garden Groups ---
|
||||||
|
https://adventofcode.com/2024/day/12
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
RRRRIICCFF
|
||||||
|
RRRRIICCCF
|
||||||
|
VVRRRCCFFF
|
||||||
|
VVRCCCJFFF
|
||||||
|
VVVVCJJCFE
|
||||||
|
VVIVCCJJEE
|
||||||
|
VVIIICJJEE
|
||||||
|
MIIIIIJJEE
|
||||||
|
MIIISIJEEE
|
||||||
|
MMMISSJEEE
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val grid = CharGrid(input)
|
||||||
|
var sum = 0
|
||||||
|
for (p in grid.generateGridPos()) {
|
||||||
|
val c = grid[p]
|
||||||
|
if (c != '.') {
|
||||||
|
val areaPos = HashSet<RelPos>()
|
||||||
|
val fence = HashSet<RelPos>()
|
||||||
|
var newPos = setOf(p)
|
||||||
|
while (newPos.isNotEmpty()) {
|
||||||
|
val newSeeds = HashSet<RelPos>()
|
||||||
|
for (np in newPos) {
|
||||||
|
areaPos.add(np)
|
||||||
|
grid[np] = c.lowercaseChar()
|
||||||
|
newSeeds.addAll(grid.matchAbsoluteRelPos(np, CharGrid.PLUS_POS) { it == c })
|
||||||
|
fence.addAll(grid.matchRelative(np.dc, np.dr, CharGrid.PLUS_POS) { it.uppercaseChar() != c }
|
||||||
|
.map { RelPos(np.dc * 2 + it.dc, np.dr * 2 + it.dr) })
|
||||||
|
}
|
||||||
|
newPos = newSeeds
|
||||||
|
}
|
||||||
|
areaPos.forEach { grid[it] = '.' }
|
||||||
|
sum += areaPos.size * fence.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
val grid = CharGrid(input)
|
||||||
|
var sum = 0
|
||||||
|
val magic = listOf(3, 5, 10, 12)
|
||||||
|
val star = CharGrid.PLUS_POS.plus(CharGrid.CROSS_POS)
|
||||||
|
for (p in grid.generateGridPos()) {
|
||||||
|
val c = grid[p]
|
||||||
|
if (c != '.') {
|
||||||
|
val areaPos = HashSet<RelPos>()
|
||||||
|
var newPos = setOf(p)
|
||||||
|
while (newPos.isNotEmpty()) {
|
||||||
|
val newSeeds = HashSet<RelPos>()
|
||||||
|
for (np in newPos) {
|
||||||
|
areaPos.add(np)
|
||||||
|
grid[np] = '.'
|
||||||
|
newSeeds.addAll(grid.matchAbsoluteRelPos(np, CharGrid.PLUS_POS) { it == c })
|
||||||
|
}
|
||||||
|
newPos = newSeeds
|
||||||
|
}
|
||||||
|
val corners = areaPos.sumOf {
|
||||||
|
val b = star.mapIndexed { i, rp -> if (areaPos.contains(it.translate(rp))) 1 shl i else 0 }.sum()
|
||||||
|
val outer = magic.count { b and it == 0 }
|
||||||
|
val inner = magic.filterIndexed { i, m -> (b and m == m) && (b and (16 shl i) == 0) }.count()
|
||||||
|
outer + inner
|
||||||
|
}
|
||||||
|
sum += areaPos.size * corners
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day12_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 1930)
|
||||||
|
check(testInputPart2Result == 1206)
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day12")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 13: Claw Contraption ---
|
||||||
|
https://adventofcode.com/2024/day/13
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
Button A: X+94, Y+34
|
||||||
|
Button B: X+22, Y+67
|
||||||
|
Prize: X=8400, Y=5400
|
||||||
|
|
||||||
|
Button A: X+26, Y+66
|
||||||
|
Button B: X+67, Y+21
|
||||||
|
Prize: X=12748, Y=12176
|
||||||
|
|
||||||
|
Button A: X+17, Y+86
|
||||||
|
Button B: X+84, Y+37
|
||||||
|
Prize: X=7870, Y=6450
|
||||||
|
|
||||||
|
Button A: X+69, Y+23
|
||||||
|
Button B: X+27, Y+71
|
||||||
|
Prize: X=18641, Y=10279
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
var i = 0
|
||||||
|
var sum = 0
|
||||||
|
while (i < input.size) {
|
||||||
|
val (ax, ay) = "Button A: X\\+(\\d+), Y\\+(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toInt() }
|
||||||
|
val (bx, by) = "Button B: X\\+(\\d+), Y\\+(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toInt() }
|
||||||
|
val (px, py) = "Prize: X=(\\d+), Y=(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toInt() }
|
||||||
|
i++
|
||||||
|
val xpos = IntArray(101 * 101)
|
||||||
|
val ypos = IntArray(101 * 101)
|
||||||
|
val solutions = ArrayList<Int>()
|
||||||
|
for (b in 1..100) {
|
||||||
|
for (a in 1..100) {
|
||||||
|
val op = (a - 1) + (b - 1) * 101
|
||||||
|
if ((xpos[op] == px) && (ypos[op] == py)) {
|
||||||
|
solutions.add(op)
|
||||||
|
}
|
||||||
|
if ((xpos[op] > px) || (ypos[op] > py)) break
|
||||||
|
xpos[a + (b - 1) * 101] = xpos[op] + ax
|
||||||
|
xpos[(a - 1) + b * 101] = xpos[op] + bx
|
||||||
|
ypos[a + (b - 1) * 101] = ypos[op] + ay
|
||||||
|
ypos[(a - 1) + b * 101] = ypos[op] + by
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val minTokens = solutions.minOfOrNull { (it % 101) * 3 + (it / 101) }
|
||||||
|
sum += minTokens ?: 0
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
var i = 0
|
||||||
|
var sum = 0L
|
||||||
|
|
||||||
|
// 1. ax * pa + bx * pb = px
|
||||||
|
// 2. ay * pa + by * pb = py
|
||||||
|
// 3. pa * 3 + pb <- min
|
||||||
|
// 4. pa, pb in N (int > 0)
|
||||||
|
|
||||||
|
// ax * pa + bx * pb - px = ay * pa + by * pb - py
|
||||||
|
// (ax - ay) * pa + (bx - by) * pb + py - px = 0
|
||||||
|
// (ax - ay) * pa + (bx - by) * pb = px - py
|
||||||
|
|
||||||
|
while (i < input.size) {
|
||||||
|
val (ax, ay) = "Button A: X\\+(\\d+), Y\\+(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toLong() }
|
||||||
|
val (bx, by) = "Button B: X\\+(\\d+), Y\\+(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toLong() }
|
||||||
|
val (px, py) = "Prize: X=(\\d+), Y=(\\d+)".toRegex().find(input[i++])!!.groupValues.drop(1).map { it.toLong() + 10000000000000L }
|
||||||
|
i++
|
||||||
|
|
||||||
|
// val t1 = ax - ay
|
||||||
|
// val t2 = bx - by
|
||||||
|
// val t3 = px - py
|
||||||
|
|
||||||
|
// Linear Diophantine equations
|
||||||
|
/* The simplest linear Diophantine equation takes the form
|
||||||
|
a*x + b*y = c
|
||||||
|
where a, b and c are given integers.
|
||||||
|
The solutions are described by the following theorem:
|
||||||
|
This Diophantine equation has a solution (where x and y are integers),
|
||||||
|
IFF c is a multiple of the greatest common divisor of a and b.
|
||||||
|
Moreover, if (x, y) is a solution, then the other solutions have the
|
||||||
|
form (x + kv, y − ku), where
|
||||||
|
- k is an arbitrary integer, and
|
||||||
|
- u and v are the quotients of a and b (respectively) by the greatest common divisor of a and b.
|
||||||
|
*/
|
||||||
|
// With our input data, all equations have exactly one solution
|
||||||
|
val pad = (px * by - py * bx)
|
||||||
|
val pan = (ax * by - ay * bx)
|
||||||
|
if (pad % pan == 0L) {
|
||||||
|
val pa = pad / pan
|
||||||
|
val pbd = (px - pa * ax)
|
||||||
|
if (pbd % bx == 0L) {
|
||||||
|
val pb = pbd / bx
|
||||||
|
sum += pa * 3 + pb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day13_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 480)
|
||||||
|
//check(testInputPart2Result == 0L)
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day13")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 14: Restroom Redoubt ---
|
||||||
|
https://adventofcode.com/2024/day/14
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
p=0,4 v=3,-3
|
||||||
|
p=6,3 v=-1,-3
|
||||||
|
p=10,3 v=-1,2
|
||||||
|
p=2,0 v=2,-1
|
||||||
|
p=0,0 v=1,3
|
||||||
|
p=3,0 v=-2,-2
|
||||||
|
p=7,6 v=-1,-3
|
||||||
|
p=3,0 v=-1,-2
|
||||||
|
p=9,3 v=2,3
|
||||||
|
p=7,3 v=-1,2
|
||||||
|
p=2,4 v=2,-3
|
||||||
|
p=9,5 v=-3,-3
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>, time: Int, w: Int, h: Int): Int {
|
||||||
|
val robots = input.map { "p=(\\d+),(\\d+) v=(-?\\d+),(-?\\d+)".toRegex().find(it)!!.groupValues.drop(1).map { it.toInt() }.toIntArray() }
|
||||||
|
for (r in robots) {
|
||||||
|
r[0] = (r[0] + time * (r[2] + w)) % w
|
||||||
|
r[1] = (r[1] + time * (r[3] + h)) % h
|
||||||
|
}
|
||||||
|
val q1 = robots.count { it[0] < w / 2 && it[1] < h / 2 }
|
||||||
|
val q2 = robots.count { it[0] > w / 2 && it[1] < h / 2 }
|
||||||
|
val q3 = robots.count { it[0] < w / 2 && it[1] > h / 2 }
|
||||||
|
val q4 = robots.count { it[0] > w / 2 && it[1] > h / 2 }
|
||||||
|
return q1 * q2 * q3 * q4
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>, w: Int, h: Int): Int {
|
||||||
|
val robots = input.map { "p=(\\d+),(\\d+) v=(-?\\d+),(-?\\d+)".toRegex().find(it)!!.groupValues.drop(1).map { it.toInt() }.toIntArray() }
|
||||||
|
/*
|
||||||
|
either: 1) x == w/2 -+ c, y == c c in [0..w/2]
|
||||||
|
or : 2) x == w/2, y in (w/2..h)
|
||||||
|
|
||||||
|
1) px + t * (vx + w) % w = w/2 - c; py + t * (vy + h) % h = c
|
||||||
|
px + t * (vx + w) % w = w/2 - (py + t * (vy + h) % h)
|
||||||
|
(px - py) + t * (((vx + w) % w) - ((vy + h) % h)) = w/2
|
||||||
|
|
||||||
|
2) px == w/2, py > w/2 % h
|
||||||
|
*/
|
||||||
|
val z = Array(robots.size) { IntArray(4) }
|
||||||
|
for (t in 1..w * h) {
|
||||||
|
/*val good = robots.all {
|
||||||
|
val x = ((it[0] + t * (it[2] + w)) % w).toInt()
|
||||||
|
val y = ((it[1] + t * (it[3] + h)) % h).toInt()
|
||||||
|
(y <= w / 2 && ((x >= w / 2 - y) && (x <= w / 2 + y))) || (x == w / 2 && y > w / 2)
|
||||||
|
}
|
||||||
|
if (!good) continue*/
|
||||||
|
for ((i, r) in robots.withIndex()) {
|
||||||
|
z[i][0] = ((r[0] + t * (r[2] + w)) % w).toInt()
|
||||||
|
z[i][1] = ((r[1] + t * (r[3] + h)) % h).toInt()
|
||||||
|
}
|
||||||
|
if (z.map { it[0] to it[1] }.distinct().count() < robots.size) continue
|
||||||
|
println("Solution $t")
|
||||||
|
val grid = CharGrid(w, h, '.')
|
||||||
|
z.forEach { grid[it[0], it[1]] = '*' }
|
||||||
|
grid.debug()
|
||||||
|
println()
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day14_test")
|
||||||
|
val testInputPart1Result = part1(testInput, 100, 11, 7)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
//val testInputPart2Result = part2(testInput, 11, 7)
|
||||||
|
//println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 12)
|
||||||
|
//check(testInputPart2Result == 0)
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day14")
|
||||||
|
part1(input, 100, 101, 103).println()
|
||||||
|
part2(input, 101, 103).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 15: Warehouse Woes ---
|
||||||
|
https://adventofcode.com/2024/day/15
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput2 = """
|
||||||
|
##########
|
||||||
|
#..O..O.O#
|
||||||
|
#......O.#
|
||||||
|
#.OO..O.O#
|
||||||
|
#..O@..O.#
|
||||||
|
#O#..O...#
|
||||||
|
#O..O..O.#
|
||||||
|
#.OO.O.OO#
|
||||||
|
#....O...#
|
||||||
|
##########
|
||||||
|
|
||||||
|
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
|
||||||
|
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
|
||||||
|
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
|
||||||
|
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
|
||||||
|
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
|
||||||
|
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
|
||||||
|
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
|
||||||
|
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
|
||||||
|
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
|
||||||
|
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^
|
||||||
|
"""
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
########
|
||||||
|
#..O.O.#
|
||||||
|
##@.O..#
|
||||||
|
#...O..#
|
||||||
|
#.#.O..#
|
||||||
|
#...O..#
|
||||||
|
#......#
|
||||||
|
########
|
||||||
|
|
||||||
|
<^^>>>vv<v>>v<<
|
||||||
|
"""
|
||||||
|
|
||||||
|
val inlineTestInput3 = """
|
||||||
|
#######
|
||||||
|
#...#.#
|
||||||
|
#.....#
|
||||||
|
#..OO@#
|
||||||
|
#..O..#
|
||||||
|
#.....#
|
||||||
|
#######
|
||||||
|
|
||||||
|
<vv<<^^<<^^
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val splitp = input.withIndex().find { it.value.isEmpty() }!!.index
|
||||||
|
val grid = CharGrid(input.take(splitp))
|
||||||
|
val inst = input.drop(splitp).joinToString("")
|
||||||
|
var (rc, rd) = grid.findMatches { it == '@' }[0]
|
||||||
|
for (m in inst) {
|
||||||
|
grid[rc, rd] = '.'
|
||||||
|
var dc = 0
|
||||||
|
var dr = 0
|
||||||
|
when (m) {
|
||||||
|
'<' -> dc = -1
|
||||||
|
'>' -> dc = 1
|
||||||
|
'^' -> dr = -1
|
||||||
|
'v' -> dr = 1
|
||||||
|
}
|
||||||
|
var s = 1
|
||||||
|
while (grid[rc + dc * s, rd + dr * s] != '.') {
|
||||||
|
if (grid[rc + dc * s, rd + dr * s] == '#') {
|
||||||
|
s = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s++
|
||||||
|
}
|
||||||
|
if (s > 0) {
|
||||||
|
if (s > 1) {
|
||||||
|
grid[rc + dc * s, rd + dr * s] = 'O'
|
||||||
|
}
|
||||||
|
rc += dc
|
||||||
|
rd += dr
|
||||||
|
}
|
||||||
|
grid[rc, rd] = '@'
|
||||||
|
}
|
||||||
|
return grid.generateGridPos().filter { grid[it] == 'O' }.sumOf { it.dc + it.dr * 100 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun canMove(grid: CharGrid, bc: Int, rd: Int, dr: Int): Boolean {
|
||||||
|
if ((grid[bc, rd] == '[')) {
|
||||||
|
val c1 = grid[bc, rd + dr]
|
||||||
|
val c2 = grid[bc + 1, rd + dr]
|
||||||
|
if ((c1 == '.') && (c2 == '.')) return true
|
||||||
|
if ((c1 == '#') || (c2 == '#')) return false
|
||||||
|
if ((c1 == '[')) return canMove(grid, bc, rd + dr, dr)
|
||||||
|
return (if (c1 == ']') canMove(grid, bc - 1, rd + dr, dr) else true)
|
||||||
|
&& (if (c2 == '[') canMove(grid, bc + 1, rd + dr, dr) else true)
|
||||||
|
} else if ((grid[bc, rd] == ']')) {
|
||||||
|
return canMove(grid, bc - 1, rd, dr)
|
||||||
|
} else {
|
||||||
|
throw IllegalStateException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun doMove(grid: CharGrid, bc: Int, rd: Int, dr: Int) {
|
||||||
|
if ((grid[bc, rd] == '[')) {
|
||||||
|
val c1 = grid[bc, rd + dr]
|
||||||
|
val c2 = grid[bc + 1, rd + dr]
|
||||||
|
if ((c1 == '[')) {
|
||||||
|
doMove(grid, bc, rd + dr, dr)
|
||||||
|
} else {
|
||||||
|
if (c1 == ']') doMove(grid, bc - 1, rd + dr, dr)
|
||||||
|
if (c2 == '[') doMove(grid, bc + 1, rd + dr, dr)
|
||||||
|
}
|
||||||
|
if ((grid[bc, rd + dr] == '.') && (grid[bc + 1, rd + dr] == '.')) {
|
||||||
|
grid[bc, rd + dr] = '['
|
||||||
|
grid[bc + 1, rd + dr] = ']'
|
||||||
|
grid[bc, rd] = '.'
|
||||||
|
grid[bc + 1, rd] = '.'
|
||||||
|
} else {
|
||||||
|
throw IllegalStateException()
|
||||||
|
}
|
||||||
|
} else if ((grid[bc, rd] == ']')) {
|
||||||
|
doMove(grid, bc - 1, rd, dr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
val splitp = input.withIndex().find { it.value.isEmpty() }!!.index
|
||||||
|
val sgrid = CharGrid(input.take(splitp))
|
||||||
|
val grid = CharGrid(sgrid.width * 2, sgrid.height)
|
||||||
|
sgrid.generateGridPos().forEach {
|
||||||
|
when (val cc = sgrid[it]) {
|
||||||
|
'@' -> {
|
||||||
|
grid[it.dc * 2, it.dr] = cc
|
||||||
|
grid[it.dc * 2 + 1, it.dr] = '.'
|
||||||
|
}
|
||||||
|
|
||||||
|
'O' -> {
|
||||||
|
grid[it.dc * 2, it.dr] = '['
|
||||||
|
grid[it.dc * 2 + 1, it.dr] = ']'
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
grid[it.dc * 2, it.dr] = cc
|
||||||
|
grid[it.dc * 2 + 1, it.dr] = cc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val inst = input.drop(splitp).joinToString("")
|
||||||
|
var (rc, rd) = grid.findMatches { it == '@' }[0]
|
||||||
|
for (m in inst) {
|
||||||
|
grid[rc, rd] = '.'
|
||||||
|
var dc = 0
|
||||||
|
var dr = 0
|
||||||
|
when (m) {
|
||||||
|
'<' -> dc = -1
|
||||||
|
'>' -> dc = 1
|
||||||
|
'^' -> dr = -1
|
||||||
|
'v' -> dr = 1
|
||||||
|
}
|
||||||
|
if (dc != 0) {
|
||||||
|
var s = 1
|
||||||
|
var bc: Char? = null
|
||||||
|
while (grid[rc + dc * s, rd] != '.') {
|
||||||
|
val cc = grid[rc + dc * s, rd]
|
||||||
|
if (cc == '#') {
|
||||||
|
s = 0
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
if (bc != null && cc == bc) {
|
||||||
|
s = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
bc = cc
|
||||||
|
}
|
||||||
|
s++
|
||||||
|
}
|
||||||
|
if (s > 0) {
|
||||||
|
if (s > 1) {
|
||||||
|
for (p in s downTo 1) {
|
||||||
|
grid[rc + dc * p, rd] = grid[rc + dc * (p - 1), rd]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rc += dc
|
||||||
|
rd += dr
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (grid[rc, rd + dr] != '.') {
|
||||||
|
if (grid[rc, rd + dr] != '#') {
|
||||||
|
if (canMove(grid, rc, rd + dr, dr)) {
|
||||||
|
doMove(grid, rc, rd + dr, dr)
|
||||||
|
rc += dc
|
||||||
|
rd += dr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rc += dc
|
||||||
|
rd += dr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
grid[rc, rd] = '@'
|
||||||
|
}
|
||||||
|
return grid.generateGridPos().filter { grid[it] == '[' }.sumOf { it.dc + it.dr * 100 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
val testInput2 = inlineTestInput2.trim().reader().readLines()
|
||||||
|
val testInput3 = inlineTestInput3.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day15_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput2)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 2028)
|
||||||
|
check(testInputPart2Result == 9021)
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day15")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import RelPos
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 16: Reindeer Maze ---
|
||||||
|
https://adventofcode.com/2024/day/16
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
###############
|
||||||
|
#.......#....E#
|
||||||
|
#.#.###.#.###.#
|
||||||
|
#.....#.#...#.#
|
||||||
|
#.###.#####.#.#
|
||||||
|
#.#.#.......#.#
|
||||||
|
#.#.#####.###.#
|
||||||
|
#...........#.#
|
||||||
|
###.#.#####.#.#
|
||||||
|
#...#.....#.#.#
|
||||||
|
#.#.#.###.#.#.#
|
||||||
|
#.....#...#.#.#
|
||||||
|
#.###.#.#.#.#.#
|
||||||
|
#S..#.....#...#
|
||||||
|
###############
|
||||||
|
"""
|
||||||
|
val inlineTestInput2 = """
|
||||||
|
#################
|
||||||
|
#...#...#...#..E#
|
||||||
|
#.#.#.#.#.#.#.#.#
|
||||||
|
#.#.#.#...#...#.#
|
||||||
|
#.#.#.#.###.#.#.#
|
||||||
|
#...#.#.#.....#.#
|
||||||
|
#.#.#.#.#.#####.#
|
||||||
|
#.#...#.#.#.....#
|
||||||
|
#.#.#####.#.###.#
|
||||||
|
#.#.#.......#...#
|
||||||
|
#.#.###.#####.###
|
||||||
|
#.#.#...#.....#.#
|
||||||
|
#.#.#.#####.###.#
|
||||||
|
#.#.#.........#.#
|
||||||
|
#.#.#.#########.#
|
||||||
|
#S#.............#
|
||||||
|
#################
|
||||||
|
"""
|
||||||
|
|
||||||
|
data class Node(val pos: RelPos, val dir: RelPos, val relLen: Int, val path: LinkedHashSet<Pair<RelPos, RelPos>>)
|
||||||
|
|
||||||
|
val costMap = hashMapOf(
|
||||||
|
RelPos(0, -1) to listOf(RelPos(0, -1) to 1, RelPos(1, 0) to 1001, RelPos(-1, 0) to 1001, RelPos(0, 1) to 2001),
|
||||||
|
RelPos(1, 0) to listOf(RelPos(1, 0) to 1, RelPos(0, 1) to 1001, RelPos(0, -1) to 1001, RelPos(-1, 0) to 2001),
|
||||||
|
RelPos(0, 1) to listOf(RelPos(0, 1) to 1, RelPos(-1, 0) to 1001, RelPos(1, 0) to 1001, RelPos(0, -1) to 2001),
|
||||||
|
RelPos(-1, 0) to listOf(RelPos(-1, 0) to 1, RelPos(0, 1) to 1001, RelPos(0, -1) to 1001, RelPos(1, 0) to 2001)
|
||||||
|
)
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val grid = CharGrid(input, '#')
|
||||||
|
val queue = ArrayDeque<Node>()
|
||||||
|
val (startc, startr) = grid.findMatches { it == 'S' }[0]
|
||||||
|
val (endc, endr) = grid.findMatches { it == 'E' }[0]
|
||||||
|
val endpos = RelPos(endc, endr)
|
||||||
|
|
||||||
|
grid[startc, startr] = '.'
|
||||||
|
grid[endpos] = '.'
|
||||||
|
queue.add(Node(RelPos(startc, startr), RelPos(1, 0), 0, LinkedHashSet()))
|
||||||
|
val bestCosts = HashMap<Pair<RelPos, RelPos>, Int>()
|
||||||
|
var minCost = Int.MAX_VALUE
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
val node = queue.removeFirst()
|
||||||
|
val pos = node.pos
|
||||||
|
val dir = node.dir
|
||||||
|
val cost = node.relLen
|
||||||
|
val path = node.path
|
||||||
|
path.add(pos to dir)
|
||||||
|
if (pos == endpos) {
|
||||||
|
minCost = minCost.coerceAtMost(cost)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ((bestCosts[pos to dir] ?: Int.MAX_VALUE) <= cost) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bestCosts[pos to dir] = cost
|
||||||
|
val nextDirs =
|
||||||
|
costMap[dir]!!.filter { grid[pos.translate(it.first)] == '.' && cost + it.second < minCost && !path.contains(pos.translate(it.first) to it.first) }
|
||||||
|
.map { Node(pos.translate(it.first), it.first, cost + it.second, LinkedHashSet(path)) }
|
||||||
|
|
||||||
|
queue.addAll(nextDirs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return minCost
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
val grid = CharGrid(input, '#')
|
||||||
|
val queue = ArrayDeque<Node>()
|
||||||
|
val (startc, startr) = grid.findMatches { it == 'S' }[0]
|
||||||
|
val (endc, endr) = grid.findMatches { it == 'E' }[0]
|
||||||
|
val endpos = RelPos(endc, endr)
|
||||||
|
|
||||||
|
grid[startc, startr] = '.'
|
||||||
|
grid[endpos] = '.'
|
||||||
|
queue.add(Node(RelPos(startc, startr), RelPos(1, 0), 0, LinkedHashSet()))
|
||||||
|
val bestCosts = HashMap<Pair<RelPos, RelPos>, Int>()
|
||||||
|
var minCost = Int.MAX_VALUE
|
||||||
|
val bestNodes = HashSet<RelPos>()
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
val node = queue.removeFirst()
|
||||||
|
val pos = node.pos
|
||||||
|
val dir = node.dir
|
||||||
|
val cost = node.relLen
|
||||||
|
val path = node.path
|
||||||
|
path.add(pos to dir)
|
||||||
|
if (pos == endpos) {
|
||||||
|
if (cost < minCost) {
|
||||||
|
bestNodes.clear()
|
||||||
|
bestNodes.addAll(path.map { it.first })
|
||||||
|
}
|
||||||
|
if (minCost == cost) {
|
||||||
|
bestNodes.addAll(path.map { it.first })
|
||||||
|
}
|
||||||
|
minCost = minCost.coerceAtMost(cost)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ((bestCosts[pos to dir] ?: Int.MAX_VALUE) < cost) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bestCosts[pos to dir] = cost
|
||||||
|
val nextDirs =
|
||||||
|
costMap[dir]!!.filter { grid[pos.translate(it.first)] == '.' && cost + it.second < minCost && !path.contains(pos.translate(it.first) to it.first) }
|
||||||
|
.map { Node(pos.translate(it.first), it.first, cost + it.second, LinkedHashSet(path)) }
|
||||||
|
|
||||||
|
queue.addAll(nextDirs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestNodes.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
val testInput2 = inlineTestInput2.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day16_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart1Result2 = part1(testInput2)
|
||||||
|
println("Part 1 Test 2: $testInputPart1Result2")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
val testInputPart2Result2 = part2(testInput2)
|
||||||
|
println("Part 2 Test2: $testInputPart2Result2")
|
||||||
|
check(testInputPart1Result == 7036)
|
||||||
|
check(testInputPart1Result2 == 11048)
|
||||||
|
check(testInputPart2Result == 45)
|
||||||
|
check(testInputPart2Result2 == 64)
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day16")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import splitInts
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 17: Chronospatial Computer ---
|
||||||
|
https://adventofcode.com/2024/day/17
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
Register A: 117440
|
||||||
|
Register B: 0
|
||||||
|
Register C: 0
|
||||||
|
|
||||||
|
Program: 0,3,5,4,3,0
|
||||||
|
"""
|
||||||
|
val regs = LongArray(3)
|
||||||
|
|
||||||
|
fun getComboOperand(op: Int): Long =
|
||||||
|
when (op) {
|
||||||
|
0, 1, 2, 3 -> op.toLong()
|
||||||
|
4, 5, 6 -> regs[op - 4]
|
||||||
|
else -> throw IllegalStateException()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part1(input: List<String>): String {
|
||||||
|
regs[0] = input[0].split(" ")[2].toLong()
|
||||||
|
regs[1] = input[1].split(" ")[2].toLong()
|
||||||
|
regs[2] = input[2].split(" ")[2].toLong()
|
||||||
|
val ops = input[4].split(" ")[1].splitInts(",")
|
||||||
|
val outs = ArrayList<Int>()
|
||||||
|
var pc = 0
|
||||||
|
while (pc < ops.size) {
|
||||||
|
when (ops[pc++]) {
|
||||||
|
// adv
|
||||||
|
0 -> regs[0] = regs[0] shr getComboOperand(ops[pc++]).toInt()
|
||||||
|
// bdv
|
||||||
|
6 -> regs[1] = regs[0] shr getComboOperand(ops[pc++]).toInt()
|
||||||
|
// cdv
|
||||||
|
7 -> regs[2] = regs[0] shr getComboOperand(ops[pc++]).toInt()
|
||||||
|
// bxl
|
||||||
|
1 -> regs[1] = regs[1] xor ops[pc++].toLong()
|
||||||
|
// bst
|
||||||
|
2 -> regs[1] = getComboOperand(ops[pc++]) and 7
|
||||||
|
// jnz
|
||||||
|
3 -> if (regs[0] == 0L) pc++ else pc = ops[pc]
|
||||||
|
// bxc
|
||||||
|
4 -> {
|
||||||
|
regs[1] = regs[1] xor regs[2]
|
||||||
|
pc++
|
||||||
|
}
|
||||||
|
// out
|
||||||
|
5 -> outs.add((getComboOperand(ops[pc++]) and 7).toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return outs.joinToString(",")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verify(rega: Long, ops: List<Int>, outs: List<Int>): Boolean {
|
||||||
|
regs[0] = rega
|
||||||
|
var pc = 0
|
||||||
|
var outpos = 0
|
||||||
|
while (pc < ops.size) {
|
||||||
|
when (ops[pc++]) {
|
||||||
|
// adv
|
||||||
|
0 -> regs[0] = regs[0] shr getComboOperand(ops[pc++]).toInt()
|
||||||
|
// bdv
|
||||||
|
6 -> regs[1] = regs[0] shr getComboOperand(ops[pc++]).toInt()
|
||||||
|
// cdv
|
||||||
|
7 -> regs[2] = regs[0] shr getComboOperand(ops[pc++]).toInt()
|
||||||
|
// bxl
|
||||||
|
1 -> regs[1] = regs[1] xor ops[pc++].toLong()
|
||||||
|
// bst
|
||||||
|
2 -> regs[1] = getComboOperand(ops[pc++]) and 7
|
||||||
|
// jnz
|
||||||
|
3 -> if (regs[0] == 0L) pc++ else pc = ops[pc]
|
||||||
|
// bxc
|
||||||
|
4 -> {
|
||||||
|
regs[1] = regs[1] xor regs[2]
|
||||||
|
pc++
|
||||||
|
}
|
||||||
|
// out
|
||||||
|
5 -> {
|
||||||
|
val o = (getComboOperand(ops[pc++]) and 7).toInt()
|
||||||
|
if (outs[outpos++] != o) return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rec(a: Long, p: Int, ops: List<Int>, outs: List<Int>): Long {
|
||||||
|
val partOutput = outs.take(p).reversed()
|
||||||
|
// assuming that all programs use 3 bit shifts in the last operation
|
||||||
|
for (i in 0L..7L) {
|
||||||
|
if (verify((a shl 3) or i, ops, partOutput)) {
|
||||||
|
if (p == outs.size) return (a shl 3) or i
|
||||||
|
val res = rec((a shl 3) or i, p + 1, ops, outs)
|
||||||
|
if (res != 0L) return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
val ops = input[4].split(" ")[1].splitInts(",")
|
||||||
|
val outs = ops.reversed()
|
||||||
|
return rec(0L, 1, ops, outs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day17_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day17")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import RelPos
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import splitInts
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 18: RAM Run ---
|
||||||
|
https://adventofcode.com/2024/day/18
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
5,4
|
||||||
|
4,2
|
||||||
|
4,5
|
||||||
|
3,0
|
||||||
|
2,1
|
||||||
|
6,3
|
||||||
|
2,4
|
||||||
|
1,5
|
||||||
|
0,6
|
||||||
|
3,3
|
||||||
|
2,6
|
||||||
|
5,1
|
||||||
|
1,2
|
||||||
|
5,5
|
||||||
|
2,5
|
||||||
|
6,5
|
||||||
|
1,4
|
||||||
|
0,4
|
||||||
|
6,4
|
||||||
|
1,1
|
||||||
|
6,1
|
||||||
|
1,0
|
||||||
|
0,5
|
||||||
|
1,6
|
||||||
|
2,0
|
||||||
|
"""
|
||||||
|
|
||||||
|
data class Node(val pos: RelPos, val relLen: Int)
|
||||||
|
|
||||||
|
fun part1(input: List<String>, steps: Int, wh: Int): Int {
|
||||||
|
val posList = input.map { it.splitInts(",").toIntArray() }
|
||||||
|
val grid = CharGrid(wh, wh, '#', fillChar = '.')
|
||||||
|
posList.take(steps).forEach { grid[it[0], it[1]] = '#' }
|
||||||
|
val queue = PriorityQueue<Node>(Comparator.comparing { -it.pos.dc - it.pos.dr })
|
||||||
|
queue.add(Node(RelPos(0, 0), 0))
|
||||||
|
val bestCosts = HashMap<RelPos, Int>()
|
||||||
|
var minCost = Int.MAX_VALUE
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
val node = queue.remove()
|
||||||
|
val pos = node.pos
|
||||||
|
val cost = node.relLen
|
||||||
|
if (pos.dc == wh - 1 && pos.dr == wh - 1) {
|
||||||
|
minCost = minCost.coerceAtMost(cost)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ((bestCosts[pos] ?: Int.MAX_VALUE) < cost) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bestCosts[pos] = cost
|
||||||
|
queue.addAll(CharGrid.PLUS_POS
|
||||||
|
.map { pos.translate(it) }
|
||||||
|
.filter { grid[it] == '.' && (bestCosts[it] ?: Int.MAX_VALUE) > cost + 1 }
|
||||||
|
.map { Node(it, cost + 1) })
|
||||||
|
}
|
||||||
|
return minCost
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>, wh: Int): RelPos {
|
||||||
|
val posList = input.map { it.splitInts(",").toIntArray() }
|
||||||
|
var lowerBound = 0
|
||||||
|
var higherBound = posList.size
|
||||||
|
do {
|
||||||
|
val grid = CharGrid(wh, wh, '#', fillChar = '.')
|
||||||
|
val boulderPos = (lowerBound + higherBound) / 2
|
||||||
|
posList.take(boulderPos).forEach { grid[it[0], it[1]] = '#' }
|
||||||
|
|
||||||
|
var foundPath = false
|
||||||
|
val queue = PriorityQueue<Node>(Comparator.comparing { -it.pos.dc - it.pos.dr })
|
||||||
|
queue.add(Node(RelPos(0, 0), 0))
|
||||||
|
val bestCosts = HashMap<RelPos, Int>()
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
val node = queue.remove()
|
||||||
|
val pos = node.pos
|
||||||
|
val cost = node.relLen
|
||||||
|
if (pos.dc == wh - 1 && pos.dr == wh - 1) {
|
||||||
|
foundPath = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if ((bestCosts[pos] ?: Int.MAX_VALUE) < cost) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
bestCosts[pos] = cost
|
||||||
|
queue.addAll(CharGrid.PLUS_POS
|
||||||
|
.map { pos.translate(it) }
|
||||||
|
.filter { grid[it] == '.' && (bestCosts[it] ?: Int.MAX_VALUE) > cost + 1 }
|
||||||
|
.map { Node(it, cost + 1) })
|
||||||
|
}
|
||||||
|
if (foundPath) {
|
||||||
|
lowerBound = boulderPos + 1
|
||||||
|
} else {
|
||||||
|
higherBound = boulderPos
|
||||||
|
}
|
||||||
|
} while (lowerBound < higherBound)
|
||||||
|
println("$lowerBound $higherBound")
|
||||||
|
return RelPos(posList[lowerBound - 1][0], posList[lowerBound - 1][1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day18_test")
|
||||||
|
val testInputPart1Result = part1(testInput, 12, 7)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput, 7)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 22)
|
||||||
|
check(testInputPart2Result == RelPos(6, 1))
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day18")
|
||||||
|
part1(input, 1024, 71).println()
|
||||||
|
part2(input, 71).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 19: Linen Layout ---
|
||||||
|
https://adventofcode.com/2024/day/19
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
r, wr, b, g, bwu, rb, gb, br
|
||||||
|
|
||||||
|
brwrr
|
||||||
|
bggr
|
||||||
|
gbbr
|
||||||
|
rrbgbr
|
||||||
|
ubwu
|
||||||
|
bwurrg
|
||||||
|
brgr
|
||||||
|
bbrgwb
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun rec(inp: String, towels: Array<String>): Boolean =
|
||||||
|
inp.isEmpty() || towels.any { inp.startsWith(it) && rec(inp.substring(it.length), towels) }
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val towels = input[0].split(", ").toTypedArray()
|
||||||
|
return input.drop(2).count { rec(it, towels) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val cache = HashMap<String, Long>()
|
||||||
|
|
||||||
|
fun rec2(inp: String, towels: Array<String>): Long =
|
||||||
|
if (inp.isEmpty()) 1 else cache.getOrPut(inp) {
|
||||||
|
towels.filter { inp.startsWith(it) }.sumOf { rec2(inp.substring(it.length), towels) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
val towels = input[0].split(", ").toTypedArray()
|
||||||
|
return input.drop(2).sumOf { rec2(it, towels) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day19_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 6)
|
||||||
|
check(testInputPart2Result == 16L)
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day19")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import RelPos
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 20: Race Condition ---
|
||||||
|
https://adventofcode.com/2024/day/20
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
###############
|
||||||
|
#...#...#.....#
|
||||||
|
#.#.#.#.#.###.#
|
||||||
|
#S#...#.#.#...#
|
||||||
|
#######.#.#.###
|
||||||
|
#######.#.#...#
|
||||||
|
#######.#.###.#
|
||||||
|
###..E#...#...#
|
||||||
|
###.#######.###
|
||||||
|
#...###...#...#
|
||||||
|
#.#####.#.###.#
|
||||||
|
#.#...#.#.#...#
|
||||||
|
#.#.#.#.#.#.###
|
||||||
|
#...#...#...###
|
||||||
|
###############
|
||||||
|
"""
|
||||||
|
|
||||||
|
data class Node(val pos: RelPos, val relLen: Int)
|
||||||
|
|
||||||
|
fun getPathCosts(grid: CharGrid, startPos: RelPos, endPos: RelPos): HashMap<RelPos, Int> {
|
||||||
|
val queue = ArrayDeque<Node>()
|
||||||
|
queue.add(Node(startPos, 0))
|
||||||
|
val bestCosts = HashMap<RelPos, Int>()
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
val node = queue.removeFirst()
|
||||||
|
val pos = node.pos
|
||||||
|
val cost = node.relLen
|
||||||
|
bestCosts[pos] = cost
|
||||||
|
if (pos == endPos) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
queue.addAll(CharGrid.PLUS_POS
|
||||||
|
.map { pos.translate(it) }
|
||||||
|
.filter { grid[it] == '.' && !bestCosts.contains(it) }
|
||||||
|
.map { Node(it, cost + 1) })
|
||||||
|
}
|
||||||
|
return bestCosts
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val grid = CharGrid(input)
|
||||||
|
val start = grid.findMatchesRelPos { it == 'S' }[0]
|
||||||
|
val end = grid.findMatchesRelPos { it == 'E' }[0]
|
||||||
|
grid[end] = '.'
|
||||||
|
|
||||||
|
val cheats = listOf(
|
||||||
|
RelPos(0, -1), RelPos(-1, 0), RelPos(1, 0), RelPos(0, 1),
|
||||||
|
RelPos(0, -2), RelPos(-2, 0), RelPos(2, 0), RelPos(0, 2),
|
||||||
|
)
|
||||||
|
val pathCosts = getPathCosts(grid, start, end)
|
||||||
|
|
||||||
|
return pathCosts.asSequence().sumOf { (pos, cost) ->
|
||||||
|
cheats.map { pos.translate(it) to (abs(it.dr) + abs(it.dc)) }
|
||||||
|
.filter { (pathCosts[it.first] ?: 0) - (cost + it.second) >= 100 }
|
||||||
|
.map { it.first to (pathCosts[it.first]!! - (cost + it.second)) }.count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
val grid = CharGrid(input)
|
||||||
|
val start = grid.findMatchesRelPos { it == 'S' }[0]
|
||||||
|
val end = grid.findMatchesRelPos { it == 'E' }[0]
|
||||||
|
grid[end] = '.'
|
||||||
|
|
||||||
|
val pathCosts = getPathCosts(grid, start, end)
|
||||||
|
|
||||||
|
// manhattan distance!
|
||||||
|
val pathCostsList = pathCosts.toList()
|
||||||
|
return pathCostsList.sumOf { (pos, cost) ->
|
||||||
|
pathCostsList.asSequence().filter { abs(pos.dc - it.first.dc) + abs(pos.dr - it.first.dr) <= 20 }
|
||||||
|
.filter { (pathCosts[it.first] ?: 0) - (cost + abs(pos.dc - it.first.dc) + abs(pos.dr - it.first.dr)) >= 100 }
|
||||||
|
.map { it.first to (pathCosts[it.first]!! - (cost + it.second)) }.count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day20_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 0)
|
||||||
|
check(testInputPart2Result == 0)
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day20")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import RelPos
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 21: Keypad Conundrum ---
|
||||||
|
https://adventofcode.com/2024/day/21
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
029A
|
||||||
|
980A
|
||||||
|
179A
|
||||||
|
456A
|
||||||
|
379A
|
||||||
|
"""
|
||||||
|
|
||||||
|
val doorPadMap = hashMapOf(
|
||||||
|
'7' to RelPos(-2, -3),
|
||||||
|
'8' to RelPos(-1, -3),
|
||||||
|
'9' to RelPos(0, -3),
|
||||||
|
'4' to RelPos(-2, -2),
|
||||||
|
'5' to RelPos(-1, -2),
|
||||||
|
'6' to RelPos(0, -2),
|
||||||
|
'1' to RelPos(-2, -1),
|
||||||
|
'2' to RelPos(-1, -1),
|
||||||
|
'3' to RelPos(0, -1),
|
||||||
|
'0' to RelPos(-1, 0),
|
||||||
|
'A' to RelPos(0, 0),
|
||||||
|
)
|
||||||
|
|
||||||
|
val robotPadMap = hashMapOf(
|
||||||
|
'^' to RelPos(-1, 0),
|
||||||
|
'A' to RelPos(0, 0),
|
||||||
|
'<' to RelPos(-2, 1),
|
||||||
|
'v' to RelPos(-1, 1),
|
||||||
|
'>' to RelPos(0, 1),
|
||||||
|
)
|
||||||
|
|
||||||
|
data class PadState(
|
||||||
|
var pos: RelPos,
|
||||||
|
val padMap: Map<Char, RelPos>,
|
||||||
|
val child: PadState? = null,
|
||||||
|
val cache: MutableMap<Pair<RelPos, RelPos>, Long> = HashMap()
|
||||||
|
) {
|
||||||
|
fun getMovement(c: Char): Long {
|
||||||
|
val targetPos = padMap[c]!!
|
||||||
|
return if (child == null) {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
val movement = cache.getOrPut(pos to targetPos) {
|
||||||
|
val child = child
|
||||||
|
var sum = 0L
|
||||||
|
if (pos.dr == 0 && targetPos.dc == -2) {
|
||||||
|
while (targetPos.dr < pos.dr) {
|
||||||
|
sum += child.getMovement('^')
|
||||||
|
pos = pos.translate(0, -1)
|
||||||
|
}
|
||||||
|
while (targetPos.dr > pos.dr) {
|
||||||
|
sum += child.getMovement('v')
|
||||||
|
pos = pos.translate(0, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (pos.dc == -2 && targetPos.dr == 0) {
|
||||||
|
while (targetPos.dc > pos.dc) {
|
||||||
|
sum += child.getMovement('>')
|
||||||
|
pos = pos.translate(1, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (targetPos.dc < pos.dc) {
|
||||||
|
sum += child.getMovement('<')
|
||||||
|
pos = pos.translate(-1, 0)
|
||||||
|
}
|
||||||
|
while (targetPos.dr > pos.dr) {
|
||||||
|
sum += child.getMovement('v')
|
||||||
|
pos = pos.translate(0, 1)
|
||||||
|
}
|
||||||
|
while (targetPos.dr < pos.dr) {
|
||||||
|
sum += child.getMovement('^')
|
||||||
|
pos = pos.translate(0, -1)
|
||||||
|
}
|
||||||
|
while (targetPos.dc > pos.dc) {
|
||||||
|
sum += child.getMovement('>')
|
||||||
|
pos = pos.translate(1, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
sum += child.getMovement('A')
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = targetPos
|
||||||
|
movement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Long {
|
||||||
|
val home = RelPos(0, 0)
|
||||||
|
val myPad = PadState(home, robotPadMap)
|
||||||
|
val robotPad2 = PadState(home, robotPadMap, myPad)
|
||||||
|
val robotPad1 = PadState(home, robotPadMap, robotPad2)
|
||||||
|
val doorPad = PadState(home, doorPadMap, robotPad1)
|
||||||
|
var sum = 0L
|
||||||
|
for (seq in input) {
|
||||||
|
val seqVal = seq.take(3).toLong()
|
||||||
|
sum += seq.sumOf { doorPad.getMovement(it) } * seqVal
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
val home = RelPos(0, 0)
|
||||||
|
val myPad = PadState(home, robotPadMap)
|
||||||
|
var lastPad = myPad
|
||||||
|
Array(25) { PadState(home, robotPadMap, lastPad).also { lastPad = it } }
|
||||||
|
val doorPad = PadState(home, doorPadMap, lastPad)
|
||||||
|
var sum = 0L
|
||||||
|
for (seq in input) {
|
||||||
|
val seqVal = seq.take(3).toLong()
|
||||||
|
sum += seq.sumOf { doorPad.getMovement(it) } * seqVal
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day21_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 126384L)
|
||||||
|
//check(testInputPart2Result == 0)
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day21")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 22: Monkey Market ---
|
||||||
|
https://adventofcode.com/2024/day/22
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
1
|
||||||
|
10
|
||||||
|
100
|
||||||
|
2024
|
||||||
|
"""
|
||||||
|
|
||||||
|
val inlineTestInput2 = """
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
2024
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun priceSequence(seed: Int) = generateSequence(seed) {
|
||||||
|
val r1 = (it xor (it shl 6)) and 0xff_ff_ff
|
||||||
|
val r2 = r1 xor (r1 shr 5)
|
||||||
|
(r2 xor (r2 shl 11)) and 0xff_ff_ff
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part1(input: List<String>) =
|
||||||
|
input.map(String::toInt).sumOf { priceSequence(it).elementAt(2000).toLong() }
|
||||||
|
|
||||||
|
fun part2bruteforce(input: List<String>): Int {
|
||||||
|
val strings = input.map(String::toInt).map {
|
||||||
|
val normalString = StringBuffer(2024)
|
||||||
|
val deltaString = StringBuffer(2024)
|
||||||
|
var lastd = it % 10
|
||||||
|
priceSequence(it).take(2001).forEach {
|
||||||
|
val d = it % 10
|
||||||
|
val delta = d - lastd
|
||||||
|
normalString.append(d)
|
||||||
|
deltaString.append('j' + delta)
|
||||||
|
lastd = d
|
||||||
|
}
|
||||||
|
deltaString.toString() to normalString.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's not my fault that brute force is still working
|
||||||
|
// note that there are less than 19^4 valid combinations, but we don't care
|
||||||
|
return runBlocking {
|
||||||
|
(0..(19 * 19 * 19 * 19)).map {
|
||||||
|
async(Dispatchers.Default) {
|
||||||
|
val token = StringBuffer(4)
|
||||||
|
.append('a' + (it / (19 * 19 * 19)))
|
||||||
|
.append('a' + ((it / (19 * 19)) % 19))
|
||||||
|
.append('a' + ((it / 19) % 19))
|
||||||
|
.append('a' + ((it % 19))).toString()
|
||||||
|
strings.sumOf {
|
||||||
|
val idx = it.first.indexOf(token)
|
||||||
|
if (idx >= 0) it.second[idx + 3].digitToInt() else 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.awaitAll().max()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
val map = HashMap<Int, MutableList<Int>>()
|
||||||
|
input.map(String::toInt).forEach {
|
||||||
|
var lastd = 0
|
||||||
|
var code = 0
|
||||||
|
var countDown = 4
|
||||||
|
val seenArray = BitSet(19 * 19 * 19 * 19)
|
||||||
|
for (v in priceSequence(it).take(2001)) {
|
||||||
|
val d = v % 10
|
||||||
|
val delta = d - lastd
|
||||||
|
code = ((code * 19) % (19 * 19 * 19 * 19)) + (delta + 9)
|
||||||
|
if (--countDown < 0 && !seenArray[code]) {
|
||||||
|
map.getOrPut(code) { ArrayList() }.add(d)
|
||||||
|
seenArray[code] = true
|
||||||
|
}
|
||||||
|
lastd = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map.maxOf { it.value.sum() }
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
val testInput2 = inlineTestInput2.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day22_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput2)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 37327623L)
|
||||||
|
check(testInputPart2Result == 23)
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day22")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 23: LAN Party ---
|
||||||
|
https://adventofcode.com/2024/day/23
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
kh-tc
|
||||||
|
qp-kh
|
||||||
|
de-cg
|
||||||
|
ka-co
|
||||||
|
yn-aq
|
||||||
|
qp-ub
|
||||||
|
cg-tb
|
||||||
|
vc-aq
|
||||||
|
tb-ka
|
||||||
|
wh-tc
|
||||||
|
yn-cg
|
||||||
|
kh-ub
|
||||||
|
ta-co
|
||||||
|
de-co
|
||||||
|
tc-td
|
||||||
|
tb-wq
|
||||||
|
wh-td
|
||||||
|
ta-ka
|
||||||
|
td-qp
|
||||||
|
aq-cg
|
||||||
|
wq-ub
|
||||||
|
ub-vc
|
||||||
|
de-ta
|
||||||
|
wq-aq
|
||||||
|
wq-vc
|
||||||
|
wh-yn
|
||||||
|
ka-de
|
||||||
|
kh-ta
|
||||||
|
co-tc
|
||||||
|
wh-qp
|
||||||
|
tb-vc
|
||||||
|
td-yn
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val nodes = HashMap<String, MutableSet<String>>()
|
||||||
|
for (i in input) {
|
||||||
|
val (n1, n2) = i.split("-")
|
||||||
|
val e1 = nodes.getOrPut(n1) { HashSet() }
|
||||||
|
val e2 = nodes.getOrPut(n2) { HashSet() }
|
||||||
|
e1.add(n2)
|
||||||
|
e2.add(n1)
|
||||||
|
}
|
||||||
|
val threesomes = nodes
|
||||||
|
.flatMap {
|
||||||
|
it.value.flatMap { i1 ->
|
||||||
|
it.value.filter { it != i1 }.mapNotNull { i2 ->
|
||||||
|
if (nodes[i1]!!.contains(it.key) && nodes[i2]!!.contains(it.key) && nodes[i1]!!.contains(i2)) sortedSetOf(it.key, i1, i2) else null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.map { it.joinToString("-", prefix = "-") }.distinct()
|
||||||
|
|
||||||
|
return threesomes.count { it.contains("-t") }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bronKerbosch(nodes: Map<String, Set<String>>, r: Set<String>, p: Set<String>, x: MutableSet<String>): Set<String> {
|
||||||
|
if (p.isEmpty() && x.isEmpty()) {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
var maxClique = emptySet<String>()
|
||||||
|
val ptmp = p.toMutableSet()
|
||||||
|
for (v in p) {
|
||||||
|
val res = bronKerbosch(nodes, r.plus(v), ptmp.intersect(nodes[v]!!), x.intersect(nodes[v]!!).toMutableSet())
|
||||||
|
if (res.size > maxClique.size) {
|
||||||
|
maxClique = res
|
||||||
|
}
|
||||||
|
ptmp.remove(v)
|
||||||
|
x.add(v)
|
||||||
|
}
|
||||||
|
return maxClique
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): String {
|
||||||
|
val nodes = HashMap<String, MutableSet<String>>()
|
||||||
|
for (i in input) {
|
||||||
|
val (n1, n2) = i.split("-")
|
||||||
|
val e1 = nodes.getOrPut(n1) { HashSet() }
|
||||||
|
val e2 = nodes.getOrPut(n2) { HashSet() }
|
||||||
|
e1.add(n2)
|
||||||
|
e2.add(n1)
|
||||||
|
}
|
||||||
|
val largestSet = bronKerbosch(nodes, emptySet(), nodes.keys, mutableSetOf())
|
||||||
|
return largestSet.asSequence().sorted().joinToString(",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day23_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 7)
|
||||||
|
check(testInputPart2Result == "co,de,ka,ta")
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day23")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,345 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.collections.ArrayDeque
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 24: Crossed Wires ---
|
||||||
|
https://adventofcode.com/2024/day/24
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
x00: 1
|
||||||
|
x01: 1
|
||||||
|
x02: 1
|
||||||
|
y00: 0
|
||||||
|
y01: 1
|
||||||
|
y02: 0
|
||||||
|
|
||||||
|
x00 AND y00 -> z00
|
||||||
|
x01 XOR y01 -> z01
|
||||||
|
x02 OR y02 -> z02
|
||||||
|
"""
|
||||||
|
|
||||||
|
val inlineTestInput2 = """
|
||||||
|
x00: 1
|
||||||
|
x01: 0
|
||||||
|
x02: 1
|
||||||
|
x03: 1
|
||||||
|
x04: 0
|
||||||
|
y00: 1
|
||||||
|
y01: 1
|
||||||
|
y02: 1
|
||||||
|
y03: 1
|
||||||
|
y04: 1
|
||||||
|
|
||||||
|
ntg XOR fgs -> mjb
|
||||||
|
y02 OR x01 -> tnw
|
||||||
|
kwq OR kpj -> z05
|
||||||
|
x00 OR x03 -> fst
|
||||||
|
tgd XOR rvg -> z01
|
||||||
|
vdt OR tnw -> bfw
|
||||||
|
bfw AND frj -> z10
|
||||||
|
ffh OR nrd -> bqk
|
||||||
|
y00 AND y03 -> djm
|
||||||
|
y03 OR y00 -> psh
|
||||||
|
bqk OR frj -> z08
|
||||||
|
tnw OR fst -> frj
|
||||||
|
gnj AND tgd -> z11
|
||||||
|
bfw XOR mjb -> z00
|
||||||
|
x03 OR x00 -> vdt
|
||||||
|
gnj AND wpb -> z02
|
||||||
|
x04 AND y00 -> kjc
|
||||||
|
djm OR pbm -> qhw
|
||||||
|
nrd AND vdt -> hwm
|
||||||
|
kjc AND fst -> rvg
|
||||||
|
y04 OR y02 -> fgs
|
||||||
|
y01 AND x02 -> pbm
|
||||||
|
ntg OR kjc -> kwq
|
||||||
|
psh XOR fgs -> tgd
|
||||||
|
qhw XOR tgd -> z09
|
||||||
|
pbm OR djm -> kpj
|
||||||
|
x03 XOR y03 -> ffh
|
||||||
|
x00 XOR y04 -> ntg
|
||||||
|
bfw OR bqk -> z06
|
||||||
|
nrd XOR fgs -> wpb
|
||||||
|
frj XOR qhw -> z04
|
||||||
|
bqk OR frj -> z07
|
||||||
|
y03 OR x01 -> nrd
|
||||||
|
hwm AND bqk -> z03
|
||||||
|
tgd XOR rvg -> z12
|
||||||
|
tnw OR pbm -> gnj
|
||||||
|
"""
|
||||||
|
|
||||||
|
data class Node(val n1: String, val n2: String, val op: Int, var target: String, var val1: Boolean? = null, var val2: Boolean? = null) {
|
||||||
|
fun output() = when (op) {
|
||||||
|
0 -> val1!! || val2!!
|
||||||
|
1 -> val1!! && val2!!
|
||||||
|
2 -> val1!! != val2!!
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Node
|
||||||
|
|
||||||
|
return target == other.target
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return target.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val nodes = ArrayList<Node>()
|
||||||
|
val nodesWaiting = HashMap<String, MutableList<Node>>()
|
||||||
|
var satisfiedNodes = ArrayList<Node>()
|
||||||
|
val zTargets = TreeSet<String>()
|
||||||
|
val zState = HashMap<String, Boolean>()
|
||||||
|
val targetParent = HashMap<String, Node>()
|
||||||
|
val initialStates = HashMap<String, Boolean>()
|
||||||
|
val zInvolvedNodes = HashMap<String, MutableSet<Node>>()
|
||||||
|
val random = kotlin.random.Random(1337)
|
||||||
|
|
||||||
|
fun readCircuit(input: List<String>) {
|
||||||
|
nodes.clear()
|
||||||
|
nodesWaiting.clear()
|
||||||
|
satisfiedNodes.clear()
|
||||||
|
zTargets.clear()
|
||||||
|
zState.clear()
|
||||||
|
targetParent.clear()
|
||||||
|
initialStates.clear()
|
||||||
|
var i = 0
|
||||||
|
while (input[i++].isNotEmpty()) {
|
||||||
|
val (n, st) = input[i - 1].split(": ")
|
||||||
|
initialStates[n] = (st == "1")
|
||||||
|
}
|
||||||
|
while (i < input.size) {
|
||||||
|
val (n1, op, n2, _, target) = input[i++].split(" ")
|
||||||
|
val opCode = when (op) {
|
||||||
|
"OR" -> 0
|
||||||
|
"AND" -> 1
|
||||||
|
"XOR" -> 2
|
||||||
|
else -> throw IllegalStateException()
|
||||||
|
}
|
||||||
|
if (target.startsWith("z")) {
|
||||||
|
zTargets.add(target)
|
||||||
|
}
|
||||||
|
val node = Node(n1, n2, opCode, target)
|
||||||
|
nodes.add(node)
|
||||||
|
targetParent[target] = node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun outputState(target: String, output: Boolean, newSatisfiedNodes: MutableCollection<Node>) {
|
||||||
|
if (target.startsWith("z")) {
|
||||||
|
zState[target] = output
|
||||||
|
}
|
||||||
|
val targetList = nodesWaiting[target] ?: return
|
||||||
|
while (targetList.isNotEmpty()) {
|
||||||
|
val tNode = targetList.removeFirst()
|
||||||
|
if (tNode.n1 == target) {
|
||||||
|
tNode.val1 = output
|
||||||
|
if (tNode.val2 != null) {
|
||||||
|
newSatisfiedNodes.add(tNode)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tNode.val2 = output
|
||||||
|
if (tNode.val1 != null) {
|
||||||
|
newSatisfiedNodes.add(tNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun runCircuit(): Boolean {
|
||||||
|
while (zState.size != zTargets.size) {
|
||||||
|
if (satisfiedNodes.isEmpty()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
val newSatisfiedNodes = ArrayList<Node>()
|
||||||
|
for (node in satisfiedNodes) {
|
||||||
|
val output = node.output()
|
||||||
|
node.val1 = null
|
||||||
|
node.val2 = null
|
||||||
|
nodesWaiting.getOrPut(node.n1) { ArrayList() }.add(node)
|
||||||
|
nodesWaiting.getOrPut(node.n2) { ArrayList() }.add(node)
|
||||||
|
|
||||||
|
outputState(node.target, output, newSatisfiedNodes)
|
||||||
|
}
|
||||||
|
satisfiedNodes = newSatisfiedNodes
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
satisfiedNodes.clear()
|
||||||
|
nodesWaiting.clear()
|
||||||
|
zState.clear()
|
||||||
|
for (node in nodes) {
|
||||||
|
node.val1 = null
|
||||||
|
node.val2 = null
|
||||||
|
nodesWaiting.getOrPut(node.n1) { ArrayDeque() }.add(node)
|
||||||
|
nodesWaiting.getOrPut(node.n2) { ArrayDeque() }.add(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setXY(n: String, value: Long) {
|
||||||
|
var v = value
|
||||||
|
for (i in 0..zTargets.size - 2) {
|
||||||
|
outputState("%s%02d".format(n, i), (v and 1L) != 0L, satisfiedNodes)
|
||||||
|
v = v shr 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readZ(): Long {
|
||||||
|
var result = 0L
|
||||||
|
for (i in zTargets.reversed()) {
|
||||||
|
result *= 2
|
||||||
|
if (zState[i] == true) result++
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun calcInvolvedNodes() {
|
||||||
|
zInvolvedNodes.clear()
|
||||||
|
for (target in zTargets) {
|
||||||
|
val set = HashSet<Node>()
|
||||||
|
val queue = ArrayDeque<Node>()
|
||||||
|
queue.add(targetParent[target]!!)
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
val node = queue.removeFirst()
|
||||||
|
set.add(node)
|
||||||
|
val t1 = targetParent[node.n1]
|
||||||
|
val t2 = targetParent[node.n2]
|
||||||
|
if (t1 != null && !set.contains(t1)) queue.add(t1)
|
||||||
|
if (t2 != null && !set.contains(t2)) queue.add(t2)
|
||||||
|
}
|
||||||
|
zInvolvedNodes[target] = set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun swapOutputs(t1: String, t2: String) {
|
||||||
|
val n1 = targetParent[t1]!!
|
||||||
|
val n2 = targetParent[t2]!!
|
||||||
|
n1.target = t2
|
||||||
|
n2.target = t1
|
||||||
|
targetParent[t1] = n2
|
||||||
|
targetParent[t2] = n1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Long {
|
||||||
|
readCircuit(input)
|
||||||
|
reset()
|
||||||
|
initialStates.forEach { outputState(it.key, it.value, satisfiedNodes) }
|
||||||
|
runCircuit()
|
||||||
|
|
||||||
|
return readZ()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun checkIt(x: Long, y: Long, sum: Long, mask: Long = -1L): Boolean {
|
||||||
|
reset()
|
||||||
|
setXY("x", x)
|
||||||
|
setXY("y", y)
|
||||||
|
return runCircuit() && (readZ() and mask) == sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun countGoodBits(startbit: Int): Int {
|
||||||
|
val inv = (1L shl (zTargets.size - 1)) - 1
|
||||||
|
for (b in startbit..zTargets.size - 2) {
|
||||||
|
val tv = 1L shl b
|
||||||
|
val tvi = (1L shl (b + 1)) - 1L
|
||||||
|
if (!(checkIt(tv, 0L, tv) &&
|
||||||
|
checkIt(tvi, 0L, tvi) &&
|
||||||
|
checkIt(0L, tv, tv) &&
|
||||||
|
checkIt(0L, tvi, tvi) &&
|
||||||
|
checkIt(tv, tv, 2 * tv) &&
|
||||||
|
checkIt(tvi, tvi, 2 * tvi) &&
|
||||||
|
checkIt(inv - tvi, 0L, 0L, tvi) &&
|
||||||
|
checkIt(0L, inv - tvi, 0L, tvi)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
for (i in 0..(1L shl ((b - 8).coerceIn(1..6)))) {
|
||||||
|
val rx = random.nextLong() and tvi
|
||||||
|
val ry = random.nextLong() and tvi
|
||||||
|
if (!checkIt(rx, ry, rx + ry, mask = tvi * 2 + 1)) return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return zTargets.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): String {
|
||||||
|
readCircuit(input)
|
||||||
|
calcInvolvedNodes()
|
||||||
|
|
||||||
|
val goodGates = HashSet<Node>()
|
||||||
|
|
||||||
|
val swaps = ArrayList<Pair<String, String>>()
|
||||||
|
|
||||||
|
for (b in 0..zTargets.size - 3) {
|
||||||
|
val zb0 = zInvolvedNodes["z%02d".format(b)]!!
|
||||||
|
val zb1 = zInvolvedNodes["z%02d".format(b + 1)]!!
|
||||||
|
val zb2 = zInvolvedNodes["z%02d".format(b + 2)]!!
|
||||||
|
|
||||||
|
val tv = 1L shl b
|
||||||
|
val tvi = (1L shl (b + 1)) - 1L
|
||||||
|
val good = checkIt(tv, 0L, tv) &&
|
||||||
|
checkIt(tvi, 0L, tvi) &&
|
||||||
|
checkIt(0L, tv, tv) &&
|
||||||
|
checkIt(0L, tvi, tvi) &&
|
||||||
|
checkIt(tv, tv, 2 * tv) &&
|
||||||
|
checkIt(tvi, tvi, 2 * tvi)
|
||||||
|
if (good) {
|
||||||
|
goodGates.addAll(zb0)
|
||||||
|
//goodGates.addAll(zb1)
|
||||||
|
} else {
|
||||||
|
println("Bit error at $b")
|
||||||
|
val badList = zb0.union(zb1).union(zb2).minus(goodGates).map { it.target }.toList()
|
||||||
|
var swapped = false
|
||||||
|
out@ for (p1 in badList.indices) {
|
||||||
|
for (p2 in p1 + 1..badList.lastIndex) {
|
||||||
|
swapOutputs(badList[p1], badList[p2])
|
||||||
|
val goodBits = countGoodBits(b)
|
||||||
|
if (goodBits > b + 1) {
|
||||||
|
println("**** Successfully $goodBits swapped ${badList[p1]} with ${badList[p2]}")
|
||||||
|
swapped = true
|
||||||
|
swaps.add(badList[p1] to badList[p2])
|
||||||
|
break@out
|
||||||
|
} else {
|
||||||
|
swapOutputs(badList[p1], badList[p2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!swapped) throw IllegalStateException("Sob!")
|
||||||
|
calcInvolvedNodes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return swaps.flatMap { listOf(it.first, it.second) }.sorted().joinToString(",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
val testInput2 = inlineTestInput2.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day24_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart1Result2 = part1(testInput2)
|
||||||
|
println("Part 1 Test 2: $testInputPart1Result2")
|
||||||
|
// val testInputPart2Result = part2(testInput2)
|
||||||
|
// println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 4L)
|
||||||
|
check(testInputPart1Result2 == 2024L)
|
||||||
|
//check(testInputPart2Result == "z00,z01,z02,z05")
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day24")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package aoc2024
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 25: Code Chronicle ---
|
||||||
|
https://adventofcode.com/2024/day/25
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
#####
|
||||||
|
.####
|
||||||
|
.####
|
||||||
|
.####
|
||||||
|
.#.#.
|
||||||
|
.#...
|
||||||
|
.....
|
||||||
|
|
||||||
|
#####
|
||||||
|
##.##
|
||||||
|
.#.##
|
||||||
|
...##
|
||||||
|
...#.
|
||||||
|
...#.
|
||||||
|
.....
|
||||||
|
|
||||||
|
.....
|
||||||
|
#....
|
||||||
|
#....
|
||||||
|
#...#
|
||||||
|
#.#.#
|
||||||
|
#.###
|
||||||
|
#####
|
||||||
|
|
||||||
|
.....
|
||||||
|
.....
|
||||||
|
#.#..
|
||||||
|
###..
|
||||||
|
###.#
|
||||||
|
###.#
|
||||||
|
#####
|
||||||
|
|
||||||
|
.....
|
||||||
|
.....
|
||||||
|
.....
|
||||||
|
#....
|
||||||
|
#.#..
|
||||||
|
#.#.#
|
||||||
|
#####
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
var p = 0
|
||||||
|
val topRow = IntRange(0, 4)
|
||||||
|
val locks = ArrayList<IntArray>()
|
||||||
|
val keys = ArrayList<IntArray>()
|
||||||
|
while (p < input.size) {
|
||||||
|
val grid = CharGrid(input.subList(p, p + 7))
|
||||||
|
if (topRow.all { grid[it, 0] == '#' }) {
|
||||||
|
val lock = topRow.map { rc -> (topRow.find { grid[rc, it + 1] != '#' } ?: 5) }.toIntArray()
|
||||||
|
locks.add(lock)
|
||||||
|
} else {
|
||||||
|
val key = topRow.map { rc -> 5 - (topRow.find { grid[rc, 5 - it] != '#' } ?: 5) }.toIntArray()
|
||||||
|
keys.add(key)
|
||||||
|
}
|
||||||
|
p += 8
|
||||||
|
}
|
||||||
|
return locks.sumOf { lock -> keys.count { key -> key.zip(lock).all { it.first >= it.second } } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2024/Day25_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 3)
|
||||||
|
check(testInputPart2Result == 0)
|
||||||
|
|
||||||
|
val input = readInput("aoc2024/Day25")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 1: Secret Entrance ---
|
||||||
|
https://adventofcode.com/2025/day/1
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
L68
|
||||||
|
L30
|
||||||
|
R48
|
||||||
|
L5
|
||||||
|
R60
|
||||||
|
L55
|
||||||
|
L1
|
||||||
|
L99
|
||||||
|
R14
|
||||||
|
L82
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
var dialPos = 50
|
||||||
|
var pass = 0
|
||||||
|
for (i in input) {
|
||||||
|
dialPos = if (i.startsWith("L")) {
|
||||||
|
dialPos + 100 - i.substring(1).toInt()
|
||||||
|
} else {
|
||||||
|
dialPos + i.substring(1).toInt()
|
||||||
|
}
|
||||||
|
dialPos = dialPos % 100
|
||||||
|
if (dialPos == 0) pass++
|
||||||
|
}
|
||||||
|
return pass
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
var dialPos = 50
|
||||||
|
var pass = 0
|
||||||
|
for (i in input) {
|
||||||
|
val rot = i.substring(1).toInt()
|
||||||
|
if (rot == 0) continue
|
||||||
|
if (i.startsWith("L")) {
|
||||||
|
if (dialPos == 0) pass--
|
||||||
|
dialPos -= rot
|
||||||
|
pass += (100 - dialPos) / 100
|
||||||
|
} else {
|
||||||
|
dialPos += rot
|
||||||
|
pass += dialPos / 100
|
||||||
|
}
|
||||||
|
dialPos = if (dialPos < 0) (100 - (-dialPos % 100)) % 100 else dialPos % 100
|
||||||
|
}
|
||||||
|
return pass
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day01_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 3)
|
||||||
|
check(testInputPart2Result == 6)
|
||||||
|
|
||||||
|
val input = readInput("aoc2025/Day01")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import kotlin.math.log10
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 2: Gift Shop ---
|
||||||
|
https://adventofcode.com/2025/day/2
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Long {
|
||||||
|
val ranges = input[0].split(",").map { it.split("-").map { it.toLong() } }
|
||||||
|
val powTenTable = generateSequence(1L) { it * 10L }.take(20).toList().toLongArray()
|
||||||
|
var sum = 0L
|
||||||
|
for ((low, high) in ranges) {
|
||||||
|
var i = low
|
||||||
|
while (i <= high) {
|
||||||
|
val numDigits = log10(i.toDouble()).toInt() + 1
|
||||||
|
if (numDigits % 2 == 1) {
|
||||||
|
i = powTenTable[numDigits]
|
||||||
|
} else {
|
||||||
|
val firstHalf = i / powTenTable[numDigits / 2]
|
||||||
|
val matchNum = firstHalf * powTenTable[numDigits / 2] + firstHalf
|
||||||
|
if (matchNum in low..high) {
|
||||||
|
sum += matchNum
|
||||||
|
}
|
||||||
|
i = (firstHalf + 1) * powTenTable[numDigits / 2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
val ranges = input[0].split(",").map { it.split("-").map { it.toLong() } }
|
||||||
|
val powTenTable = generateSequence(1L) { it * 10L }.take(20).toList().toLongArray()
|
||||||
|
val mulElevenArray = arrayOf(
|
||||||
|
generateSequence(1L) { it * 10L + 1L }.take(20).toList().toLongArray(),
|
||||||
|
generateSequence(1L) { it * 100L + 1L }.take(10).toList().toLongArray(),
|
||||||
|
generateSequence(1L) { it * 1000L + 1L }.take(7).toList().toLongArray(),
|
||||||
|
generateSequence(1L) { it * 10000L + 1L }.take(5).toList().toLongArray(),
|
||||||
|
generateSequence(1L) { it * 100000L + 1L }.take(4).toList().toLongArray(),
|
||||||
|
generateSequence(1L) { it * 1000000L + 1L }.take(4).toList().toLongArray(),
|
||||||
|
generateSequence(1L) { it * 10000000L + 1L }.take(3).toList().toLongArray(),
|
||||||
|
generateSequence(1L) { it * 100000000L + 1L }.take(3).toList().toLongArray(),
|
||||||
|
)
|
||||||
|
|
||||||
|
var sum = 0L
|
||||||
|
for ((low, high) in ranges) {
|
||||||
|
var i = low
|
||||||
|
val illegalSet = mutableSetOf<Long>()
|
||||||
|
while (i <= high) {
|
||||||
|
val numDigits = log10(i.toDouble()).toInt() + 1
|
||||||
|
for (n in 1..numDigits / 2) {
|
||||||
|
val firstBit = i / powTenTable[numDigits - n]
|
||||||
|
val matchNum = firstBit * mulElevenArray[n - 1][(numDigits - 1) / n]
|
||||||
|
illegalSet.add(matchNum)
|
||||||
|
}
|
||||||
|
val firstHalf = i / powTenTable[numDigits / 2]
|
||||||
|
i = (firstHalf + 1) * powTenTable[numDigits / 2]
|
||||||
|
}
|
||||||
|
sum += illegalSet.filter { it in low..high }.sum()
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day02_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 1227775554L)
|
||||||
|
check(testInputPart2Result == 4174379265L)
|
||||||
|
|
||||||
|
val input = readInput("aoc2025/Day02")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 3: Lobby ---
|
||||||
|
https://adventofcode.com/2025/day/3
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
987654321111111
|
||||||
|
811111111111119
|
||||||
|
234234234234278
|
||||||
|
818181911112111
|
||||||
|
"""
|
||||||
|
|
||||||
|
val powTenTable = generateSequence(1L) { it * 10L }.take(20).toList().toLongArray()
|
||||||
|
|
||||||
|
fun rec(i: String, si: Int, left: Int): Long {
|
||||||
|
for (dig in 9 downTo 1) {
|
||||||
|
val dp = i.indexOf('0' + dig, si)
|
||||||
|
if (dp >= 0) {
|
||||||
|
if (left == 0) {
|
||||||
|
return dig.toLong()
|
||||||
|
} else {
|
||||||
|
val lastDigits = rec(i, dp + 1, left - 1)
|
||||||
|
if (lastDigits >= 0) return lastDigits + dig * powTenTable[left]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1L
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Long {
|
||||||
|
return input.sumOf { rec(it, 0, 1) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
return input.sumOf { rec(it, 0, 11) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day03_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 357L)
|
||||||
|
check(testInputPart2Result == 3121910778619L)
|
||||||
|
|
||||||
|
val input = readInput("aoc2025/Day03")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 4: Printing Department ---
|
||||||
|
https://adventofcode.com/2025/day/4
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
..@@.@@@@.
|
||||||
|
@@@.@.@.@@
|
||||||
|
@@@@@.@.@@
|
||||||
|
@.@@@@..@.
|
||||||
|
@@.@@@@.@@
|
||||||
|
.@@@@@@@.@
|
||||||
|
.@.@.@.@@@
|
||||||
|
@.@@@.@@@@
|
||||||
|
.@@@@@@@@.
|
||||||
|
@.@.@@@.@.
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val grid = CharGrid(input)
|
||||||
|
val matches = grid.findMatchesRelPos { it == '@' }.filter { grid.matchRelative(it, CharGrid.BOX_POS) { it == '@' }.size < 4 }
|
||||||
|
return matches.count()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
val grid = CharGrid(input)
|
||||||
|
var removed = 0
|
||||||
|
do {
|
||||||
|
val matches = grid.findMatchesRelPos { it == '@' }.filter { grid.matchRelative(it, CharGrid.BOX_POS) { it == '@' }.size < 4 }
|
||||||
|
grid.applyWithPos(matches) { _, _ -> 'x' }
|
||||||
|
removed += matches.size
|
||||||
|
} while (matches.isNotEmpty())
|
||||||
|
return removed
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day04_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 13)
|
||||||
|
check(testInputPart2Result == 43)
|
||||||
|
|
||||||
|
val input = readInput("aoc2025/Day04")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 5: Cafeteria ---
|
||||||
|
https://adventofcode.com/2025/day/5
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
3-5
|
||||||
|
10-14
|
||||||
|
16-20
|
||||||
|
12-18
|
||||||
|
|
||||||
|
1
|
||||||
|
5
|
||||||
|
8
|
||||||
|
11
|
||||||
|
17
|
||||||
|
32
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val ranges = ArrayList<LongRange>()
|
||||||
|
var freshNum = 0
|
||||||
|
for (i in input) {
|
||||||
|
if (i.contains("-")) {
|
||||||
|
val (low, high) = i.split("-").map { it.toLong() }
|
||||||
|
ranges.add(LongRange(low, high))
|
||||||
|
} else if (i.isNotBlank()) {
|
||||||
|
if (ranges.any { i.toLong() in it }) freshNum++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return freshNum
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
val ranges = ArrayList<LongRange>()
|
||||||
|
for (i in input) {
|
||||||
|
if (i.contains("-")) {
|
||||||
|
val (low, high) = i.split("-").map { it.toLong() }
|
||||||
|
ranges.add(LongRange(low, high))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val consolidated = ArrayList<LongRange>()
|
||||||
|
ranges.sortBy { it.first }
|
||||||
|
var lastLow = ranges[0].first
|
||||||
|
var lastHigh = ranges[0].last
|
||||||
|
for (r in 1..ranges.lastIndex) {
|
||||||
|
if (ranges[r].first > lastHigh) {
|
||||||
|
consolidated.add(LongRange(lastLow, lastHigh))
|
||||||
|
lastLow = ranges[r].first
|
||||||
|
lastHigh = ranges[r].last
|
||||||
|
} else {
|
||||||
|
lastHigh = lastHigh.coerceAtLeast(ranges[r].last)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
consolidated.add(LongRange(lastLow, lastHigh))
|
||||||
|
return consolidated.sumOf { it.last - it.first + 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day05_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 3)
|
||||||
|
check(testInputPart2Result == 14L)
|
||||||
|
|
||||||
|
val input = readInput("aoc2025/Day05")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import splitLongs
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 6: Trash Compactor ---
|
||||||
|
https://adventofcode.com/2025/day/6
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
123 328 51 64
|
||||||
|
45 64 387 23
|
||||||
|
6 98 215 314
|
||||||
|
* + * +
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Long {
|
||||||
|
val numbers = input.dropLast(1).map { it.splitLongs().toLongArray() }.toList()
|
||||||
|
val ops = input.last().split(" ").filter(String::isNotBlank)
|
||||||
|
return ops.withIndex().sumOf { (c, op) -> if (op == "+") numbers.sumOf { it[c] } else numbers.fold(1L) { acc, row -> acc * row[c] } }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
val numbers = ArrayList<LongArray>()
|
||||||
|
val maxLineLength = input.maxOf { it.length }
|
||||||
|
val spaceMask = BooleanArray(maxLineLength)
|
||||||
|
input.forEach { it.withIndex().filter { (_, ch) -> ch != ' ' }.forEach { (c, _) -> spaceMask[c] = true } }
|
||||||
|
val numColl = ArrayList<Long>()
|
||||||
|
for (c in 0 until maxLineLength) {
|
||||||
|
if (spaceMask[c]) numColl.add(input.dropLast(1).map { it[c] }.joinToString("").trim().toLong())
|
||||||
|
if (!spaceMask[c] || c == maxLineLength - 1) {
|
||||||
|
numbers.add(numColl.toLongArray())
|
||||||
|
numColl.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return input.last().split(" ").filter(String::isNotBlank).withIndex()
|
||||||
|
.sumOf { (c, op) -> if (op == "+") numbers[c].sum() else numbers[c].fold(1L) { acc, row -> acc * row } }
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day06_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 4277556L)
|
||||||
|
check(testInputPart2Result == 3263827L)
|
||||||
|
|
||||||
|
val input = readInput("aoc2025/Day06")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 7: Laboratories ---
|
||||||
|
https://adventofcode.com/2025/day/7
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
.......S.......
|
||||||
|
...............
|
||||||
|
.......^.......
|
||||||
|
...............
|
||||||
|
......^.^......
|
||||||
|
...............
|
||||||
|
.....^.^.^.....
|
||||||
|
...............
|
||||||
|
....^.^...^....
|
||||||
|
...............
|
||||||
|
...^.^...^.^...
|
||||||
|
...............
|
||||||
|
..^...^.....^..
|
||||||
|
...............
|
||||||
|
.^.^.^.^.^...^.
|
||||||
|
...............
|
||||||
|
"""
|
||||||
|
|
||||||
|
val memo = ArrayList<HashMap<Int, Long>>()
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val grid = CharGrid(input)
|
||||||
|
var splits = 0
|
||||||
|
for (row in 2 until grid.height step 2) {
|
||||||
|
for (col in 1 until grid.width - 1) {
|
||||||
|
val ch = grid[col, row - 2]
|
||||||
|
if (ch == 'S') {
|
||||||
|
if (grid[col, row] == '^') {
|
||||||
|
grid[col - 1, row] = 'S'
|
||||||
|
grid[col + 1, row] = 'S'
|
||||||
|
splits++
|
||||||
|
} else {
|
||||||
|
grid[col, row] = 'S'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return splits
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rec(grid: CharGrid, col: Int, row: Int): Long {
|
||||||
|
if (row >= grid.height) return 1
|
||||||
|
return memo[row / 2].getOrPut(col) {
|
||||||
|
if (grid[col, row + 2] == '^')
|
||||||
|
rec(grid, col - 1, row + 2) + rec(grid, col + 1, row + 2)
|
||||||
|
else
|
||||||
|
rec(grid, col, row + 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
val grid = CharGrid(input)
|
||||||
|
val col = grid.collectMatches { it == 'S' }.single().second.dc
|
||||||
|
while (memo.size <= grid.height / 2) memo.add(HashMap())
|
||||||
|
return rec(grid, col, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day07_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 21)
|
||||||
|
check(testInputPart2Result == 40L)
|
||||||
|
|
||||||
|
val input = readInput("aoc2025/Day07")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import splitInts
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 8: Playground ---
|
||||||
|
https://adventofcode.com/2025/day/8
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
162,817,812
|
||||||
|
57,618,57
|
||||||
|
906,360,560
|
||||||
|
592,479,940
|
||||||
|
352,342,300
|
||||||
|
466,668,158
|
||||||
|
542,29,236
|
||||||
|
431,825,988
|
||||||
|
739,650,466
|
||||||
|
52,470,668
|
||||||
|
216,146,977
|
||||||
|
819,987,18
|
||||||
|
117,168,530
|
||||||
|
805,96,715
|
||||||
|
346,949,466
|
||||||
|
970,615,88
|
||||||
|
941,993,340
|
||||||
|
862,61,35
|
||||||
|
984,92,344
|
||||||
|
425,690,689
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun dist(n1: IntArray, n2: IntArray) =
|
||||||
|
sqrt(((n1[0] - n2[0]).toFloat() * (n1[0] - n2[0]) + (n1[1] - n2[1]).toFloat() * (n1[1] - n2[1]) + (n1[2] - n2[2]).toFloat() * (n1[2] - n2[2])))
|
||||||
|
|
||||||
|
fun find(parent: IntArray, n: Int): Int {
|
||||||
|
if (parent[n] != n) {
|
||||||
|
parent[n] = find(parent, parent[n])
|
||||||
|
}
|
||||||
|
return parent[n]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun union(parent: IntArray, n1: Int, n2: Int): Boolean {
|
||||||
|
val r1 = find(parent, n1)
|
||||||
|
val r2 = find(parent, n2)
|
||||||
|
if (r1 != r2) {
|
||||||
|
parent[r1] = r2
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// having an oct-tree might speed up things here, so we skip far distances
|
||||||
|
fun createDistances(nodes: Array<IntArray>): PriorityQueue<Pair<Pair<Int, Int>, Float>> {
|
||||||
|
val distances = PriorityQueue<Pair<Pair<Int, Int>, Float>>(nodes.size * nodes.size / 2, Comparator.comparing { it.second })
|
||||||
|
for (i1 in 0 until nodes.size) {
|
||||||
|
for (i2 in i1 + 1 until nodes.size) {
|
||||||
|
val d = dist(nodes[i1], nodes[i2])
|
||||||
|
distances.add((i1 to i2) to d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return distances
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part1(input: List<String>, numConnections: Int): Long {
|
||||||
|
val nodes = input.map { it.splitInts(",").toIntArray() }.toTypedArray()
|
||||||
|
val distances = createDistances(nodes)
|
||||||
|
|
||||||
|
val n = nodes.size
|
||||||
|
var numCircuits = 0
|
||||||
|
val unionFindParentArray = IntArray(n) { it }
|
||||||
|
while (distances.isNotEmpty()) {
|
||||||
|
val (n1, n2) = distances.remove().first
|
||||||
|
union(unionFindParentArray, n1, n2)
|
||||||
|
numCircuits++
|
||||||
|
if (numCircuits == numConnections) break
|
||||||
|
}
|
||||||
|
|
||||||
|
val circuitSizes = LongArray(n)
|
||||||
|
for (i in 0 until n) {
|
||||||
|
circuitSizes[find(unionFindParentArray, i)]++
|
||||||
|
}
|
||||||
|
return circuitSizes.sortedDescending().take(3).fold(1L) { acc, l -> acc * l }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
val nodes = input.map { it.splitInts(",").toIntArray() }.toTypedArray()
|
||||||
|
val distances = createDistances(nodes)
|
||||||
|
|
||||||
|
val n = nodes.size
|
||||||
|
var numCircuits = 1
|
||||||
|
val unionFindParentArray = IntArray(n) { it }
|
||||||
|
while (distances.isNotEmpty()) {
|
||||||
|
val (n1, n2) = distances.remove().first
|
||||||
|
if (union(unionFindParentArray, n1, n2)) {
|
||||||
|
numCircuits++
|
||||||
|
if (numCircuits == n) {
|
||||||
|
return nodes[n1][0].toLong() * nodes[n2][0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1L
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day08_test")
|
||||||
|
val testInputPart1Result = part1(testInput, 10)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 40L)
|
||||||
|
check(testInputPart2Result == 25272L)
|
||||||
|
|
||||||
|
val input = readInput("aoc2025/Day08")
|
||||||
|
part1(input, 1000).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import splitInts
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 9: Movie Theater ---
|
||||||
|
https://adventofcode.com/2025/day/9
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
7,1
|
||||||
|
11,1
|
||||||
|
11,7
|
||||||
|
9,7
|
||||||
|
9,5
|
||||||
|
2,5
|
||||||
|
2,3
|
||||||
|
7,3
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Long {
|
||||||
|
val coords = input.map { it.splitInts(",") }
|
||||||
|
var maxArea = 0L
|
||||||
|
for (i1 in 0 until coords.size step 2) {
|
||||||
|
for (i2 in i1 + 2 until coords.size step 2) {
|
||||||
|
maxArea =
|
||||||
|
((abs(coords[i1][0] - coords[i2][0]) + 1).toLong() * (abs(coords[i1][1] - coords[i2][1]) + 1).toLong()).coerceAtLeast(
|
||||||
|
maxArea
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxArea
|
||||||
|
}
|
||||||
|
|
||||||
|
fun intersectsRanges(set: TreeMap<Int, ArrayList<IntRange>>, x1: Int, y1: Int, x2: Int, y2: Int) =
|
||||||
|
set.subMap(y1 + 1, y2).values.any { it.any { it.first < x2 && it.last > x1 } }
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
val coords = input.map { it.splitInts(",") }
|
||||||
|
val xSpans = TreeMap<Int, ArrayList<IntRange>>()
|
||||||
|
val ySpans = TreeMap<Int, ArrayList<IntRange>>()
|
||||||
|
for (i1 in 0 until coords.size) {
|
||||||
|
val i2 = if (i1 != coords.size - 1) i1 + 1 else 0
|
||||||
|
if (coords[i1][1] == coords[i2][1]) {
|
||||||
|
xSpans.getOrPut(coords[i1][1]) { ArrayList<IntRange>() }.add(
|
||||||
|
IntRange(coords[i1][0].coerceAtMost(coords[i2][0]), coords[i1][0].coerceAtLeast(coords[i2][0]))
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ySpans.getOrPut(coords[i1][0]) { ArrayList<IntRange>() }.add(
|
||||||
|
IntRange(coords[i1][1].coerceAtMost(coords[i2][1]), coords[i1][1].coerceAtLeast(coords[i2][1]))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxArea = 0L
|
||||||
|
for (i1 in 0 until coords.size step 2) {
|
||||||
|
for (i2 in i1 + 2 until coords.size step 2) {
|
||||||
|
val x1 = coords[i1][0].coerceAtMost(coords[i2][0])
|
||||||
|
val x2 = coords[i1][0].coerceAtLeast(coords[i2][0])
|
||||||
|
val y1 = coords[i1][1].coerceAtMost(coords[i2][1])
|
||||||
|
val y2 = coords[i1][1].coerceAtLeast(coords[i2][1])
|
||||||
|
|
||||||
|
val area = ((x2 - x1 + 1).toLong() * (y2 - y1 + 1).toLong())
|
||||||
|
if (area > maxArea &&
|
||||||
|
!(intersectsRanges(xSpans, x1, y1, x2, y2) || intersectsRanges(ySpans, y1, x1, y2, x2))
|
||||||
|
) {
|
||||||
|
maxArea = area
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxArea
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day09_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 50L)
|
||||||
|
check(testInputPart2Result == 24L)
|
||||||
|
val input = readInput("aoc2025/Day09")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import chineseRemainder
|
||||||
|
import extendedGcdBigInteger
|
||||||
|
import primeFactors
|
||||||
|
import primeSequence
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import sieveOfErastosthenes
|
||||||
|
import splitInts
|
||||||
|
import java.math.BigInteger
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 10: Factory ---
|
||||||
|
https://adventofcode.com/2025/day/10
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
[.##.] (3) (1,3) (2) (2,3) (0,2) (0,1) {3,5,4,7}
|
||||||
|
[...#.] (0,2,3,4) (2,3) (0,4) (0,1,2) (1,2,3,4) {7,5,12,7,2}
|
||||||
|
[.###.#] (0,1,2,3,4) (0,3,4) (0,1,2,4,5) (1,2) {10,11,11,5,10,5}
|
||||||
|
"""
|
||||||
|
|
||||||
|
data class Machine(val size: Int, val target: Int, val toggles: IntArray, val joltage: IntArray)
|
||||||
|
|
||||||
|
fun parse(input: List<String>): ArrayList<Machine> {
|
||||||
|
val machines = ArrayList<Machine>()
|
||||||
|
for (i in input) {
|
||||||
|
val stuff = i.split(" ")
|
||||||
|
val machSize = stuff[0].length - 2
|
||||||
|
val target = stuff[0].removeSurrounding("[", "]")
|
||||||
|
.foldIndexed(0) { i, acc, ch -> acc + if (ch == '#') (1 shl i) else 0 }
|
||||||
|
val toggles =
|
||||||
|
stuff.drop(1).dropLast(1)
|
||||||
|
.map { it.removeSurrounding("(", ")").splitInts(",").fold(0) { acc, v -> acc + (1 shl v) } }
|
||||||
|
.toIntArray()
|
||||||
|
val joltages = stuff.last().removeSurrounding("{", "}").splitInts(",").toIntArray()
|
||||||
|
machines.add(Machine(machSize, target, toggles, joltages))
|
||||||
|
}
|
||||||
|
return machines
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
val machines = parse(input)
|
||||||
|
var sumButts = 0
|
||||||
|
for (m in machines) {
|
||||||
|
// look at which different possible sets of toggles need to be pressed to result in the target number
|
||||||
|
// pressing an even time will cancel out the effect, so only look what happens if you press once
|
||||||
|
var minPresses = Int.MAX_VALUE
|
||||||
|
for (tm in 1 until (1 shl m.toggles.size)) {
|
||||||
|
val odds = m.toggles.filterIndexed { i, v -> (1 shl i) and tm != 0 }
|
||||||
|
val result = odds.fold(0) { acc, iv -> acc xor iv }
|
||||||
|
if (result == m.target) {
|
||||||
|
minPresses = minPresses.coerceAtMost(odds.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sumButts += minPresses
|
||||||
|
}
|
||||||
|
return sumButts
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
val machines = parse(input)
|
||||||
|
|
||||||
|
val sieve = sieveOfErastosthenes(10000)
|
||||||
|
val primes = primeSequence(sieve).take(500).toList()
|
||||||
|
var sumButts = 0
|
||||||
|
for (m in machines) {
|
||||||
|
println()
|
||||||
|
val maxJoltage = m.joltage.max()
|
||||||
|
val numBits = 32 - maxJoltage.countLeadingZeroBits()
|
||||||
|
val bigTarget = m.joltage.foldIndexed(BigInteger.ZERO) { index, acc, i ->
|
||||||
|
acc.plus(
|
||||||
|
BigInteger.valueOf(i.toLong()).shiftLeft(numBits * index)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val bigToggles = ArrayList<BigInteger>()
|
||||||
|
for (t in m.toggles) {
|
||||||
|
var tt = t
|
||||||
|
var bigToggle = BigInteger.ZERO
|
||||||
|
var shift = 0
|
||||||
|
while (tt > 0) {
|
||||||
|
if (tt and 1 != 0) {
|
||||||
|
bigToggle += BigInteger.ONE.shiftLeft(shift)
|
||||||
|
}
|
||||||
|
shift += numBits
|
||||||
|
tt = tt shr 1
|
||||||
|
}
|
||||||
|
bigToggles.add(bigToggle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// j0 * bigtoggle[0] + j1 * bigtoggle[1] + ... = bigTarget
|
||||||
|
// is a Linear Diophantine equation that can be solved with the extended Euclidean algorithm
|
||||||
|
|
||||||
|
val (gcd, coeffients) = extendedGcdBigInteger(bigToggles)
|
||||||
|
if (bigTarget % gcd != BigInteger.ZERO) throw IllegalStateException()
|
||||||
|
|
||||||
|
val factorMap = HashMap<Long, Long>()
|
||||||
|
var good = true
|
||||||
|
val primeFactors = primeFactors(bigTarget.toLong(), sieve)
|
||||||
|
for (pf in primeFactors) {
|
||||||
|
val sf = factorMap[pf]
|
||||||
|
val pr = 1000 % pf
|
||||||
|
if (sf != null && sf != pr) {
|
||||||
|
good = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
factorMap[pf] = pr
|
||||||
|
}
|
||||||
|
val reducedPairs = factorMap.map { it.value to it.key }.sortedBy { it.second }
|
||||||
|
val rx = reducedPairs.chineseRemainder()
|
||||||
|
|
||||||
|
val leastFingers = 0
|
||||||
|
println(leastFingers)
|
||||||
|
sumButts += leastFingers
|
||||||
|
}
|
||||||
|
return sumButts
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day10_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 7)
|
||||||
|
//check(testInputPart2Result == 33)
|
||||||
|
|
||||||
|
val input = readInput("aoc2025/Day10")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 11: Reactor ---
|
||||||
|
https://adventofcode.com/2025/day/11
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
aaa: you hhh
|
||||||
|
you: bbb ccc
|
||||||
|
bbb: ddd eee
|
||||||
|
ccc: ddd eee fff
|
||||||
|
ddd: ggg
|
||||||
|
eee: out
|
||||||
|
fff: out
|
||||||
|
ggg: out
|
||||||
|
hhh: ccc fff iii
|
||||||
|
iii: out
|
||||||
|
"""
|
||||||
|
|
||||||
|
val inlineTestInput2 = """
|
||||||
|
svr: aaa bbb
|
||||||
|
aaa: fft
|
||||||
|
fft: ccc
|
||||||
|
bbb: tty
|
||||||
|
tty: ccc
|
||||||
|
ccc: ddd eee
|
||||||
|
ddd: hub
|
||||||
|
hub: fff
|
||||||
|
eee: dac
|
||||||
|
dac: fff
|
||||||
|
fff: ggg hhh
|
||||||
|
ggg: out
|
||||||
|
hhh: out
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun parseInput(input: List<String>): HashMap<String, MutableSet<String>> {
|
||||||
|
val children = HashMap<String, MutableSet<String>>()
|
||||||
|
for (i in input) {
|
||||||
|
val (n, chs) = i.split(": ")
|
||||||
|
val ch = chs.split(" ")
|
||||||
|
children.getOrPut(n) { HashSet() }.addAll(ch)
|
||||||
|
}
|
||||||
|
return children
|
||||||
|
}
|
||||||
|
|
||||||
|
fun rec(
|
||||||
|
children: HashMap<String, MutableSet<String>>,
|
||||||
|
p: String,
|
||||||
|
stop: String,
|
||||||
|
visited: MutableSet<String> = HashSet(),
|
||||||
|
memo: HashMap<String, Long> = HashMap()
|
||||||
|
): Long {
|
||||||
|
if (p == stop) return 1L
|
||||||
|
return memo.getOrPut(p) {
|
||||||
|
visited.add(p)
|
||||||
|
val res = children[p]?.sumOf { rec(children, it, stop, visited, memo) } ?: 0L
|
||||||
|
visited.remove(p)
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Long {
|
||||||
|
return rec(parseInput(input), "you", "out")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Long {
|
||||||
|
val children = parseInput(input)
|
||||||
|
|
||||||
|
val srvToDac = rec(children, "svr", "dac")
|
||||||
|
val dacToFft = rec(children, "dac", "fft")
|
||||||
|
val fftToOut = rec(children, "fft", "out")
|
||||||
|
val srvToFft = rec(children, "svr", "fft")
|
||||||
|
val fftToDac = rec(children, "fft", "dac")
|
||||||
|
val dacToOut = rec(children, "dac", "out")
|
||||||
|
|
||||||
|
return srvToDac * dacToFft * fftToOut + srvToFft * fftToDac * dacToOut
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
val testInput2 = inlineTestInput2.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day11_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput2)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
check(testInputPart1Result == 5L)
|
||||||
|
check(testInputPart2Result == 2L)
|
||||||
|
|
||||||
|
val input = readInput("aoc2025/Day11")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
package aoc2025
|
||||||
|
|
||||||
|
import CharGrid
|
||||||
|
import println
|
||||||
|
import readInput
|
||||||
|
import splitInts
|
||||||
|
|
||||||
|
/*
|
||||||
|
--- Day 12: Christmas Tree Farm ---
|
||||||
|
https://adventofcode.com/2025/day/12
|
||||||
|
*/
|
||||||
|
fun main() {
|
||||||
|
|
||||||
|
val inlineTestInput = """
|
||||||
|
0:
|
||||||
|
###
|
||||||
|
##.
|
||||||
|
##.
|
||||||
|
|
||||||
|
1:
|
||||||
|
###
|
||||||
|
##.
|
||||||
|
.##
|
||||||
|
|
||||||
|
2:
|
||||||
|
.##
|
||||||
|
###
|
||||||
|
##.
|
||||||
|
|
||||||
|
3:
|
||||||
|
##.
|
||||||
|
###
|
||||||
|
##.
|
||||||
|
|
||||||
|
4:
|
||||||
|
###
|
||||||
|
#..
|
||||||
|
###
|
||||||
|
|
||||||
|
5:
|
||||||
|
###
|
||||||
|
.#.
|
||||||
|
###
|
||||||
|
|
||||||
|
4x4: 0 0 0 0 2 0
|
||||||
|
12x5: 1 0 1 0 2 2
|
||||||
|
12x5: 1 0 1 0 3 2
|
||||||
|
"""
|
||||||
|
|
||||||
|
fun fits(grid: LongArray, x: Int, y: Int, present: Long) =
|
||||||
|
(grid[y] or ((7L and present) shl x) == grid[y]) &&
|
||||||
|
(grid[y + 1] or ((7L and (present shr 3)) shl x) == grid[y + 1]) &&
|
||||||
|
(grid[y + 2] or ((7L and (present shr 6)) shl x) == grid[y + 2])
|
||||||
|
|
||||||
|
fun part1(input: List<String>): Int {
|
||||||
|
var lp = 0
|
||||||
|
val presents = ArrayList<IntArray>()
|
||||||
|
// nothing of this stuff is needed :-(
|
||||||
|
while (!input[lp].contains("x")) {
|
||||||
|
var charGrid = CharGrid(input.subList(lp + 1, lp + 4))
|
||||||
|
lp += 5
|
||||||
|
|
||||||
|
val setRot = HashSet<Int>()
|
||||||
|
for (r in 0..7) {
|
||||||
|
val present = charGrid.generateGridPos().foldIndexed(0) { index, acc, pos -> acc + (if (charGrid[pos] == '#') (1 shl index) else 0) }
|
||||||
|
setRot.add(present)
|
||||||
|
if (r != 3) {
|
||||||
|
// rotate
|
||||||
|
val newGrid = charGrid.copyOf()
|
||||||
|
newGrid.generateGridPos().forEach { (dc, dr) -> newGrid[2 - dr, dc] = charGrid[dc, dr] }
|
||||||
|
charGrid = newGrid
|
||||||
|
} else {
|
||||||
|
// flip
|
||||||
|
val newGrid = charGrid.copyOf()
|
||||||
|
newGrid.generateGridPos().forEach { (dc, dr) -> newGrid[2 - dc, dr] = charGrid[dc, dr] }
|
||||||
|
charGrid = newGrid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
presents.add(setRot.toIntArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
var fitted = 0
|
||||||
|
val presentSizes = presents.map { it[0].countOneBits() }.toIntArray()
|
||||||
|
|
||||||
|
for (p in lp until input.size) {
|
||||||
|
val (dim, pl) = input[p].split(": ")
|
||||||
|
val (width, height) = dim.splitInts("x")
|
||||||
|
val placements = pl.splitInts().toIntArray()
|
||||||
|
val totalSize = placements.mapIndexed { i, v -> v * presentSizes[i] }.sum()
|
||||||
|
if (totalSize > width * height) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val totalPresents = placements.sum()
|
||||||
|
// just assume it will fit if there is enough area
|
||||||
|
if (totalPresents * 9 <= width * height) {
|
||||||
|
fitted++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Oh no!")
|
||||||
|
// here the hard part would have started, but except for the example input,
|
||||||
|
// it never gets here
|
||||||
|
//val grid = LongArray(height) { (1L shl width) - 1L }
|
||||||
|
}
|
||||||
|
return fitted
|
||||||
|
}
|
||||||
|
|
||||||
|
fun part2(input: List<String>): Int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// test if implementation meets criteria from the description, like:
|
||||||
|
val testInput = inlineTestInput.trim().reader().readLines()
|
||||||
|
//val testInput = readInput("aoc2025/Day12_test")
|
||||||
|
val testInputPart1Result = part1(testInput)
|
||||||
|
println("Part 1 Test: $testInputPart1Result")
|
||||||
|
val testInputPart2Result = part2(testInput)
|
||||||
|
println("Part 2 Test: $testInputPart2Result")
|
||||||
|
//check(testInputPart1Result == 2)
|
||||||
|
check(testInputPart2Result == 0)
|
||||||
|
|
||||||
|
val input = readInput("aoc2025/Day12")
|
||||||
|
part1(input).println()
|
||||||
|
part2(input).println()
|
||||||
|
}
|
||||||
@@ -4,15 +4,19 @@ import com.mohamedrejeb.ksoup.html.parser.KsoupHtmlHandler
|
|||||||
import com.mohamedrejeb.ksoup.html.parser.KsoupHtmlParser
|
import com.mohamedrejeb.ksoup.html.parser.KsoupHtmlParser
|
||||||
import fuel.Fuel
|
import fuel.Fuel
|
||||||
import fuel.method
|
import fuel.method
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.nio.charset.Charset
|
import java.nio.charset.Charset
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
|
import java.time.LocalTime
|
||||||
import java.time.Month
|
import java.time.Month
|
||||||
import java.time.ZoneId
|
import java.time.ZoneId
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.time.Duration.Companion.minutes
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
var cookie = "<insert your session cookie here or store in gradle.properties>"
|
var cookie = "<insert your session cookie here or store in gradle.properties>"
|
||||||
@@ -23,7 +27,7 @@ fun main() {
|
|||||||
cookie = props.getProperty("cookie", cookie)
|
cookie = props.getProperty("cookie", cookie)
|
||||||
}
|
}
|
||||||
|
|
||||||
val downloader = Downloader(2024, "aoc2024", cookie)
|
val downloader = Downloader(2025, "aoc2025", cookie)
|
||||||
downloader.updateToLatest()
|
downloader.updateToLatest()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,15 +46,16 @@ class Downloader(val year: Int, val packageName: String, val sessionCookie: Stri
|
|||||||
if (!Files.exists(targetDir)) {
|
if (!Files.exists(targetDir)) {
|
||||||
Files.createDirectories(targetDir)
|
Files.createDirectories(targetDir)
|
||||||
}
|
}
|
||||||
val now = LocalDate.now(ZoneId.of("UTC-1"))
|
val now = LocalDate.now(ZoneId.of("UTC+1"))
|
||||||
val lastDay = if (now.isBefore(LocalDate.of(year, Month.DECEMBER, 25))) {
|
val maxPuzzles = if (year < 2025) 25 else 12
|
||||||
|
val lastDay = if (now.isBefore(LocalDate.of(year, Month.DECEMBER, maxPuzzles))) {
|
||||||
if (now.isAfter(LocalDate.of(year, Month.NOVEMBER, 30))) {
|
if (now.isAfter(LocalDate.of(year, Month.NOVEMBER, 30))) {
|
||||||
now.dayOfMonth
|
now.dayOfMonth
|
||||||
} else {
|
} else {
|
||||||
throw IllegalStateException("You're too early.")
|
throw IllegalStateException("You're too early.")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
25
|
maxPuzzles
|
||||||
}
|
}
|
||||||
|
|
||||||
for (day in 1..lastDay) {
|
for (day in 1..lastDay) {
|
||||||
@@ -59,6 +64,20 @@ class Downloader(val year: Int, val packageName: String, val sessionCookie: Stri
|
|||||||
val descriptionFile = targetDir.resolve(DESC_FILENAME.format(day))
|
val descriptionFile = targetDir.resolve(DESC_FILENAME.format(day))
|
||||||
val genClassFile = targetDir.resolve(GENCLASS_FILENAME.format(day))
|
val genClassFile = targetDir.resolve(GENCLASS_FILENAME.format(day))
|
||||||
if (!Files.exists(inputFile)) {
|
if (!Files.exists(inputFile)) {
|
||||||
|
if (day == lastDay && LocalTime.now(ZoneId.of("UTC+1")).hour < 5) {
|
||||||
|
println("Puzzle will not be available within the next hour. Skipping.")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
while (day == lastDay && LocalTime.now(ZoneId.of("UTC+1")).hour == 5) {
|
||||||
|
println("Waiting for puzzle to become available...")
|
||||||
|
runBlocking {
|
||||||
|
if (LocalTime.now(ZoneId.of("UTC+1")).minute < 59) {
|
||||||
|
delay(1.minutes)
|
||||||
|
} else {
|
||||||
|
delay(1.seconds)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
println("Attempting to download input for day $day")
|
println("Attempting to download input for day $day")
|
||||||
val (code, data) = downloadInput(day)
|
val (code, data) = downloadInput(day)
|
||||||
if (code != 200) {
|
if (code != 200) {
|
||||||
@@ -156,7 +175,7 @@ class Downloader(val year: Int, val packageName: String, val sessionCookie: Stri
|
|||||||
url = "https://adventofcode.com/$year/day/$day$suffix",
|
url = "https://adventofcode.com/$year/day/$day$suffix",
|
||||||
method = "GET",
|
method = "GET",
|
||||||
headers = mapOf(
|
headers = mapOf(
|
||||||
"User-Agent" to "git.platon42.de/chrisly42/advent-of-code",
|
"User-Agent" to "git.platon42.de/chrisly42/advent-of-code (chrisly@platon42.de)",
|
||||||
"Cookie" to "session=$sessionCookie"
|
"Cookie" to "session=$sessionCookie"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user