advent of code 2025 in ts and nix
1const file = await Bun.file("../../shared/09/input.txt").text();
2
3interface Point {
4 x: number;
5 y: number;
6}
7
8// Parse tile coordinates
9const tiles: Point[] = file
10 .trim()
11 .split("\n")
12 .map((line) => {
13 const [x, y] = line.split(",").map(Number);
14 return { x, y };
15 });
16
17// Part 1: Any rectangle with red corners
18function part1(points: Point[]): number {
19 let largestRectangleSize = 0;
20 for (let pointAIndex = 0; pointAIndex < points.length; pointAIndex++) {
21 const pointA = points[pointAIndex];
22 for (
23 let pointBIndex = pointAIndex + 1;
24 pointBIndex < points.length;
25 pointBIndex++
26 ) {
27 const pointB = points[pointBIndex];
28 const rectangleSize =
29 (Math.abs(pointB.x - pointA.x) + 1) *
30 (Math.abs(pointB.y - pointA.y) + 1);
31 if (rectangleSize > largestRectangleSize) {
32 largestRectangleSize = rectangleSize;
33 }
34 }
35 }
36 return largestRectangleSize;
37}
38
39// Part 2: Rectangle must only contain red or green tiles
40function part2(_points: Point[]): number {
41 const minX = Math.min(..._points.map((p) => p.x)) - 1;
42 const minY = Math.min(..._points.map((p) => p.y)) - 1;
43 const __points = _points.map((p) => ({ x: p.x - minX, y: p.y - minY }));
44
45 const xs = __points
46 .map((p) => p.x)
47 .toSorted((a, b) => a - b)
48 .filter((_, i) => i % 2 === 0);
49 const ys = __points
50 .map((p) => p.y)
51 .toSorted((a, b) => a - b)
52 .filter((_, i) => i % 2 === 0);
53 const points = __points.map((p) => ({
54 x: 1 + xs.indexOf(p.x) * 2,
55 y: 1 + ys.indexOf(p.y) * 2,
56 }));
57
58 const grid: number[][] = [];
59 const width = Math.max(...points.map((p) => p.x)) + 1;
60 const height = Math.max(...points.map((p) => p.y)) + 1;
61
62 for (let y = 0; y <= height; y++) {
63 grid[y] = [];
64 for (let x = 0; x <= width; x++) {
65 grid[y][x] = 0;
66 }
67 }
68
69 points.forEach((p, pIndex) => {
70 grid[p.y][p.x] = 1;
71 const nextPoint = points[(pIndex + 1) % points.length];
72 const deltaX = Math.sign(nextPoint.x - p.x);
73 const deltaY = Math.sign(nextPoint.y - p.y);
74 if (deltaX !== 0) {
75 let currentX = p.x + deltaX;
76 while (currentX !== nextPoint.x) {
77 if (grid[p.y][currentX] === 0) {
78 grid[p.y][currentX] = 2;
79 }
80 currentX += deltaX;
81 }
82 }
83 if (deltaY !== 0) {
84 let currentY = p.y + deltaY;
85 while (currentY !== nextPoint.y) {
86 if (grid[currentY][p.x] === 0) {
87 grid[currentY][p.x] = 2;
88 }
89 currentY += deltaY;
90 }
91 }
92 });
93
94 // Flood fill all cells with -1 that are 0 and connected to the border
95 let open = [{ x: 0, y: 0 }];
96 const floodFill = (x: number, y: number) => {
97 if (x < 0 || x > width || y < 0 || y > height) {
98 return;
99 }
100 if (grid[y][x] !== 0) {
101 return;
102 }
103 grid[y][x] = -1;
104 const add = (nx: number, ny: number) => {
105 if (nx < 0 || nx > width || ny < 0 || ny > height) {
106 return;
107 }
108 if (grid[ny][nx] !== 0) {
109 return;
110 }
111 open.push({ x: nx, y: ny });
112 };
113 add(x + 1, y);
114 add(x - 1, y);
115 add(x, y + 1);
116 add(x, y - 1);
117 };
118 while (open.length > 0) {
119 const point = open.pop()!;
120 floodFill(point.x, point.y);
121 }
122
123 const hasOnlyValidPoints = (pointA: Point, pointB: Point): boolean => {
124 for (
125 let y = Math.min(pointA.y, pointB.y);
126 y <= Math.max(pointA.y, pointB.y);
127 y++
128 ) {
129 for (
130 let x = Math.min(pointA.x, pointB.x);
131 x <= Math.max(pointA.x, pointB.x);
132 x++
133 ) {
134 if (grid[y][x] < 0) {
135 return false;
136 }
137 }
138 }
139 return true;
140 };
141
142 let largestRectangleSize = 0;
143 for (let pointAIndex = 0; pointAIndex < points.length; pointAIndex++) {
144 for (
145 let pointBIndex = pointAIndex + 1;
146 pointBIndex < points.length;
147 pointBIndex++
148 ) {
149 const pointA = _points[pointAIndex];
150 const pointB = _points[pointBIndex];
151 const rectangleSize =
152 (Math.abs(pointB.x - pointA.x) + 1) *
153 (Math.abs(pointB.y - pointA.y) + 1);
154 if (
155 rectangleSize > largestRectangleSize &&
156 hasOnlyValidPoints(points[pointAIndex], points[pointBIndex])
157 ) {
158 largestRectangleSize = rectangleSize;
159 }
160 }
161 }
162 return largestRectangleSize;
163}
164
165console.log("part 1:", part1(tiles));
166console.log("part 2:", part2(tiles));