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};
61
62const dayCards = days
63 .map((day) => {
64 const info = dayInfo[day];
65 if (!info) return "";
66
67 return ` <li>
68 <a href="${day}/index.html" class="day-item">
69 <span class="day-number">Day ${parseInt(day, 10)}:</span>
70 <span class="day-title">${info.title}</span>
71 <span class="stars"> **</span>
72 </a>
73 <div class="day-description">${info.description}</div>
74 </li>`;
75 })
76 .join("\n");
77
78const indexHtml = `<!DOCTYPE html>
79<html lang="en">
80<head>
81 <meta charset="UTF-8">
82 <meta name="viewport" content="width=device-width, initial-scale=1.0">
83 <title>Advent of Code 2025 - Visualizations</title>
84 <style>
85 body {
86 background: #1e1e2e;
87 color: #cdd6f4;
88 font-family: "Source Code Pro", monospace;
89 font-size: 14pt;
90 font-weight: 300;
91 padding: 1rem;
92 }
93 a {
94 text-decoration: none;
95 color: #a6e3a1;
96 outline: 0;
97 }
98 a:hover, a:focus {
99 background-color: #181825 !important;
100 }
101 h1, h2 {
102 font-size: 1em;
103 font-weight: normal;
104 }
105 header {
106 white-space: nowrap;
107 margin-bottom: 2em;
108 }
109 header h1 {
110 display: inline-block;
111 margin: 0;
112 padding-right: 1em;
113 }
114 header h1 span {
115 color: #a6e3a1;
116 text-shadow: 0 0 2px #a6e3a1, 0 0 5px #a6e3a1;
117 }
118 main {
119 width: 60em;
120 margin: 0 auto;
121 min-height: 76vh;
122 }
123 article {
124 margin-bottom: 2em;
125 }
126 article h2 {
127 color: #cdd6f4;
128 margin-top: 1em;
129 margin-bottom: 1em;
130 }
131 .days-list {
132 list-style-type: none;
133 padding: 0;
134 }
135 .day-item {
136 display: block;
137 padding: 0.5em 0;
138 color: inherit;
139 }
140 .day-item:hover, .day-item:focus {
141 background-color: #181825 !important;
142 }
143 .day-number {
144 color: #a6adc8;
145 }
146 .day-title {
147 color: #cdd6f4;
148 }
149 .stars {
150 color: #f9e2af;
151 }
152 .day-description {
153 color: #a6adc8;
154 padding-left: 2.5em;
155 }
156 footer {
157 margin-top: 3em;
158 color: #a6adc8;
159 text-align: center;
160 font-size: 12px;
161 }
162 </style>
163</head>
164<body>
165 <header>
166 <h1><span>Advent of Code</span> 2025 - Visualizations</h1>
167 </header>
168
169 <main>
170 <article>
171 <h2>Interactive Problem Visualizations</h2>
172 <ul class="days-list">
173${dayCards}
174 </ul>
175 </article>
176
177 </main>
178
179 <footer>
180 Made with ♥ by <a href="https://dunkirk.sh">Kieran Klukas</a>
181 <br>
182 <a href="https://adventofcode.com/2025">[Return to Advent of Code]</a>
183 </footer>
184</body>
185</html>`;
186
187await Bun.write(join(OUTPUT_DIR, "index.html"), indexHtml);
188
189console.log("\n✨ All visualizations generated successfully!");
190console.log(`📁 Output directory: ${OUTPUT_DIR}`);
191console.log(`🌐 Open ${join(OUTPUT_DIR, "index.html")} to view\n`);