Compare commits

..

37 Commits

Author SHA1 Message Date
7a1dba17f1 Day 12
All checks were successful
Solve / example-action (push) Successful in 3m20s
2024-12-12 20:51:17 +01:00
0f2b85f2ad Fix weird indent
All checks were successful
Solve / example-action (push) Successful in 3m8s
2024-12-12 15:36:47 +01:00
f16258eae7 Add readme
All checks were successful
Solve / example-action (push) Successful in 3m11s
2024-12-11 19:51:28 +01:00
aef2dc6159 Decrease runtime by increasing cache size.
All checks were successful
Solve / example-action (push) Successful in 3m16s
2024-12-11 19:42:02 +01:00
5fd63ac62f remove extra output
All checks were successful
Solve / example-action (push) Successful in 6m2s
2024-12-11 16:58:16 +01:00
c359318235 day 11
All checks were successful
Solve / example-action (push) Successful in 6m6s
2024-12-11 16:42:20 +01:00
162b48d5cb In progress day 11
Some checks failed
Solve / example-action (push) Failing after 1m40s
2024-12-11 14:44:30 +01:00
2a5388e90b Change trailhead values to private.
All checks were successful
Solve / example-action (push) Successful in 3m0s
2024-12-11 14:19:03 +01:00
6f69aae304 Day 10!
Some checks failed
Solve / example-action (push) Has been cancelled
2024-12-11 14:17:25 +01:00
874622fc78 Complete day09
All checks were successful
Solve / example-action (push) Successful in 2m55s
2024-12-11 11:10:10 +01:00
68eec5b3b3 Prepared Day 9 files.
All checks were successful
Solve / example-action (push) Successful in 2m48s
2024-12-09 08:29:10 +01:00
a3195fc0d9 Add testvalues for day 8
All checks were successful
Solve / example-action (push) Successful in 2m48s
2024-12-08 13:30:06 +01:00
b2ff775638 Day 8
Some checks failed
Solve / example-action (push) Has been cancelled
2024-12-08 13:28:24 +01:00
ccf7dd091c revert dafef2c099
All checks were successful
Solve / example-action (push) Successful in 2m35s
revert Timings for solutions.
2024-12-07 21:24:53 +00:00
dafef2c099 Timings for solutions.
Some checks failed
Solve / example-action (push) Failing after 1m26s
2024-12-07 21:22:38 +00:00
9bbf08f489 Add tests as well.
All checks were successful
Solve / example-action (push) Successful in 2m38s
2024-12-07 16:59:25 +01:00
9e3e1adeaa Day 7!
All checks were successful
Solve / example-action (push) Successful in 2m31s
2024-12-07 16:54:30 +01:00
8f47a462df Remove unused imports.
All checks were successful
Solve / example-action (push) Successful in 2m29s
2024-12-06 12:00:46 +01:00
fcaa401d60 Day 6 part 2 completed.
All checks were successful
Solve / example-action (push) Successful in 2m26s
2024-12-06 11:25:52 +01:00
67ce498228 Minor fix with yMax.
All checks were successful
Solve / example-action (push) Successful in 1m36s
2024-12-06 10:31:20 +01:00
83c078a4cb Part 1 solved, part 2 got issues.
All checks were successful
Solve / example-action (push) Successful in 1m40s
2024-12-06 09:56:47 +01:00
0f75670efb Switch to raw string.
All checks were successful
Solve / example-action (push) Successful in 46s
2024-12-06 04:33:43 +00:00
c1179a2f11 Revert stuff to see if input is fixed.
All checks were successful
Solve / example-action (push) Successful in 47s
2024-12-05 15:22:40 +01:00
f416a0f07c Or not?
All checks were successful
Solve / example-action (push) Successful in 48s
2024-12-05 15:21:00 +01:00
beb4b9ef6c Does this fix?
All checks were successful
Solve / example-action (push) Successful in 46s
2024-12-05 15:17:30 +01:00
6a42539f09 Bit of debugging3
Some checks failed
Solve / example-action (push) Failing after 42s
2024-12-05 15:15:45 +01:00
a36d0efd4b Bit of debugging2
Some checks failed
Solve / example-action (push) Failing after 36s
2024-12-05 15:11:22 +01:00
143679ec7e Bit of debugging
Some checks failed
Solve / example-action (push) Failing after 35s
2024-12-05 15:05:43 +01:00
12cab8af56 Check input
Some checks failed
Solve / example-action (push) Failing after 36s
2024-12-05 14:59:39 +01:00
29a9f321cd Removing inputs.
Some checks failed
Solve / example-action (push) Failing after 36s
2024-12-05 14:43:19 +01:00
92176926c7 Removed unnecessary constructor.
All checks were successful
Solve / example-action (push) Successful in 47s
2024-12-05 08:14:31 +01:00
0a78452ca9 Day 5.
All checks were successful
Solve / example-action (push) Successful in 47s
2024-12-05 07:55:38 +01:00
a4264163af Make day 4 executable!
All checks were successful
Solve / example-action (push) Successful in 38s
2024-12-04 08:39:36 +01:00
24f65fd994 Day4
All checks were successful
Solve / example-action (push) Successful in 39s
2024-12-04 08:31:12 +01:00
3992e5f484 Forgot to set validate mode for tests.
All checks were successful
Solve / example-action (push) Successful in 31s
2024-12-03 08:28:42 +01:00
07e25fadd1 Added extremely simple tests for future refactoring.
Some checks failed
Solve / example-action (push) Failing after 8s
2024-12-03 08:26:53 +01:00
80ffa962c0 Day 3.
All checks were successful
Solve / example-action (push) Successful in 21s
2024-12-03 07:40:58 +01:00
30 changed files with 1025 additions and 2007 deletions

View File

@@ -8,19 +8,53 @@ on:
jobs: jobs:
example-action: example-action:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
DAY01INPUT: ${{ secrets.DAY01INPUT }}
DAY02INPUT: ${{ secrets.DAY02INPUT }}
DAY03INPUT: ${{ secrets.DAY03INPUT }}
DAY04INPUT: ${{ secrets.DAY04INPUT }}
DAY05INPUT: ${{ secrets.DAY05INPUT }}
DAY06INPUT: ${{ secrets.DAY06INPUT }}
DAY07INPUT: ${{ secrets.DAY07INPUT }}
DAY08INPUT: ${{ secrets.DAY08INPUT }}
DAY09INPUT: ${{ secrets.DAY09INPUT }}
DAY10INPUT: ${{ secrets.DAY10INPUT }}
DAY11INPUT: ${{ secrets.DAY11INPUT }}
DAY12INPUT: ${{ secrets.DAY12INPUT }}
DAY13INPUT: ${{ secrets.DAY13INPUT }}
DAY14INPUT: ${{ secrets.DAY14INPUT }}
DAY15INPUT: ${{ secrets.DAY15INPUT }}
DAY16INPUT: ${{ secrets.DAY16INPUT }}
DAY17INPUT: ${{ secrets.DAY17INPUT }}
DAY18INPUT: ${{ secrets.DAY18INPUT }}
DAY19INPUT: ${{ secrets.DAY19INPUT }}
DAY20INPUT: ${{ secrets.DAY20INPUT }}
DAY21INPUT: ${{ secrets.DAY21INPUT }}
DAY22INPUT: ${{ secrets.DAY22INPUT }}
DAY23INPUT: ${{ secrets.DAY23INPUT }}
DAY24INPUT: ${{ secrets.DAY24INPUT }}
DAY25INPUT: ${{ secrets.DAY25INPUT }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-java@v4 - uses: actions/setup-java@v4
with: with:
distribution: 'corretto' # See 'Supported distributions' for available options distribution: 'corretto'
java-version: '21' java-version: '21'
- uses: https://github.com/fwilhe2/setup-kotlin@main - uses: https://github.com/fwilhe2/setup-kotlin@main
- name: Run tests
run: ./simpletest.sh validate
- name: Solutions - name: Solutions
run: | run: |
set -e set -e
for script in $(ls *.kts | sort -r); do for script in $(ls *.kts | sort -r); do
echo "Running $script" | tee -a solutions-output.txt dayname=$(basename $script .kts | tr 'a-z' 'A-Z')INPUT
kotlin $script 2>&1 | tee -a solutions-output.txt input_var="${!dayname}"
if [ -z "$input_var" ]; then
echo "Skipping $script: No input found for $dayname" | tee -a solutions-output.txt
continue
fi
echo "Running $script with input from $dayname" | tee -a solutions-output.txt
echo -n "$input_var" | kotlin $script 2>&1 | tee -a solutions-output.txt
done done
- name: Upload Solutions Output - name: Upload Solutions Output
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.idea
*input.txt

5
README.md Normal file
View File

@@ -0,0 +1,5 @@
To run the script, use the following command:
cat inputfile.txt | kotlin dayxx.kts
Replace `inputfile.txt` with the name of your input file. You can obtain your personalized input for the problem at [adventofcode.com](https://adventofcode.com). Note that the test values are based on my own inputs and may not match yours.

4
day01.kts Normal file → Executable file
View File

@@ -1,8 +1,8 @@
import java.io.File #!/usr/bin/env kotlin
import java.util.Scanner import java.util.Scanner
import kotlin.math.abs import kotlin.math.abs
val scanner = Scanner(File("day01input.txt")) val scanner = Scanner(System.`in`)
val firstList = mutableListOf<Int>() val firstList = mutableListOf<Int>()
val secondList = mutableListOf<Int>() val secondList = mutableListOf<Int>()
var secondMap = mutableMapOf<Int, Int>() var secondMap = mutableMapOf<Int, Int>()

2
day01.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
Total distance: 1603498
Similarity score: 25574739

File diff suppressed because it is too large Load Diff

4
day02.kts Normal file → Executable file
View File

@@ -1,8 +1,8 @@
import java.io.File #!/usr/bin/env kotlin
import java.util.Scanner import java.util.Scanner
import kotlin.math.abs import kotlin.math.abs
val scanner = Scanner(File("day02input.txt")) val scanner = Scanner(System.`in`)
var safelines = 0 var safelines = 0
var dampenedsafelines = 0 var dampenedsafelines = 0
while (scanner.hasNextLine()) { while (scanner.hasNextLine()) {

2
day02.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
Safe lines: 472
Safe lines with dampener: 520

File diff suppressed because it is too large Load Diff

30
day03.kts Executable file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/env kotlin
import java.util.Scanner
val scanner = Scanner(System.`in`)
var partOneSum = 0
var partTwoSum = 0
var doState = true
while (scanner.hasNextLine()) {
val line = scanner.nextLine()
val regex = Regex("""(mul\((\d+),(\d+)\))|(do\(\))|(don't\(\))""")
val match = regex.findAll(line)
for (matchResult in match) {
if (matchResult.value == "don't()") {
doState = false
} else if (matchResult.value == "do()") {
doState = true
} else {
val (a, b, c) = matchResult.destructured
val factorOne = b.toInt()
val factorTwo = c.toInt()
partOneSum += factorOne * factorTwo
if (doState) {
partTwoSum += factorOne * factorTwo
}
}
}
}
println("Part One Sum: $partOneSum")
println("Part Two Sum: $partTwoSum")

2
day03.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
Part One Sum: 187833789
Part Two Sum: 94455185

81
day04.kts Executable file
View File

@@ -0,0 +1,81 @@
#!/usr/bin/env kotlin
import java.util.Scanner
val scanner = Scanner(System.`in`)
val xMasSearchWord = listOf("M", "A", "S") // Skip X because that triggers the search at 0,0 offset.
val xMasSearchOffsets = listOf(
listOf(Pair(0, 1), Pair(0, 2), Pair(0, 3)),
listOf(Pair(1, 0), Pair(2, 0), Pair(3, 0)),
listOf(Pair(0, -1), Pair(0, -2), Pair(0, -3)),
listOf(Pair(-1, 0), Pair(-2, 0), Pair(-3, 0)),
listOf(Pair(1, 1), Pair(2, 2), Pair(3, 3)),
listOf(Pair(-1, 1), Pair(-2, 2), Pair(-3, 3)),
listOf(Pair(-1, -1), Pair(-2, -2), Pair(-3, -3)),
listOf(Pair(1, -1), Pair(2, -2), Pair(3, -3))
)
var ycoordinates = mutableListOf<List<String>>()
while (scanner.hasNextLine()) {
val line = scanner.nextLine()
val xcoordinates = line.split("").filter { it.isNotEmpty() }
ycoordinates.add(xcoordinates)
}
var xMasCount = 0
var masXCount = 0
for (y in ycoordinates.indices) {
for (x in ycoordinates[y].indices) {
if (ycoordinates[y][x] == "X") {
xMasCount += findXmas(y, x)
}
if (ycoordinates[y][x] == "A") {
masXCount += findMasX(y, x)
}
}
}
fun findXmas(y: Int, x: Int): Int {
var xmasfound = 0
for (searchOffset in xMasSearchOffsets) {
val last = searchOffset.last()
if ((y+last.first) !in ycoordinates.indices || (x+last.second) !in ycoordinates[y].indices){
continue
}
var match = true
for (i in searchOffset.indices) {
val (dy, dx) = searchOffset[i]
val search = xMasSearchWord[i]
if (ycoordinates[y+dy][x+dx] != search) {
match = false
break
}
}
if (match) {
xmasfound += 1
}
}
return xmasfound
}
println("Xmas count: $xMasCount")
println("Mas X count: $masXCount")
fun findMasX(y: Int, x: Int): Int {
if (y == 0 || x == 0 || y == ycoordinates.lastIndex || x == ycoordinates[y].lastIndex) {
return 0
}
val validChars = mutableListOf("M", "S")
if (ycoordinates[y - 1][x - 1] !in validChars) {
return 0
}
val remainsOne = validChars[if(validChars.indexOf(ycoordinates[y - 1][x - 1]) == 0) 1 else 0]
if (ycoordinates[y + 1][x + 1] != remainsOne) {
return 0
}
if (ycoordinates[y - 1][x + 1] !in validChars) {
return 0
}
val remainsTwo = validChars[if(validChars.indexOf(ycoordinates[y - 1][x + 1]) == 0) 1 else 0]
if (ycoordinates[y + 1][x - 1] != remainsTwo) {
return 0
}
return 1
}

2
day04.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
Xmas count: 2573
Mas X count: 1850

49
day05.kts Executable file
View File

@@ -0,0 +1,49 @@
#!/usr/bin/env kotlin
import java.util.Scanner
var orderingRules = mutableListOf<Pair<Int, Int>>()
var pagesList = mutableListOf<MutableList<Int>>()
val scanner = Scanner(System.`in`)
while (scanner.hasNextLine()) {
val line = scanner.nextLine()
if (line.contains('|')) {
val order = line.split("|").map { it.toInt() }
orderingRules.add(Pair(order[0], order[1]))
} else if (line.contains(',')) {
val pages = line.split(",").map { it.toInt() }
pagesList.add(pages.toMutableList())
}
}
var orderedMiddleSum = 0
var unorderedMiddleSum = 0
for (page in pagesList) {
if (isOrdered(page)) {
orderedMiddleSum += page[page.size / 2]
} else {
val orderedPage = page.sortedWith { a, b -> compareByRules(a, b) }
unorderedMiddleSum += orderedPage[orderedPage.size / 2]
}
}
fun compareByRules(a: Int, b: Int): Int {
for ((smaller, bigger) in orderingRules) {
if (a == smaller && b == bigger) return -1
if (a == bigger && b == smaller) return 1
}
return 0
}
fun isOrdered(page: MutableList<Int>): Boolean {
for (i in orderingRules.indices) {
val (smaller, bigger) = orderingRules[i]
val smallerIndex = page.indexOf(smaller)
val biggerIndex = page.indexOf(bigger)
if (smallerIndex != -1 && biggerIndex != -1 && biggerIndex < smallerIndex) {
return false
}
}
return true
}
println("Ordered middle sum: $orderedMiddleSum")
println("Unordered middle sum: $unorderedMiddleSum")

2
day05.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
Ordered middle sum: 5713
Unordered middle sum: 5180

142
day06.kts Executable file
View File

@@ -0,0 +1,142 @@
#!/usr/bin/env kotlin
import java.lang.RuntimeException
import java.util.Scanner
data class LocationState(val location: Pair<Int, Int>, val direction: Direction)
enum class Direction(val offset: Pair<Int, Int>) {
NORTH(Pair(0, -1)),
EAST(Pair(1, 0)),
SOUTH(Pair(0, 1)),
WEST(Pair(-1, 0));
fun move(currentLocation: Pair<Int, Int>): Pair<Int, Int> {
return Pair(
currentLocation.first + offset.first,
currentLocation.second + offset.second
)
}
fun turnLeft(): Direction {
return when (this) {
NORTH -> WEST
WEST -> SOUTH
SOUTH -> EAST
EAST -> NORTH
}
}
fun turnRight(): Direction {
return when (this) {
NORTH -> EAST
EAST -> SOUTH
SOUTH -> WEST
WEST -> NORTH
}
}
fun moveByPartOneRules(
currentLocation: Pair<Int, Int>,
blocks: Set<Pair<Int, Int>>
): LocationState {
var currentDirection = this
do {
val potentialMove = currentDirection.move(currentLocation)
if (potentialMove !in blocks) {
return LocationState(potentialMove, currentDirection)
}
currentDirection = currentDirection.turnRight()
} while (currentDirection != this)
throw RuntimeException("Nowhere to go! $currentLocation $this")
}
}
fun isPositionInTheRoom(player: Pair<Int, Int>, xMax: Int, yMax: Int): Boolean {
return player.first in 0..xMax && player.second in 0..yMax
}
fun isLoop(
playerLocation: Pair<Int, Int>,
playerDirection: Direction,
history: List<LocationState>,
blocks: MutableSet<Pair<Int, Int>>,
potentialBlockLocation: Pair<Int, Int>,
xMax: Int,
yMax: Int
): Boolean {
var localLocation = playerLocation
var localDirection = playerDirection
val localHistory = history.toMutableList()
val localBlocks = blocks.toMutableSet()
localBlocks.add(potentialBlockLocation)
while (LocationState(localLocation, localDirection) !in localHistory) {
localHistory.add(LocationState(localLocation, localDirection))
val (newLocation, newDirection) = localDirection.moveByPartOneRules(localLocation, localBlocks)
localLocation = newLocation
localDirection = newDirection
if (!isPositionInTheRoom(localLocation, xMax, yMax)) {
return false
}
}
return true
}
val scanner = Scanner(System.`in`)
val visited = mutableSetOf<Pair<Int, Int>>()
val history = mutableListOf<LocationState>()
var playerLocation = Pair(0, 0)
var playerDirection: Direction = Direction.NORTH
val blocks = mutableSetOf<Pair<Int, Int>>()
val potentialBlocks = mutableSetOf<Pair<Int, Int>>()
var yMax = 0
var xMax = 0
while (scanner.hasNextLine()) {
val line = scanner.nextLine()
for (i in line.indices) {
if (line[i] == '.') {
continue
}
if (line[i] == '>') {
playerDirection = Direction.EAST
playerLocation = Pair(i, yMax)
} else if (line[i] == '<') {
playerDirection = Direction.WEST
playerLocation = Pair(i, yMax)
} else if (line[i] == '^') {
playerDirection = Direction.NORTH
playerLocation = Pair(i, yMax)
} else if (line[i] == 'v') {
playerDirection = Direction.SOUTH
playerLocation = Pair(i, yMax)
} else if (line[i] == '#') {
blocks.add(Pair(i, yMax))
}
}
xMax = xMax.coerceAtLeast(line.lastIndex)
yMax++
}
yMax-- // Should represent the max index.
val initialPosition = playerLocation.copy()
while (isPositionInTheRoom(playerLocation, xMax, yMax)) {
visited.add(playerLocation)
if (checkForLoopIfObstacleInFront(playerLocation, playerDirection, history)) {
potentialBlocks.add(playerDirection.move(playerLocation))
}
history.add(LocationState(playerLocation, playerDirection))
val (newLocation, newDirection) = playerDirection.moveByPartOneRules(playerLocation, blocks)
playerLocation = newLocation
playerDirection = newDirection
}
println("Player location: ${visited.size}")
println("Potential blocks: ${potentialBlocks.size}")
fun checkForLoopIfObstacleInFront(
playerLocation: Pair<Int, Int>,
playerDirection: Direction,
history: MutableList<LocationState>
): Boolean {
val potentialBlockLocation = playerDirection.moveByPartOneRules(playerLocation, blocks).location
val alreadyVisited = potentialBlockLocation in visited
val positionNotInTheRoom = !isPositionInTheRoom(potentialBlockLocation, xMax, yMax)
if (alreadyVisited || positionNotInTheRoom) {
return false
}
return isLoop(playerLocation, playerDirection, history, blocks, potentialBlockLocation, xMax, yMax)
}

2
day06.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
Player location: 4883
Potential blocks: 1655

45
day07.kts Executable file
View File

@@ -0,0 +1,45 @@
#!/usr/bin/env kotlin
import java.util.Scanner
val scanner = Scanner(System.`in`)
var total = 0L
var totalTwo = 0L
val operations = listOf("*", "+")
while (scanner.hasNext()) {
val line = scanner.nextLine()
val (targetString, rest) = line.split(":", limit = 2).map { it.trim() }
val target = targetString.toLong()
val values = rest.split(Regex("""\s+""")).map { it.toLong() }
if (hitsTarget(target, values, operations)) {
total += target
totalTwo += target
} else if (hitsTarget(target, values, listOf("||") + operations)) {
totalTwo += target
}
}
println("Total result: $total")
println("Total result (part 2): $totalTwo")
fun hitsTarget(target: Long, values: List<Long>, operations: List<String>): Boolean {
if (values.size == 1) {
return target == values[0]
}
val (operandOne, operandTwo) = values.take(2)
if (operandOne > target) {
return false
}
val rest = values.drop(2)
for (op in operations) {
val result = when (op) {
"+" -> operandOne + operandTwo
"*" -> operandOne * operandTwo
"||" -> "$operandOne$operandTwo".toLong()
else -> throw IllegalArgumentException("Unknown operation: $op")
}
if (hitsTarget(target, listOf(result) + rest, operations)) {
return true
}
}
return false
}

2
day07.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
Total result: 12553187650171
Total result (part 2): 96779702119491

82
day08.kts Executable file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/env kotlin
import java.util.Scanner
val scanner = Scanner(System.`in`)
val antennaMap = mutableMapOf<String, MutableList<Pair<Int, Int>>>()
val antinodeSet = mutableSetOf<Pair<Int, Int>>()
val harmonicAntinodeSet = mutableSetOf<Pair<Int, Int>>()
var xMax = 0
var yMax = 0
while (scanner.hasNext()) {
val symbols = scanner.nextLine().trim().split("").filter { it.isNotEmpty() }
xMax = xMax.coerceAtLeast(symbols.lastIndex)
for ((i, symbol) in symbols.withIndex()) {
if (symbol == ".") continue
antennaMap.getOrPut(symbol) { mutableListOf() }.add(Pair(i, yMax))
}
yMax++
}
yMax--
fun getAntinodes(pair: Pair<Int, Int>, pair1: Pair<Int, Int>, xMax: Int, yMax: Int): Collection<Pair<Int, Int>> {
val antinodes = mutableSetOf<Pair<Int, Int>>()
val diffOne = Pair(pair.first - pair1.first, pair.second - pair1.second)
antinodes.add(Pair(pair.first + diffOne.first, pair.second + diffOne.second))
antinodes.add(Pair(pair1.first - diffOne.first, pair1.second - diffOne.second))
antinodes.removeIf { it.first < 0 || it.second < 0 || it.first > xMax || it.second > yMax }
return antinodes
}
fun getHarmonicAntiNodes(
pair: Pair<Int, Int>,
pair1: Pair<Int, Int>,
xMax: Int,
yMax: Int
): Collection<Pair<Int, Int>> {
val antinodes = mutableSetOf<Pair<Int, Int>>()
val diffOne = Pair(pair.first - pair1.first, pair.second - pair1.second)
var mult = 0
while ((pair.first + mult * diffOne.first) <= xMax && (pair.second + mult * diffOne.second) <= yMax && 0 <= (pair.first + mult * diffOne.first) && 0 <= (pair.second + mult * diffOne.second) ) {
antinodes.add(Pair(pair.first + mult * diffOne.first, pair.second + mult * diffOne.second))
mult++
}
mult = 0
while ((pair1.first - mult * diffOne.first) <= xMax && (pair1.second - mult * diffOne.second) <= yMax && 0 <= (pair1.first - mult * diffOne.first) && 0 <= (pair1.second - mult * diffOne.second)) {
antinodes.add(Pair(pair1.first - mult * diffOne.first, pair1.second - mult * diffOne.second))
mult++
}
antinodes.removeIf { it.first < 0 || it.second < 0 || it.first > xMax || it.second > yMax }
return antinodes
}
for (antennaType in antennaMap.keys) {
for (i in antennaMap[antennaType]!!.indices) {
for (y in (i + 1)..(antennaMap[antennaType]!!.lastIndex)) {
val antinodes = getAntinodes(antennaMap[antennaType]!![i], antennaMap[antennaType]!![y], xMax, yMax)
val harmonicAntinodes = getHarmonicAntiNodes(antennaMap[antennaType]!![i], antennaMap[antennaType]!![y], xMax, yMax)
antinodeSet.addAll(antinodes)
harmonicAntinodeSet.addAll(harmonicAntinodes)
}
}
}
fun printMap(antinodeSet: Set<Pair<Int, Int>>) {
for (y in 0..yMax) {
for (x in 0..xMax) {
if (Pair(x, y) in antennaMap.values.flatten()) {
for (antennatype in antennaMap.keys) {
if (Pair(x, y) in antennaMap[antennatype]!!) {
print(antennatype)
break
}
}
} else if(Pair(x, y) in antinodeSet) print("#") else print(".")
}
println()
}
}
println("Antinodes: ${antinodeSet.size}")
println("Harmonic Antinodes: ${harmonicAntinodeSet.size}")

2
day08.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
Antinodes: 280
Harmonic Antinodes: 958

116
day09.kts Executable file
View File

@@ -0,0 +1,116 @@
#!/usr/bin/env kotlin
import java.lang.RuntimeException
import java.util.Scanner
val scanner = Scanner(System.`in`)
if (!scanner.hasNext()) {
throw RuntimeException("No input given.")
}
val fileSystem = mutableListOf<Int>()
val fileSystemDesc = scanner.nextLine().split("").filter { it.isNotEmpty() }.map { it.toInt() }.toMutableList()
for (i in 0..fileSystemDesc.lastIndex) {
for (j in 0 until fileSystemDesc[i]) {
if ((i and 1) == 0) {
fileSystem.add(i / 2)
} else {
fileSystem.add(-1)
}
}
}
val compressed = compressPartOne(fileSystem)
val checksumOne = calculateChecksum(compressed)
println("Part one checksum: $checksumOne")
val compressedTwo = compressPartTwo(fileSystem)
val checksumTwo = calculateChecksum(compressedTwo)
println("Part two checksum: $checksumTwo")
fun compressPartOne(fileSystem: MutableList<Int>): MutableList<Int> {
val compressed = fileSystem.toMutableList()
var freeSpacePointer = 0
var lastSectorPointer = compressed.lastIndex
while (freeSpacePointer <= lastSectorPointer) {
if (compressed[freeSpacePointer] != -1) {
freeSpacePointer++
continue
}
if (compressed[lastSectorPointer] == -1) {
lastSectorPointer--
continue
}
val tmp = compressed[freeSpacePointer]
compressed[freeSpacePointer] = compressed[lastSectorPointer]
compressed[lastSectorPointer] = tmp
lastSectorPointer--
freeSpacePointer++
}
return compressed
}
fun calculateChecksum(compressed: MutableList<Int>): Long {
var checksum = 0L
for (i in 0..compressed.lastIndex) {
if (compressed[i] != -1) {
checksum += i.toLong() * compressed[i].toLong()
}
}
return checksum
}
fun compressPartTwo(fileSystem: MutableList<Int>): MutableList<Int> {
val compressed = fileSystem.toMutableList()
var relevantFileId = compressed[compressed.lastIndex]
for (i in compressed.lastIndex downTo 0) {
if (compressed[i] == relevantFileId && relevantFileId >= 0) {
val fileSize = findFileSize(compressed, i, relevantFileId)
val startIndex = findStartIndexOfEmptySpaceOfLength(compressed, fileSize)
if (startIndex != -1 && startIndex < i) {
for (j in 0 until fileSize) {
compressed[startIndex + j] = compressed[i - j]
compressed[i - j] = -1
}
}
relevantFileId--
}
}
return compressed
}
fun findFileSize(compressed: MutableList<Int>, i: Int, relevantFileId: Int): Int {
var fileSize = 0
var j = i
while (j >= 0 && compressed[j] == relevantFileId) {
fileSize++
j--
}
return fileSize
}
fun findStartIndexOfEmptySpaceOfLength(compressed: MutableList<Int>, fileSize: Int): Int {
var startIndex = 0
while (startIndex < compressed.size) {
if (compressed[startIndex] != -1) {
startIndex++
} else {
var emptySpaceLength = 0
for (i in startIndex until (startIndex + fileSize)) {
if (i >= compressed.size) {
return -1
}
if (compressed[i] != -1) {
break
} else {
emptySpaceLength++
}
if (emptySpaceLength == fileSize) {
return startIndex
}
}
startIndex += emptySpaceLength
}
}
return -1
}

2
day09.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
Part one checksum: 6607511583593
Part two checksum: 6636608781232

82
day10.kts Executable file
View File

@@ -0,0 +1,82 @@
#!/usr/bin/env kotlin
import java.util.Scanner
class TrailHead(private val x: Int, private val y: Int, private val map: Map) {
private val peaks = mutableMapOf<Pair<Int, Int>, Int>()
fun getScore(): Int {
if (this.peaks.isEmpty()) {
for (peak in this.walkToPeak(this.x, this.y)) {
this.peaks[peak] = (this.peaks[peak] ?: 0) + 1
}
}
return this.peaks.keys.size
}
fun getGetRating(): Int {
this.getScore()
return this.peaks.values.sum()
}
private fun walkToPeak(x: Int, y: Int, previous: Int = -1): List<Pair<Int, Int>> {
if (x < 0 || x >= map.getLength() || y < 0 || y >= map.getHeight() || map.get(x, y) != (previous + 1)) {
return listOf()
} else if (map.get(x, y) == 9) {
return listOf(Pair(x, y))
} else {
val current = map.get(x, y)
return walkToPeak(x + 1, y, current) + walkToPeak(x - 1, y, current) + walkToPeak(x, y + 1, current) + walkToPeak(x, y - 1, current)
}
}
}
class Map(private val grid: List<List<Int>>) {
private val trailHeads = mutableListOf<TrailHead>()
init {
grid.forEachIndexed { y, row ->
row.forEachIndexed { x, height ->
if (height == 0) {
trailHeads.add(TrailHead(x, y, this))
}
}
}
}
fun addTrailHead(trailHead: TrailHead) {
trailHeads.add(trailHead)
}
fun get(x: Int, y: Int): Int {
return grid[y][x]
}
fun getHeight(): Int {
return grid.size
}
fun getLength(): Int {
return if (grid.isNotEmpty()) grid[0].size else 0
}
fun getScore(): Int {
return trailHeads.fold(0) { acc, trailHead -> acc + trailHead.getScore() }
}
fun getGetRating(): Int {
return trailHeads.fold(0) { acc, trailHead -> acc + trailHead.getGetRating() }
}
}
val scanner = Scanner(System.`in`)
val mapInput = mutableListOf<List<Int>>()
while (scanner.hasNext()) {
val line = scanner.nextLine()
val numbers = line.split("").filter { it.isNotEmpty() }.map { it.toInt() }
mapInput.add(numbers)
}
val map = Map(mapInput.reversed())
println("Score: ${map.getScore()}")
println("Rating: ${map.getGetRating()}")

2
day10.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
Score: 754
Rating: 1609

63
day11.kts Executable file
View File

@@ -0,0 +1,63 @@
#!/usr/bin/env kotlin
import java.util.Scanner
import java.util.PriorityQueue
val scanner = Scanner(System.`in`)
class PriorityCache<K, V>(private val maxSize: Int) {
private val cache = mutableMapOf<K, V>()
private val priorityQueue = PriorityQueue<Pair<K, Int>>(compareBy { it.second }) // Pair(Key, Priority)
fun put(key: K, value: V, priority: Int) {
if (cache.size >= maxSize) {
val leastPriority = priorityQueue.poll() // Remove the lowest-priority item
cache.remove(leastPriority.first)
}
cache[key] = value
priorityQueue.add(Pair(key, priority))
}
fun get(key: K): V? {
return cache[key]
}
fun isCached(key: K): Boolean {
return cache.containsKey(key)
}
}
val priorityCache = PriorityCache<String, Long>(50000)
fun getStonesAfter(input: List<Long>,iterations: Int, cache: PriorityCache<String, Long>): Long {
var sum = 0L
for (i in input) {
sum += getStoneNumber(i, iterations, cache)
}
return sum
}
fun getStoneNumber(number: Long, iterations: Int, cache: PriorityCache<String, Long>): Long {
if (iterations == 0) {
return 1L
} else if (cache.isCached("$iterations-$number")) {
return cache.get("$iterations-$number")!!
} else if (number == 0L) {
val stonesum = getStoneNumber(1, iterations - 1, cache)
cache.put("$iterations-0", stonesum, iterations)
return stonesum
} else if (number.toString().length % 2 == 0) {
val mid = number.toString().length / 2
val left = number.toString().substring(0, mid).toLong()
val right = number.toString().substring(mid).toLong()
val stonesum = getStoneNumber(left, iterations - 1, cache) + getStoneNumber(right, iterations - 1, cache)
cache.put("$iterations-$number", stonesum, iterations)
return stonesum
} else {
val newVal = number * 2024
val stonesum = getStoneNumber(newVal, iterations - 1, cache)
cache.put("$iterations-$number", stonesum, iterations)
return stonesum
}
}
val input = scanner.nextLine().split(" ").map { it.toLong() }
println("First part with 25 iterations: ${getStonesAfter(input, 25, priorityCache)}")
println("Second part with 75 iterations: ${getStonesAfter(input, 75, priorityCache)}")

2
day11.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
First part with 25 iterations: 222461
Second part with 75 iterations: 264350935776416

136
day12.kts Executable file
View File

@@ -0,0 +1,136 @@
#!/usr/bin/env kotlin
import java.util.Scanner
val scanner = Scanner(System.`in`)
class Map(private val grid: List<List<String>>) {
private val fields = mutableSetOf<Field>()
private val map = mutableListOf<List<Plot>>()
init {
for ((y, line) in grid.withIndex()) {
map.add(line.mapIndexed { x, s -> Plot(Pair(x, y), s) })
}
for (y in 0 until this.getHeight()) {
for (x in 0 until this.getWidth()) {
val plot = getPlot(x, y)
if (!plot.hasField()) {
val field = Field(this)
plot.addField(field)
fields.add(field)
field.expand(plot)
}
}
}
}
fun getWidth(): Int = grid[0].size
fun getHeight(): Int = grid.size
fun getPlot(x: Int, y: Int): Plot = map[y][x]
fun getFields(): Set<Field> = fields
fun getPrice(): Long = fields.sumOf { it.getPrice() }
fun getBulkPrice(): Long = fields.sumOf { it.getBulkPrice() }
}
class Field(private val map: Map) {
private val fieldPlots = mutableSetOf<Plot>()
private val borderPlots = mutableSetOf<Pair<Plot, Plot>>()
fun addPlot(plot: Plot) {
fieldPlots.add(plot)
}
fun expand(plot: Plot) {
val (x, y) = plot.getPosition()
val checkPositions = listOf(Pair(x - 1, y), Pair(x + 1, y), Pair(x, y - 1), Pair(x, y + 1))
for ((checkX, checkY) in checkPositions) {
if (checkX < 0 || checkX >= map.getWidth() || checkY < 0 || checkY >= map.getHeight()) {
addBorder(plot, Plot(Pair(checkX, checkY), "."))
continue
}
val checkedPlot = map.getPlot(checkX, checkY)
if (plot.getType() == checkedPlot.getType()) {
if (!checkedPlot.hasField()) {
checkedPlot.addField(this)
expand(checkedPlot)
}
} else {
addBorder(plot, checkedPlot)
}
}
}
private fun addBorder(plot: Plot, checkedPlot: Plot) {
borderPlots.add(Pair(plot, checkedPlot))
}
fun getPrice(): Long {
return getArea() * getCircumference()
}
fun getArea(): Long = fieldPlots.size.toLong()
fun getCircumference(): Long = borderPlots.size.toLong()
fun getBulkPrice(): Long {
return getArea() * getNumberOfSides()
}
private fun getNumberOfSides(): Long {
val leftBorders = borderPlots.filter { it.first.getPosition().first > it.second.getPosition().first}.map { it.first.getPosition() }
val rightBorders = borderPlots.filter { it.first.getPosition().first < it.second.getPosition().first}.map { it.first.getPosition() }
val topBorders = borderPlots.filter { it.first.getPosition().second > it.second.getPosition().second}.map { it.first.getPosition() }
val bottomBorders = borderPlots.filter { it.first.getPosition().second < it.second.getPosition().second}.map { it.first.getPosition() }
return countSides(leftBorders) + countSides(rightBorders) + countSides(topBorders) + countSides(bottomBorders)
}
private fun countSides(coordinates: List<Pair<Int, Int>>): Long {
val visited = mutableSetOf<Pair<Int, Int>>()
val coordinateSet = coordinates.toSet()
var sideCount = 0
val directions = listOf(Pair(1, 0), Pair(0, 1), Pair(-1, 0), Pair(0, -1))
fun dfs(coord: Pair<Int, Int>) {
val stack = mutableListOf(coord)
while (stack.isNotEmpty()) {
val current = stack.removeLast()
if (current in visited) continue
visited.add(current)
directions.map { (dx, dy) -> Pair(current.first + dx, current.second + dy) }
.filter { it in coordinateSet && it !in visited }
.forEach { stack.add(it) }
}
}
for (coord in coordinates) {
if (coord !in visited) {
sideCount++
dfs(coord)
}
}
return sideCount.toLong()
}
}
class Plot(private val position: Pair<Int, Int>, private val type: String) {
private var field: Field? = null
fun addField(field: Field) {
this.field = field
field.addPlot(this)
}
fun hasField(): Boolean = field != null
fun getPosition(): Pair<Int, Int> = position
fun getType(): String = type
}
val grid = mutableListOf<List<String>>()
while (scanner.hasNextLine()) {
grid.add(scanner.nextLine().split("").filter { it.isNotEmpty() })
}
val map = Map(grid.reversed())
println("The price is: ${map.getPrice()}")
println("The bulk price is ${map.getBulkPrice()}")

2
day12.kts.testvalue Normal file
View File

@@ -0,0 +1,2 @@
The price is: 1375476
The bulk price is 821372

127
simpletest.sh Executable file
View File

@@ -0,0 +1,127 @@
#!/bin/bash
# Function to show usage
usage() {
echo "Usage: $0 [-l] [write|validate]"
echo " -l Use local input files (e.g., day01input.txt)"
exit 1
}
# Parse arguments
LOCAL_MODE=0
while getopts ":l" opt; do
case $opt in
l) LOCAL_MODE=1 ;;
*) usage ;;
esac
done
shift $((OPTIND - 1))
if [[ $# -ne 1 ]]; then
usage
fi
MODE=$1
# Function to fetch input
fetch_input() {
local script=$1
local dayname
dayname=$(basename "$script" .kts | tr 'a-z' 'A-Z')
if [[ $LOCAL_MODE -eq 1 ]]; then
# Fetch input from local files
local input_file="${dayname,,}input.txt"
if [[ -f "$input_file" ]]; then
cat "$input_file"
else
echo "Error: Local input file $input_file not found" >&2
exit 1
fi
else
# Fetch input from environment variable
local env_var="${dayname}INPUT"
if [[ -z "${!env_var}" ]]; then
echo "Error: Environment variable $env_var not set" >&2
exit 1
fi
echo "${!env_var}"
fi
}
# Write mode
if [[ "$MODE" == "write" ]]; then
echo "Running in write mode..."
for script in *.kts; do
test_file="${script}.testvalue"
echo "Processing $script..."
input=$(fetch_input "$script")
output=$(echo "$input" | kotlin "$script" 2>&1)
if [[ -f "$test_file" ]]; then
if [[ "$output" == "$(cat "$test_file")" ]]; then
echo "No change: $script"
else
echo "Output changed for $script"
diff -u "$test_file" <(echo "$output")
read -p "Update test value? (y/n) " update
if [[ "$update" == "y" ]]; then
echo "$output" > "$test_file"
echo "Updated $test_file"
fi
fi
else
echo "$output" > "$test_file"
echo "Created $test_file"
fi
done
for test_file in *.kts.testvalue; do
script="${test_file%.testvalue}"
if [[ ! -f "$script" ]]; then
echo "Test file $test_file does not have a matching script."
read -p "Delete $test_file? (y/n) " delete
if [[ "$delete" == "y" ]]; then
rm "$test_file"
echo "Deleted $test_file"
fi
fi
done
# Validate mode
elif [[ "$MODE" == "validate" ]]; then
echo "Running in validate mode..."
pass=0
fail=0
for test_file in *.kts.testvalue; do
script="${test_file%.testvalue}"
if [[ -f "$script" ]]; then
input=$(fetch_input "$script")
output=$(echo "$input" | kotlin "$script" 2>&1)
if [[ "$output" == "$(cat "$test_file")" ]]; then
echo "PASS: $script"
((pass++))
else
echo "FAIL: $script"
diff -u "$test_file" <(echo "$output")
((fail++))
fi
else
echo "Orphaned test file: $test_file (no matching script)"
((fail++))
fi
done
echo "Summary: $pass pass, $fail fail"
if [[ $fail -gt 0 ]]; then
exit 1
else
exit 0
fi
else
usage
fi