Skip to content

Commit 86f4d7d

Browse files
committed
04/2024
1 parent 8e93bb0 commit 86f4d7d

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed

2024/04/index.ts

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
import { getPuzzle } from "../../utils";
2+
3+
const puzzleInput = getPuzzle(__dirname).trim();
4+
5+
const wordsearch = puzzleInput.split("\n").map((row) => row.split(""));
6+
7+
const WIDTH = wordsearch[0].length;
8+
const HEIGHT = wordsearch.length;
9+
10+
const XMAS = "XMAS";
11+
12+
type Entries<T> = { [K in keyof T]: [K, T[K]] }[keyof T][];
13+
14+
type Coordinate = { x: number; y: number };
15+
16+
const DIRECTION_MAP = {
17+
N: { x: 0, y: -1 },
18+
NE: { x: 1, y: -1 },
19+
E: { x: 1, y: 0 },
20+
SE: { x: 1, y: 1 },
21+
S: { x: 0, y: 1 },
22+
SW: { x: -1, y: 1 },
23+
W: { x: -1, y: 0 },
24+
NW: { x: -1, y: -1 },
25+
} as const satisfies Record<string, Coordinate>;
26+
27+
const isOutOfBounds = (coord: Coordinate) => {
28+
return coord.x < 0 || coord.x >= WIDTH || coord.y < 0 || coord.y >= HEIGHT;
29+
};
30+
31+
// Part 1
32+
(() => {
33+
console.time("part 1");
34+
type QueueEntry = {
35+
chars: string[];
36+
direction: keyof typeof DIRECTION_MAP | undefined;
37+
position: Coordinate;
38+
};
39+
40+
let matches = 0;
41+
const queue: QueueEntry[] = [];
42+
43+
for (let y = 0; y < HEIGHT; y++) {
44+
for (let x = 0; x < WIDTH; x++) {
45+
const char = wordsearch[y][x];
46+
47+
if (char === XMAS[0]) {
48+
queue.push({
49+
chars: [char],
50+
direction: undefined,
51+
position: { x, y },
52+
});
53+
}
54+
}
55+
}
56+
57+
while (queue.length > 0) {
58+
const current = queue.shift();
59+
60+
if (current.chars.length === XMAS.length) {
61+
matches += 1;
62+
continue;
63+
}
64+
65+
const nextChar = XMAS[current.chars.length];
66+
67+
if (typeof current.direction === "undefined") {
68+
for (const element of Object.entries(DIRECTION_MAP) as Entries<
69+
typeof DIRECTION_MAP
70+
>) {
71+
const [key, direction] = element;
72+
const nextCell = {
73+
x: current.position.x + direction.x,
74+
y: current.position.y + direction.y,
75+
};
76+
77+
if (isOutOfBounds(nextCell)) {
78+
continue;
79+
}
80+
81+
if (wordsearch[nextCell.y][nextCell.x] === nextChar) {
82+
queue.push({
83+
chars: [...current.chars, nextChar],
84+
direction: key,
85+
position: nextCell,
86+
});
87+
}
88+
}
89+
} else {
90+
const direction = DIRECTION_MAP[current.direction];
91+
92+
const nextCell = {
93+
x: current.position.x + direction.x,
94+
y: current.position.y + direction.y,
95+
};
96+
97+
if (isOutOfBounds(nextCell)) {
98+
continue;
99+
}
100+
101+
if (wordsearch[nextCell.y][nextCell.x] === nextChar) {
102+
queue.push({
103+
chars: [...current.chars, nextChar],
104+
direction: current.direction,
105+
position: nextCell,
106+
});
107+
}
108+
}
109+
}
110+
111+
console.log("part 1 matches ::", matches);
112+
console.timeEnd("part 1");
113+
})();
114+
115+
// Part 2
116+
(() => {
117+
console.time("part 2");
118+
let matches = 0;
119+
const centerPoints: Coordinate[] = [];
120+
121+
for (let y = 0; y < HEIGHT; y++) {
122+
for (let x = 0; x < WIDTH; x++) {
123+
const char = wordsearch[y][x];
124+
125+
if (char === "A") {
126+
centerPoints.push({ x, y });
127+
}
128+
}
129+
}
130+
131+
for (let i = 0; i < centerPoints.length; i++) {
132+
const point = centerPoints[i];
133+
134+
const topRightCoord = {
135+
x: point.x + DIRECTION_MAP.NE.x,
136+
y: point.y + DIRECTION_MAP.NE.y,
137+
};
138+
139+
const bottomRightCoord = {
140+
x: point.x + DIRECTION_MAP.SE.x,
141+
y: point.y + DIRECTION_MAP.SE.y,
142+
};
143+
144+
const bottomLeftCoord = {
145+
x: point.x + DIRECTION_MAP.SW.x,
146+
y: point.y + DIRECTION_MAP.SW.y,
147+
};
148+
149+
const topLeftCoord = {
150+
x: point.x + DIRECTION_MAP.NW.x,
151+
y: point.y + DIRECTION_MAP.NW.y,
152+
};
153+
154+
if (
155+
isOutOfBounds(topRightCoord) ||
156+
isOutOfBounds(bottomRightCoord) ||
157+
isOutOfBounds(bottomLeftCoord) ||
158+
isOutOfBounds(topLeftCoord)
159+
) {
160+
continue;
161+
}
162+
163+
const topLeftChar = wordsearch[topLeftCoord.y][topLeftCoord.x];
164+
const bottomLeftChar = wordsearch[bottomLeftCoord.y][bottomLeftCoord.x];
165+
const bottomRightChar = wordsearch[bottomRightCoord.y][bottomRightCoord.x];
166+
const topRightChar = wordsearch[topRightCoord.y][topRightCoord.x];
167+
168+
if (
169+
((topLeftChar === "M" && bottomRightChar === "S") ||
170+
(topLeftChar === "S" && bottomRightChar === "M")) &&
171+
((bottomLeftChar === "M" && topRightChar === "S") ||
172+
(bottomLeftChar === "S" && topRightChar === "M"))
173+
) {
174+
matches += 1;
175+
}
176+
}
177+
178+
console.log("part 2 matches ::", matches);
179+
console.timeEnd("part 2");
180+
})();

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
| Day | Part 1 | Part 2 |
88
| :---------------------------------------: | :----: | :----: |
9+
| [04](https://adventofcode.com/2024/day/4) ||  ✅ |
910
| [03](https://adventofcode.com/2024/day/3) ||  ✅ |
1011
| [02](https://adventofcode.com/2024/day/2) ||  ✅ |
1112
| [01](https://adventofcode.com/2024/day/1) ||  ✅ |

0 commit comments

Comments
 (0)