Compare commits
4 commits
e8c4abc6a3
...
8becab7e9e
Author | SHA1 | Date | |
---|---|---|---|
8becab7e9e | |||
506113c189 | |||
d4c4df3914 | |||
9b42be3343 |
10 changed files with 272 additions and 26 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
**/input.txt
|
**/input.txt
|
||||||
|
**/example.txt
|
||||||
.direnv
|
.direnv
|
||||||
node_modules
|
node_modules
|
|
@ -1,4 +1,6 @@
|
||||||
#!/usr/bin/env deno
|
#!/usr/bin/env deno
|
||||||
|
import * as secrets from "./secrets.ts";
|
||||||
|
|
||||||
import * as path from "@std/path";
|
import * as path from "@std/path";
|
||||||
import * as ini from "@std/ini";
|
import * as ini from "@std/ini";
|
||||||
|
|
||||||
|
@ -190,19 +192,7 @@ if (import.meta.main) {
|
||||||
console.error("Multiple advent of code session cookies were found??");
|
console.error("Multiple advent of code session cookies were found??");
|
||||||
Deno.exit(1);
|
Deno.exit(1);
|
||||||
} else {
|
} else {
|
||||||
const process = new Deno.Command("secret-tool", {
|
await secrets.saveToken(rows[0].value);
|
||||||
args: [
|
|
||||||
"store",
|
|
||||||
"--label",
|
|
||||||
"Advent Of Code Session Token",
|
|
||||||
"token",
|
|
||||||
"value"
|
|
||||||
],
|
|
||||||
stdin: "piped"
|
|
||||||
}).spawn();
|
|
||||||
const stdin = process.stdin.getWriter();
|
|
||||||
await stdin.write(new TextEncoder().encode(rows[0].value));
|
|
||||||
await stdin.close();
|
|
||||||
console.log("Token saved!");
|
console.log("Token saved!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,12 +204,36 @@ if (import.meta.main) {
|
||||||
)
|
)
|
||||||
.reset()
|
.reset()
|
||||||
.command(
|
.command(
|
||||||
"input [day:number]",
|
"input <day:number>",
|
||||||
"Fetches the input for one day (or all if day # omitted) and writes to input.txt files"
|
"Fetches the input for one day and writes to a dayN/input.txt file"
|
||||||
)
|
)
|
||||||
.action((_: unknown, day: number | undefined) => {
|
.option(
|
||||||
if (day != undefined) {
|
"-o, --output [path:string]",
|
||||||
|
"The location to output the text to",
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.action(async ({ output }, day: number | undefined) => {
|
||||||
|
if (Date.now() < new Date(`Dec ${day} 2024 00:00:00 GMT-0500`).getTime()) {
|
||||||
|
console.error("Can't fetch an input for an unreleased day!");
|
||||||
|
Deno.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = await secrets.readToken();
|
||||||
|
if (day != undefined) {
|
||||||
|
const inputResponse = await fetch(`https://adventofcode.com/2024/day/${day}/input`, {
|
||||||
|
headers: {
|
||||||
|
cookie: `session=${token}`,
|
||||||
|
'user-agent': "Ty's Advent of Code CLI (https://git.myriation.xyz/Ty/advent-of-code-2024)"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (inputResponse.status === 200) {
|
||||||
|
console.log(await inputResponse.text())
|
||||||
|
} else {
|
||||||
|
console.log(`Error fetching input for day ${day}:\n\n${await inputResponse.text()}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.parse(Deno.args);
|
.parse(Deno.args);
|
|
@ -1,3 +1,9 @@
|
||||||
|
/**
|
||||||
|
* There is no proper OS-secrets library for deno (or even nodejs!) so this is a
|
||||||
|
* jank way to get around that on linux at least
|
||||||
|
*/
|
||||||
|
import * as io from "@std/io";
|
||||||
|
|
||||||
export async function saveToken(token: string) {
|
export async function saveToken(token: string) {
|
||||||
const process = new Deno.Command("secret-tool", {
|
const process = new Deno.Command("secret-tool", {
|
||||||
args: [
|
args: [
|
||||||
|
@ -15,17 +21,13 @@ export async function saveToken(token: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function readToken() {
|
export async function readToken() {
|
||||||
const process = new Deno.Command("secret-tool", {
|
const { stdout } = await new Deno.Command("secret-tool", {
|
||||||
args: [
|
args: [
|
||||||
"lookup",
|
"lookup",
|
||||||
"aoc-deno",
|
"aoc-deno",
|
||||||
"token"
|
"token"
|
||||||
],
|
],
|
||||||
stdout: "piped"
|
stdout: "piped"
|
||||||
}).spawn();
|
}).spawn().output();
|
||||||
const decoderStream = new TextDecoderStream();
|
return new TextDecoder().decode(stdout);
|
||||||
const stdout = process.stdout.pipeTo(decoderStream.writable);
|
|
||||||
for await (const v of decoderStream.readable.values()) {
|
|
||||||
console.log(v);
|
|
||||||
}
|
|
||||||
}
|
}
|
40
day10/part1.kts
Executable file
40
day10/part1.kts
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env kotlin
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
operator fun <E> List<List<E>>.get(position: Pair<Int, Int>): E = this[position.first][position.second]
|
||||||
|
|
||||||
|
val trailheads = mutableListOf<Pair<Int, Int>>()
|
||||||
|
val grid = File("input.txt").readLines().mapIndexed { row, line ->
|
||||||
|
line.toCharArray().mapIndexed { col, char ->
|
||||||
|
if (char == '0') trailheads.add(row to col)
|
||||||
|
char.digitToInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum = 0
|
||||||
|
for (trailhead in trailheads) {
|
||||||
|
val positions = mutableListOf(trailhead)
|
||||||
|
|
||||||
|
while (!positions.all { pos -> grid[pos] == 9 }) {
|
||||||
|
val iter = positions.listIterator()
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
val pos = iter.next()
|
||||||
|
|
||||||
|
iter.remove()
|
||||||
|
if (grid[pos] == 9) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grid[pos.first].getOrNull(pos.second - 1) == grid[pos] + 1) iter.add(pos.first to pos.second - 1)
|
||||||
|
if (grid[pos.first].getOrNull(pos.second + 1) == grid[pos] + 1) iter.add(pos.first to pos.second + 1)
|
||||||
|
if (grid.getOrNull(pos.first + 1)?.get(pos.second) == grid[pos] + 1) iter.add(pos.first + 1 to pos.second)
|
||||||
|
if (grid.getOrNull(pos.first - 1)?.get(pos.second) == grid[pos] + 1) iter.add(pos.first - 1 to pos.second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val tops = positions.toHashSet().size
|
||||||
|
sum += tops
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Result: $sum")
|
40
day10/part2.kts
Normal file
40
day10/part2.kts
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env kotlin
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
operator fun <E> List<List<E>>.get(position: Pair<Int, Int>): E = this[position.first][position.second]
|
||||||
|
|
||||||
|
val trailheads = mutableListOf<Pair<Int, Int>>()
|
||||||
|
val grid = File("input.txt").readLines().mapIndexed { row, line ->
|
||||||
|
line.toCharArray().mapIndexed { col, char ->
|
||||||
|
if (char == '0') trailheads.add(row to col)
|
||||||
|
char.digitToInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sum = 0
|
||||||
|
for (trailhead in trailheads) {
|
||||||
|
val positions = mutableListOf(trailhead)
|
||||||
|
|
||||||
|
while (!positions.all { pos -> grid[pos] == 9 }) {
|
||||||
|
val iter = positions.listIterator()
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
val pos = iter.next()
|
||||||
|
|
||||||
|
iter.remove()
|
||||||
|
if (grid[pos] == 9) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grid[pos.first].getOrNull(pos.second - 1) == grid[pos] + 1) iter.add(pos.first to pos.second - 1)
|
||||||
|
if (grid[pos.first].getOrNull(pos.second + 1) == grid[pos] + 1) iter.add(pos.first to pos.second + 1)
|
||||||
|
if (grid.getOrNull(pos.first + 1)?.get(pos.second) == grid[pos] + 1) iter.add(pos.first + 1 to pos.second)
|
||||||
|
if (grid.getOrNull(pos.first - 1)?.get(pos.second) == grid[pos] + 1) iter.add(pos.first - 1 to pos.second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val tops = positions.size
|
||||||
|
sum += tops
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Result: $sum")
|
76
day9/part1.kts
Executable file
76
day9/part1.kts
Executable file
|
@ -0,0 +1,76 @@
|
||||||
|
#!/usr/bin/env kotlin
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
data class ReplicatedData(
|
||||||
|
var fileId: Int?,
|
||||||
|
var times: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
val disk = File(if (args.size >= 1 && args[0] == "example") "./example.txt" else "./input.txt").readLines()[0]
|
||||||
|
|
||||||
|
// Parse into labeled sections
|
||||||
|
var id = 0
|
||||||
|
val parsed = LinkedList(disk.mapIndexed { i, char -> // I love linked lists i love linked lists i love linked lists
|
||||||
|
val num = char.digitToInt()
|
||||||
|
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
// Every other starting with i = 0 should be real data
|
||||||
|
ReplicatedData(
|
||||||
|
fileId = i / 2,
|
||||||
|
times = num
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ReplicatedData(
|
||||||
|
fileId = null,
|
||||||
|
times = num
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.filter { it.times != 0 })
|
||||||
|
|
||||||
|
val iter = parsed.listIterator()
|
||||||
|
var blocksFilled = 0
|
||||||
|
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
val currentGroup = iter.next()
|
||||||
|
if (currentGroup.fileId != null || currentGroup.times < 1) continue
|
||||||
|
|
||||||
|
val (groupTakingFromIndex, groupTakingFrom) = parsed.asReversed()
|
||||||
|
.filter {it.times > 0 }
|
||||||
|
.withIndex()
|
||||||
|
.find { (_, it) -> it.fileId != null } ?: break
|
||||||
|
if (parsed.size - 1 - groupTakingFromIndex <= iter.nextIndex() - 1) break
|
||||||
|
|
||||||
|
val amountToMove = min(currentGroup.times, groupTakingFrom.times)
|
||||||
|
|
||||||
|
// Take the amount we are moving from the previous location
|
||||||
|
groupTakingFrom.times -= amountToMove
|
||||||
|
// And add it before the current one
|
||||||
|
val originalEmptySpace = currentGroup.times
|
||||||
|
iter.set(
|
||||||
|
ReplicatedData(
|
||||||
|
fileId = groupTakingFrom.fileId,
|
||||||
|
times = amountToMove
|
||||||
|
)
|
||||||
|
)
|
||||||
|
blocksFilled += amountToMove
|
||||||
|
// If we still have some left, insert back in the empty space and rewind the cursor
|
||||||
|
if (amountToMove < originalEmptySpace) {
|
||||||
|
iter.add(
|
||||||
|
ReplicatedData(
|
||||||
|
fileId = null,
|
||||||
|
times = originalEmptySpace - amountToMove
|
||||||
|
)
|
||||||
|
)
|
||||||
|
iter.previous() // Go back one so the next iteration of the loop will catch the new empty data we just added
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println(
|
||||||
|
parsed
|
||||||
|
.filter { it.times > 0 && it.fileId != null }
|
||||||
|
.flatMap { group -> (1..group.times).map { _ -> group.fileId!! } }
|
||||||
|
.mapIndexed { i, data -> i.toULong() * data.toULong() }.sum()
|
||||||
|
)
|
47
day9/part2.kts
Executable file
47
day9/part2.kts
Executable file
|
@ -0,0 +1,47 @@
|
||||||
|
#!/usr/bin/env kotlin
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
val debug = true
|
||||||
|
|
||||||
|
val disk = File(if (args.size >= 1 && args[0] == "example") "./example.txt" else "./input.txt").readLines()[0]
|
||||||
|
|
||||||
|
// Parse into labeled sections
|
||||||
|
var id = 0
|
||||||
|
val parsed = disk.flatMapIndexed { i, char -> // I love linked lists i love linked lists i love linked lists
|
||||||
|
val num = char.digitToInt()
|
||||||
|
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
// Every other starting with i = 0 should be real data
|
||||||
|
List(num) { i / 2 }
|
||||||
|
} else {
|
||||||
|
List(num) { null }
|
||||||
|
}
|
||||||
|
}.toMutableList()
|
||||||
|
|
||||||
|
var iter = parsed.asReversed().listIterator()
|
||||||
|
println(parsed.asReversed())
|
||||||
|
while (true) {
|
||||||
|
var startOfGroup = iter.next()
|
||||||
|
while (startOfGroup == null) {
|
||||||
|
startOfGroup = iter.next()
|
||||||
|
}
|
||||||
|
println(iter.nextIndex() - 1)
|
||||||
|
var endOfGroup = iter.next()
|
||||||
|
while (endOfGroup == startOfGroup) {
|
||||||
|
endOfGroup = iter.next()
|
||||||
|
}
|
||||||
|
iter.previous()
|
||||||
|
println(iter.nextIndex() - 1)
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
var emptySpaceBuffer = 0
|
||||||
|
for (i in parsed.indices) {
|
||||||
|
if (parsed[i] == null) emptySpaceBuffer += 1
|
||||||
|
else emptySpaceBuffer = 0
|
||||||
|
|
||||||
|
// if (emptySpaceBuffer == )
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
"@cross/dir": "jsr:@cross/dir@^1.1.0",
|
"@cross/dir": "jsr:@cross/dir@^1.1.0",
|
||||||
"@db/sqlite": "jsr:@db/sqlite@^0.12.0",
|
"@db/sqlite": "jsr:@db/sqlite@^0.12.0",
|
||||||
"@std/ini": "jsr:@std/ini@^0.225.2",
|
"@std/ini": "jsr:@std/ini@^0.225.2",
|
||||||
|
"@std/io": "jsr:@std/io@^0.225.0",
|
||||||
"@std/path": "jsr:@std/path@^1.0.8"
|
"@std/path": "jsr:@std/path@^1.0.8"
|
||||||
}
|
}
|
||||||
}
|
}
|
27
deno.lock
27
deno.lock
|
@ -18,17 +18,21 @@
|
||||||
"jsr:@std/assert@0.217": "0.217.0",
|
"jsr:@std/assert@0.217": "0.217.0",
|
||||||
"jsr:@std/assert@0.221": "0.221.0",
|
"jsr:@std/assert@0.221": "0.221.0",
|
||||||
"jsr:@std/assert@~1.0.6": "1.0.9",
|
"jsr:@std/assert@~1.0.6": "1.0.9",
|
||||||
|
"jsr:@std/bytes@^1.0.2": "1.0.4",
|
||||||
"jsr:@std/encoding@0.221": "0.221.0",
|
"jsr:@std/encoding@0.221": "0.221.0",
|
||||||
"jsr:@std/encoding@~1.0.5": "1.0.5",
|
"jsr:@std/encoding@~1.0.5": "1.0.5",
|
||||||
"jsr:@std/fmt@0.221": "0.221.0",
|
"jsr:@std/fmt@0.221": "0.221.0",
|
||||||
"jsr:@std/fmt@~1.0.2": "1.0.3",
|
"jsr:@std/fmt@~1.0.2": "1.0.3",
|
||||||
"jsr:@std/fs@0.221": "0.221.0",
|
"jsr:@std/fs@0.221": "0.221.0",
|
||||||
"jsr:@std/ini@~0.225.2": "0.225.2",
|
"jsr:@std/ini@~0.225.2": "0.225.2",
|
||||||
|
"jsr:@std/io@*": "0.225.0",
|
||||||
|
"jsr:@std/io@0.225": "0.225.0",
|
||||||
"jsr:@std/path@0.217": "0.217.0",
|
"jsr:@std/path@0.217": "0.217.0",
|
||||||
"jsr:@std/path@0.221": "0.221.0",
|
"jsr:@std/path@0.221": "0.221.0",
|
||||||
"jsr:@std/path@^1.0.8": "1.0.8",
|
"jsr:@std/path@^1.0.8": "1.0.8",
|
||||||
"jsr:@std/path@~1.0.6": "1.0.8",
|
"jsr:@std/path@~1.0.6": "1.0.8",
|
||||||
"jsr:@std/text@~1.0.7": "1.0.8"
|
"jsr:@std/text@~1.0.7": "1.0.8",
|
||||||
|
"npm:@types/node@*": "22.5.4"
|
||||||
},
|
},
|
||||||
"jsr": {
|
"jsr": {
|
||||||
"@cliffy/ansi@1.0.0-rc.7": {
|
"@cliffy/ansi@1.0.0-rc.7": {
|
||||||
|
@ -130,6 +134,9 @@
|
||||||
"@std/assert@1.0.9": {
|
"@std/assert@1.0.9": {
|
||||||
"integrity": "a9f0c611a869cc791b26f523eec54c7e187aab7932c2c8e8bea0622d13680dcd"
|
"integrity": "a9f0c611a869cc791b26f523eec54c7e187aab7932c2c8e8bea0622d13680dcd"
|
||||||
},
|
},
|
||||||
|
"@std/bytes@1.0.4": {
|
||||||
|
"integrity": "11a0debe522707c95c7b7ef89b478c13fb1583a7cfb9a85674cd2cc2e3a28abc"
|
||||||
|
},
|
||||||
"@std/encoding@0.221.0": {
|
"@std/encoding@0.221.0": {
|
||||||
"integrity": "d1dd76ef0dc5d14088411e6dc1dede53bf8308c95d1537df1214c97137208e45"
|
"integrity": "d1dd76ef0dc5d14088411e6dc1dede53bf8308c95d1537df1214c97137208e45"
|
||||||
},
|
},
|
||||||
|
@ -152,6 +159,12 @@
|
||||||
"@std/ini@0.225.2": {
|
"@std/ini@0.225.2": {
|
||||||
"integrity": "c70f6560dacb7e333c2f868aa09aaa4117e6618cc66c60d0b4ca716c135c8e67"
|
"integrity": "c70f6560dacb7e333c2f868aa09aaa4117e6618cc66c60d0b4ca716c135c8e67"
|
||||||
},
|
},
|
||||||
|
"@std/io@0.225.0": {
|
||||||
|
"integrity": "c1db7c5e5a231629b32d64b9a53139445b2ca640d828c26bf23e1c55f8c079b3",
|
||||||
|
"dependencies": [
|
||||||
|
"jsr:@std/bytes"
|
||||||
|
]
|
||||||
|
},
|
||||||
"@std/path@0.217.0": {
|
"@std/path@0.217.0": {
|
||||||
"integrity": "1217cc25534bca9a2f672d7fe7c6f356e4027df400c0e85c0ef3e4343bc67d11",
|
"integrity": "1217cc25534bca9a2f672d7fe7c6f356e4027df400c0e85c0ef3e4343bc67d11",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
|
@ -171,6 +184,17 @@
|
||||||
"integrity": "40ba34caa095f393e78796e5eda37b8b4e2cc6cfd6f51f34658ad7487b1451e4"
|
"integrity": "40ba34caa095f393e78796e5eda37b8b4e2cc6cfd6f51f34658ad7487b1451e4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"npm": {
|
||||||
|
"@types/node@22.5.4": {
|
||||||
|
"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
|
||||||
|
"dependencies": [
|
||||||
|
"undici-types"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"undici-types@6.19.8": {
|
||||||
|
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="
|
||||||
|
}
|
||||||
|
},
|
||||||
"remote": {
|
"remote": {
|
||||||
"https://deno.land/x/sqlite@v3.9.1/build/sqlite.js": "2afc7875c7b9c85d89730c4a311ab3a304e5d1bf761fbadd8c07bbdf130f5f9b",
|
"https://deno.land/x/sqlite@v3.9.1/build/sqlite.js": "2afc7875c7b9c85d89730c4a311ab3a304e5d1bf761fbadd8c07bbdf130f5f9b",
|
||||||
"https://deno.land/x/sqlite@v3.9.1/build/vfs.js": "7f7778a9fe499cd10738d6e43867340b50b67d3e39142b0065acd51a84cd2e03",
|
"https://deno.land/x/sqlite@v3.9.1/build/vfs.js": "7f7778a9fe499cd10738d6e43867340b50b67d3e39142b0065acd51a84cd2e03",
|
||||||
|
@ -189,6 +213,7 @@
|
||||||
"jsr:@cross/dir@^1.1.0",
|
"jsr:@cross/dir@^1.1.0",
|
||||||
"jsr:@db/sqlite@0.12",
|
"jsr:@db/sqlite@0.12",
|
||||||
"jsr:@std/ini@~0.225.2",
|
"jsr:@std/ini@~0.225.2",
|
||||||
|
"jsr:@std/io@0.225",
|
||||||
"jsr:@std/path@^1.0.8"
|
"jsr:@std/path@^1.0.8"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||||
deno run -A ./main.ts $@
|
deno run -A ./cli/mod.ts $@
|
||||||
|
|
Loading…
Reference in a new issue