diff --git a/2024/08/2024-08.run.xml b/2024/08/2024-08.run.xml
new file mode 100644
index 0000000..4c31942
--- /dev/null
+++ b/2024/08/2024-08.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/2024/08/index.test.ts b/2024/08/index.test.ts
new file mode 100644
index 0000000..c5d859d
--- /dev/null
+++ b/2024/08/index.test.ts
@@ -0,0 +1,34 @@
+import { describe, expect, it } from 'bun:test';
+import { solveFirst, solveSecond } from './index.ts';
+
+describe('2024-08', () => {
+ const testInput = `............
+........0...
+.....0......
+.......0....
+....0.......
+......A.....
+............
+............
+........A...
+.........A..
+............
+............`;
+ it('first', () => {
+ expect(solveFirst(testInput)).toBe(14);
+ });
+ it('second', () => {
+ const secondTest = `T.........
+...T......
+.T........
+..........
+..........
+..........
+..........
+..........
+..........
+..........`;
+ expect(solveSecond(testInput)).toBe(34);
+ expect(solveSecond(secondTest)).toBe(9);
+ });
+});
diff --git a/2024/08/index.ts b/2024/08/index.ts
new file mode 100644
index 0000000..1d7b89c
--- /dev/null
+++ b/2024/08/index.ts
@@ -0,0 +1,81 @@
+import { combinations, unique } from '../../utils/array.ts';
+import { inBounds, isCoordinateEqual } from '../../utils/coordinates.ts';
+
+export function solveFirst(input: string): number {
+ const rows = input.split('\n');
+
+ const antennas = new Map();
+
+ for (let y = 0; y < rows.length; y++) {
+ for (let x = 0; x < rows[y].length; x++) {
+ const digit = rows[y][x];
+ if (digit === '.') {
+ continue;
+ }
+ const entry = antennas.get(digit) || [];
+ entry.push([x, y]);
+ antennas.set(digit, entry);
+ }
+ }
+ const antiNodes: [number, number][] = [];
+
+ // console.log(rows);
+ // console.log(antennas);
+ antennas.forEach((positions) => {
+ combinations(positions).forEach(({ a, b }) =>
+ antiNodes.push([a[0] - (b[0] - a[0]), a[1] - (b[1] - a[1])])
+ );
+ });
+
+ const validAntiNodes = antiNodes
+ .filter(inBounds(rows[0].length, rows.length))
+ .filter(unique(isCoordinateEqual));
+
+ // validAntiNodes.forEach(([x, y]) => rows[y][x] === '.' && replaceChar(rows, x, y, '#'));
+ //
+ // console.log(rows.join('\n'));
+ return validAntiNodes.length;
+}
+
+export function solveSecond(input: string): number {
+ const rows = input.split('\n');
+
+ const antennas = new Map();
+
+ const yMax = rows.length;
+ const xMax = rows[0].length;
+
+ for (let y = 0; y < yMax; y++) {
+ for (let x = 0; x < xMax; x++) {
+ const digit = rows[y][x];
+ if (digit === '.') {
+ continue;
+ }
+ const entry = antennas.get(digit) || [];
+ entry.push([x, y]);
+ antennas.set(digit, entry);
+ }
+ }
+ const antiNodes: [number, number][] = [];
+
+ antennas.forEach((positions) => {
+ combinations(positions).forEach(({ a, b }) => {
+ for (let y = 0; y < yMax; y++) {
+ antiNodes.push([a[0] + (b[0] - a[0]) * y, a[1] + (b[1] - a[1]) * y]);
+ }
+ });
+ });
+
+ const validAntiNodes = antiNodes
+ .filter(inBounds(rows[0].length, rows.length))
+ .filter(unique(isCoordinateEqual));
+
+ // console.log(validAntiNodes);
+ // validAntiNodes.forEach(
+ // ([x, y]) => rows[y][x] === '.' && replaceChar(rows, x, y, '#')
+ // );
+ //
+ // console.log(rows.join('\n'));
+
+ return validAntiNodes.length;
+}
diff --git a/2024/08/run.ts b/2024/08/run.ts
new file mode 100644
index 0000000..88783cd
--- /dev/null
+++ b/2024/08/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);