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