diff --git a/2024/16/2024-16.run.xml b/2024/16/2024-16.run.xml
new file mode 100644
index 0000000..33524e2
--- /dev/null
+++ b/2024/16/2024-16.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/2024/16/index.test.ts b/2024/16/index.test.ts
new file mode 100644
index 0000000..f891aac
--- /dev/null
+++ b/2024/16/index.test.ts
@@ -0,0 +1,95 @@
+import { describe, expect, it } from 'bun:test';
+import { solveFirst, solveSecond } from './index.ts';
+
+describe('2024-16', () => {
+ it('first', () => {
+ const testInput = `###############
+#.......#....E#
+#.#.###.#.###.#
+#.....#.#...#.#
+#.###.#####.#.#
+#.#.#.......#.#
+#.#.#####.###.#
+#...........#.#
+###.#.#####.#.#
+#...#.....#.#.#
+#.#.#.###.#.#.#
+#.....#...#.#.#
+#.###.#.#.#.#.#
+#S..#.....#...#
+###############`;
+ if (process.env.NODE_ENV === 'production') {
+ expect(solveFirst(testInput)).toBe(7036);
+ expect(
+ solveFirst(`#################
+#...#...#...#..E#
+#.#.#.#.#.#.#.#.#
+#.#.#.#...#...#.#
+#.#.#.#.###.#.#.#
+#...#.#.#.....#.#
+#.#.#.#.#.#####.#
+#.#...#.#.#.....#
+#.#.#####.#.###.#
+#.#.#.......#...#
+#.#.###.#####.###
+#.#.#...#.....#.#
+#.#.#.#####.###.#
+#.#.#.........#.#
+#.#.#.#########.#
+#S#.............#
+#################`)
+ ).toBe(11048);
+ expect(
+ solveFirst(`####################################################
+#......................................#..........E#
+#......................................#...........#
+#....................#.................#...........#
+#....................#.................#...........#
+#....................#.................#...........#
+#....................#.................#...........#
+#....................#.................#...........#
+#....................#.................#...........#
+#....................#.................#...........#
+#....................#.................#...........#
+#....................#.............................#
+#S...................#.............................#
+####################################################`)
+ ).toBe(5078);
+ expect(
+ solveFirst(`###########################
+#######################..E#
+######################..#.#
+#####################..##.#
+####################..###.#
+###################..##...#
+##################..###.###
+#################..####...#
+################..#######.#
+###############..##.......#
+##############..###.#######
+#############..####.......#
+############..###########.#
+###########..##...........#
+##########..###.###########
+#########..####...........#
+########..###############.#
+#######..##...............#
+######..###.###############
+#####..####...............#
+####..###################.#
+###..##...................#
+##..###.###################
+#..####...................#
+#.#######################.#
+#S........................#
+###########################`)
+ ).toBe(21148);
+ }
+ });
+ it('second', () => {
+ // TODO: add second input
+ const testInput = ``;
+ // TODO: add second solution
+ expect(solveSecond(testInput)).toBe(0);
+ });
+});
diff --git a/2024/16/index.ts b/2024/16/index.ts
new file mode 100644
index 0000000..3c71adf
--- /dev/null
+++ b/2024/16/index.ts
@@ -0,0 +1,43 @@
+import { findCoordsOfDigit } from '../../utils/coordinates.ts';
+import { PriorityQueue } from '../../utils/queue.ts';
+
+export function solveFirst(input: string): number {
+ const map = input.split('\n');
+ const [x, y] = findCoordsOfDigit(map, 'S')[0];
+ const start = { x, y, score: 0, dx: 1, dy: 0 };
+ const possibleOptions = new PriorityQueue();
+
+ possibleOptions.enqueue(start, 0);
+ const visited = new Set<`${number}:${number}:${number}:${number}`>();
+ visited.add(`${start.x}:${start.y}:${start.dx}:${start.dy}`);
+
+ while (possibleOptions.size() > 0) {
+ const el = possibleOptions.dequeue()!!;
+ visited.add(`${el.node.x}:${el.node.y}:${el.node.dx}:${el.node.dy}`);
+ if (map[el.node.y][el.node.x] === 'E') {
+ return el.node.score;
+ }
+ for (let [nx, ny, score, ndx, ndy] of [
+ [
+ el.node.x + el.node.dx,
+ el.node.y + el.node.dy,
+ el.node.score + 1,
+ el.node.dx,
+ el.node.dy
+ ],
+ [el.node.x, el.node.y, el.node.score + 1000, el.node.dy, -el.node.dx],
+ [el.node.x, el.node.y, el.node.score + 1000, -el.node.dy, el.node.dx]
+ ]) {
+ if (map[ny][nx] === '#') continue;
+ if (visited.has(`${nx}:${ny}:${ndx}:${ndy}`)) continue;
+ possibleOptions.enqueue({ x: nx, y: ny, score, dx: ndx, dy: ndy }, score);
+ }
+ }
+ return 0;
+}
+
+export function solveSecond(input: string): number {
+ // TODO: add code
+
+ return 0;
+}
diff --git a/2024/16/run.ts b/2024/16/run.ts
new file mode 100644
index 0000000..88783cd
--- /dev/null
+++ b/2024/16/run.ts
@@ -0,0 +1,12 @@
+import { join } from 'path';
+import { solveFirst, solveSecond } from './index.ts';
+
+const input = await Bun.file(join(__dirname, 'input.txt')).text();
+
+const firstAnswer = solveFirst(input);
+
+console.log(firstAnswer);
+
+const secondAnswer = solveSecond(input);
+
+console.log(secondAnswer);
diff --git a/utils/queue.ts b/utils/queue.ts
new file mode 100644
index 0000000..fcf969b
--- /dev/null
+++ b/utils/queue.ts
@@ -0,0 +1,78 @@
+export class PriorityQueue {
+ private readonly values: { node: T; priority: number }[] = [];
+
+ enqueue(node: T, priority: number) {
+ this.values.push({ node, priority });
+ this._bubbleUp();
+ }
+
+ dequeue() {
+ if (this.values.length === 0) return null;
+
+ const min = this.values[0];
+ const end = this.values.pop();
+
+ if (this.values.length > 0) {
+ this.values[0] = end!;
+ this._sinkDown();
+ }
+
+ return min;
+ }
+
+ size() {
+ return this.values.length;
+ }
+
+ _bubbleUp() {
+ let idx = this.values.length - 1;
+ const element = this.values[idx];
+
+ while (idx > 0) {
+ const parentIdx = Math.floor((idx - 1) / 2);
+ const parent = this.values[parentIdx];
+
+ if (element.priority >= parent.priority) break;
+
+ this.values[parentIdx] = element;
+ this.values[idx] = parent;
+ idx = parentIdx;
+ }
+ }
+
+ _sinkDown() {
+ let idx = 0;
+ const length = this.values.length;
+ const element = this.values[0];
+
+ while (true) {
+ const leftChildIdx = 2 * idx + 1;
+ const rightChildIdx = 2 * idx + 2;
+ let leftChild, rightChild;
+ let swap = null;
+
+ if (leftChildIdx < length) {
+ leftChild = this.values[leftChildIdx];
+ if (leftChild.priority < element.priority) {
+ swap = leftChildIdx;
+ }
+ }
+
+ if (rightChildIdx < length) {
+ rightChild = this.values[rightChildIdx];
+ if (
+ (swap === null && rightChild.priority < element.priority) ||
+ (swap !== null && rightChild.priority < leftChild!.priority)
+ ) {
+ swap = rightChildIdx;
+ }
+ }
+
+ if (swap === null) break;
+
+ this.values[idx] = this.values[swap];
+ this.values[swap] = element;
+ idx = swap;
+ }
+ }
+}