advent of code 2025 in ts and nix
1#!/usr/bin/env bun
2
3import { mkdir, readdir, rm } from "node:fs/promises";
4import { join } from "node:path";
5import { $ } from "bun";
6
7const OUTPUT_DIR = join(import.meta.dir, "../output");
8const VIS_DIR = join(import.meta.dir, "../vis");
9
10console.log("🎨 Generating all visualizations...\n");
11
12// Clean output directory
13await rm(OUTPUT_DIR, { recursive: true, force: true });
14await mkdir(OUTPUT_DIR, { recursive: true });
15
16// Auto-detect days from vis directory
17const entries = await readdir(VIS_DIR, { withFileTypes: true });
18const days = entries
19 .filter((entry) => entry.isDirectory() && /^\d{2}$/.test(entry.name))
20 .map((entry) => entry.name)
21 .sort();
22
23// Generate each day's visualization
24for (const day of days) {
25 console.log(`📊 Day ${day}...`);
26 const dayDir = join(VIS_DIR, day);
27 const generateScript = join(dayDir, "generate.ts");
28
29 // Run the generator
30 await $`cd ${dayDir} && bun ${generateScript}`;
31
32 // Copy the output to the output directory
33 const outputDayDir = join(OUTPUT_DIR, day);
34 await mkdir(outputDayDir, { recursive: true });
35 await $`cp ${join(dayDir, "index.html")} ${outputDayDir}/index.html`;
36
37 console.log(` ✓ Generated ${day}`);
38}
39
40// Generate index page
41console.log("\n📄 Generating index page...");
42
43// Day metadata
44const dayInfo: Record<string, { title: string; description: string }> = {
45 "04": {
46 title: "Paper Removal",
47 description:
48 "Watch papers being removed layer by layer from a grid. Papers with fewer than 4 neighbors (including diagonals) are accessible and removed each iteration.",
49 },
50 "06": {
51 title: "Cephalopod Math",
52 description:
53 "Learn to read numbers like a cephalopod! Part 1 reads numbers vertically down columns, Part 2 reads digits column-by-column from right to left.",
54 },
55 "07": {
56 title: "Tachyon Beam Splitting",
57 description:
58 "Watch tachyon beams split as they travel through a manifold. Each splitter (^) stops a beam and creates two new beams extending left and right.",
59 },
60 "08": {
61 title: "Playground Junction Boxes",
62 description:
63 "3D visualization of junction boxes being connected by their shortest distances. Watch circuits form as connections merge isolated boxes into larger groups.",
64 },
65};
66
67const dayCards = days
68 .map((day) => {
69 const info = dayInfo[day];
70 if (!info) return "";
71
72 return ` <li>
73 <a href="${day}/index.html" class="day-item">
74 <span class="day-number">Day ${parseInt(day, 10)}:</span>
75 <span class="day-title">${info.title}</span>
76 <span class="stars"> **</span>
77 </a>
78 <div class="day-description">${info.description}</div>
79 </li>`;
80 })
81 .join("\n");
82
83const indexHtml = `<!DOCTYPE html>
84<html lang="en">
85<head>
86 <meta charset="UTF-8">
87 <meta name="viewport" content="width=device-width, initial-scale=1.0">
88 <title>Advent of Code 2025 - Visualizations</title>
89 <style>
90 body {
91 background: #1e1e2e;
92 color: #cdd6f4;
93 font-family: "Source Code Pro", monospace;
94 font-size: 14pt;
95 font-weight: 300;
96 padding: 1rem;
97 }
98 a {
99 text-decoration: none;
100 color: #a6e3a1;
101 outline: 0;
102 }
103 a:hover, a:focus {
104 background-color: #181825 !important;
105 }
106 h1, h2 {
107 font-size: 1em;
108 font-weight: normal;
109 }
110 header {
111 white-space: nowrap;
112 margin-bottom: 2em;
113 }
114 header h1 {
115 display: inline-block;
116 margin: 0;
117 padding-right: 1em;
118 }
119 header h1 span {
120 color: #a6e3a1;
121 text-shadow: 0 0 2px #a6e3a1, 0 0 5px #a6e3a1;
122 }
123 main {
124 width: 60em;
125 margin: 0 auto;
126 min-height: 76vh;
127 }
128 article {
129 margin-bottom: 2em;
130 }
131 article h2 {
132 color: #cdd6f4;
133 margin-top: 1em;
134 margin-bottom: 1em;
135 }
136 .days-list {
137 list-style-type: none;
138 padding: 0;
139 }
140 .day-item {
141 display: block;
142 padding: 0.5em 0;
143 color: inherit;
144 }
145 .day-item:hover, .day-item:focus {
146 background-color: #181825 !important;
147 }
148 .day-number {
149 color: #a6adc8;
150 }
151 .day-title {
152 color: #cdd6f4;
153 }
154 .stars {
155 color: #f9e2af;
156 }
157 .day-description {
158 color: #a6adc8;
159 padding-left: 2.5em;
160 }
161 footer {
162 margin-top: 3em;
163 color: #a6adc8;
164 text-align: center;
165 font-size: 12px;
166 }
167 </style>
168</head>
169<body>
170 <header>
171 <h1><span>Advent of Code</span> 2025 - Visualizations</h1>
172 </header>
173
174 <main>
175 <article>
176 <h2>Interactive Problem Visualizations</h2>
177 <ul class="days-list">
178${dayCards}
179 </ul>
180 </article>
181
182 </main>
183
184 <footer>
185 Made with ♥ by <a href="https://dunkirk.sh">Kieran Klukas</a>
186 <br>
187 <a href="https://adventofcode.com/2025">[Return to Advent of Code]</a>
188 </footer>
189</body>
190</html>`;
191
192await Bun.write(join(OUTPUT_DIR, "index.html"), indexHtml);
193
194console.log("\n✨ All visualizations generated successfully!");
195console.log(`📁 Output directory: ${OUTPUT_DIR}`);
196console.log(`🌐 Open ${join(OUTPUT_DIR, "index.html")} to view\n`);