diff --git a/2024/04/2024-04.run.xml b/2024/04/2024-04.run.xml
new file mode 100644
index 0000000..9da70d1
--- /dev/null
+++ b/2024/04/2024-04.run.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/2024/04/index.test.ts b/2024/04/index.test.ts
new file mode 100644
index 0000000..9bab481
--- /dev/null
+++ b/2024/04/index.test.ts
@@ -0,0 +1,21 @@
+import { describe, expect, it } from 'bun:test';
+import { solveFirst, solveSecond } from './index.ts';
+
+describe('2024-04', () => {
+ const testInput = `MMMSXXMASM
+MSAMXMSMSA
+AMXSXMAAMM
+MSAMASMSMX
+XMASAMXAMM
+XXAMMXXAMA
+SMSMSASXSS
+SAXAMASAAA
+MAMMMXMMMM
+MXMXAXMASX`;
+ it('first', () => {
+ expect(solveFirst(testInput)).toBe(18);
+ });
+ it('second', () => {
+ expect(solveSecond(testInput)).toBe(9);
+ });
+});
diff --git a/2024/04/index.ts b/2024/04/index.ts
new file mode 100644
index 0000000..0886047
--- /dev/null
+++ b/2024/04/index.ts
@@ -0,0 +1,87 @@
+import { sumArray } from '../../utils/array.ts';
+
+export function solveFirst(input: string): number {
+ const regex = /XMAS/g;
+ const regexBackwards = /SAMX/g;
+
+ const cForwards = [...input.matchAll(regex)].length;
+ const cBackwards = [...input.matchAll(regexBackwards)].length;
+
+ const rows = input.split('\n');
+ const columns = rows.reduce((columns, row) => {
+ row
+ .split('')
+ .forEach((char, i) => (columns[i] = (columns[i] || '') + char));
+ return columns;
+ }, []);
+
+ const cColumns = sumArray(
+ columns.map(
+ (column) =>
+ [...column.matchAll(regex), ...column.matchAll(regexBackwards)].length
+ )
+ );
+
+ const rowCount = rows.length - 1;
+
+ const diagonals = rows.reduce((diagonals, row, rowIndex) => {
+ row
+ .split('')
+ .forEach(
+ (char, columnIndex) =>
+ (diagonals[columnIndex + (rowCount - rowIndex)] =
+ (diagonals[columnIndex + (rowCount - rowIndex)] || '') + char)
+ );
+ return diagonals;
+ }, []);
+
+ const diagonals2 = rows.reduce((diagonals, row, rowIndex) => {
+ row
+ .split('')
+ .forEach(
+ (char, columnIndex) =>
+ (diagonals[columnIndex + rowIndex] =
+ (diagonals[columnIndex + rowIndex] || '') + char)
+ );
+ return diagonals;
+ }, []);
+
+ const cDiagonals = sumArray(
+ diagonals.map(
+ (column) =>
+ [...column.matchAll(regex), ...column.matchAll(regexBackwards)].length
+ )
+ );
+
+ const cDiagonals2 = sumArray(
+ diagonals2.map(
+ (column) =>
+ [...column.matchAll(regex), ...column.matchAll(regexBackwards)].length
+ )
+ );
+
+ return cForwards + cBackwards + cColumns + cDiagonals + cDiagonals2;
+}
+
+export function solveSecond(input: string): number {
+ const rows = input.split('\n');
+
+ const rowCount = rows.length;
+ const columnCount = rows[0].length;
+
+ let found = 0;
+ for (let y = 0; y < rowCount - 2; y++) {
+ for (let x = 0; x < columnCount - 2; x++) {
+ const diag = rows[y][x] + rows[y + 1][x + 1] + rows[y + 2][x + 2];
+ const diag2 = rows[y + 2][x] + rows[y + 1][x + 1] + rows[y][x + 2];
+ if (
+ (diag === 'MAS' || diag === 'SAM') &&
+ (diag2 === 'MAS' || diag2 === 'SAM')
+ ) {
+ found++;
+ }
+ }
+ }
+
+ return found;
+}
diff --git a/2024/04/run.ts b/2024/04/run.ts
new file mode 100644
index 0000000..88783cd
--- /dev/null
+++ b/2024/04/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);