Compare commits
28
Commits
79360efdf4
..
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 |
+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
|
||||||
|
|||||||
+4
-1
@@ -152,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]) }
|
||||||
|
|
||||||
@@ -201,7 +204,7 @@ 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 matchRelativeRelPos(pos: RelPos, relposes: Iterable<RelPos>, predicate: (char: Char) -> Boolean): List<RelPos> =
|
fun matchRelative(pos: RelPos, relposes: Iterable<RelPos>, predicate: (char: Char) -> Boolean): List<RelPos> =
|
||||||
relposes.filter { predicate(get(pos.translate(it))) }
|
relposes.filter { predicate(get(pos.translate(it))) }
|
||||||
|
|
||||||
fun matchAbsoluteRelPos(pos: RelPos, relposes: Iterable<RelPos>, predicate: (char: Char) -> Boolean): List<RelPos> =
|
fun matchAbsoluteRelPos(pos: RelPos, relposes: Iterable<RelPos>, predicate: (char: Char) -> Boolean): List<RelPos> =
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ Prize: X=18641, Y=10279
|
|||||||
|
|
||||||
// Linear Diophantine equations
|
// Linear Diophantine equations
|
||||||
/* The simplest linear Diophantine equation takes the form
|
/* The simplest linear Diophantine equation takes the form
|
||||||
a*x + b*x = c
|
a*x + b*y = c
|
||||||
where a, b and c are given integers.
|
where a, b and c are given integers.
|
||||||
The solutions are described by the following theorem:
|
The solutions are described by the following theorem:
|
||||||
This Diophantine equation has a solution (where x and y are integers),
|
This Diophantine equation has a solution (where x and y are integers),
|
||||||
|
|||||||
@@ -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>"
|
||||||
@@ -42,7 +46,7 @@ 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 maxPuzzles = if (year < 2025) 25 else 12
|
val maxPuzzles = if (year < 2025) 25 else 12
|
||||||
val lastDay = if (now.isBefore(LocalDate.of(year, Month.DECEMBER, maxPuzzles))) {
|
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))) {
|
||||||
@@ -60,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) {
|
||||||
@@ -157,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