This is a repository containing my solutions for Advent of Code. I generally use Advent of Code as an opportunity to get experience with a language that I don’t yet know well. You’re welcome to take inspiration from the way I solved a problem, but please don’t assume that I’ve solved it in the most idiomatic, efficient, or readable way.
This code is made available under an MIT-style license. I do not accept pull requests to this repo, but you’re welcome to point out anything you think could be done better.
| Year | Language | Thoughts |
|---|---|---|
| 2020 | Kotlin | Blog post |
| 2021 | Raku plus Go when Raku was too slow | Blog post |
| 2022 | Elixir | Blog post |
| 2023 | Julia | |
| 2024 | PostScript | |
| 2025 | Glue languages that might already be installed on your system: AWK, dc, gvpr (Graphviz), Jsonnet, jq, Lua, sed, SQL/MM, zsh plus Go and Ruby to explore some puzzles | Blog post |
When solving Advent puzzles as they come out, I also post code and commentary to u/adventofcode Solutions Megathreads on Reddit. See all my solutions comments.
Each year has its own directory and the solution for each day is in its own
subdirectory, e.g. 2025/day10. Each day’s solution is a standalone program
which solves the puzzle for each input file on the command line, or from
standard input if no file is given. Each day directory has the solution code
in one or more languages, named with the language’s source file convention,
e.g. day1.exs and Day1.jl. Some languages have a symlink to a runner
library or a Makefile.
Each day also has a file named input.example.txt with an example taken from
the problem description and input.example.expected with two lines (prefixed
part1: and part2: ) with the expected output when the solution code is run
input.example.txt. If the problem description has multiple examples, they’ll
be in input.example2.txt and input.example2.expected, etc. If a solution
needs a little extra help, run will look for a dayXlang file, e.g.
day1sed would pass each file individually to the day1.sed script and
perhaps transform the output by adding all the numbers produced by the script.
The input.actual.txt and input.actual.expected files are symlinks into a
non-public directory with input specific to my AoC account. If you’d like to
test my code against your personal input, provide the path to your input as a
command line argument: day1/day1.exs -v path/to/my/input.txt. If you’d like
to run a whole suite of solutions against your input, make sure they’re in a
directory with numbered subdirectories, e.g. 1/input.actual.txt and replace
my input directory with a symlink to yours, e.g. rm 2023/input; ln -s path/to/my/2023 2023/input which will cause runday and testday scripts to
use yours. Alternatively, substitute the input submodule at the base of the
repository, as in the next section.
Except for a few one-off programs, each language has a “runner” library and a
template generator script. The runner library generally handles reading each
input into an array of strings (one per line) and calling part1 and part2
functions passed by the program. Runners and generators are in the
lang directory. The runner library also looks for a file with
expected results and compares the puzzle solutions to the expected values,
exiting the program with a non-zero status code if the program returned the
wrong value. With the -v (and sometimes --verbose) option as the first
argument, the runner also prints start and end messages with input line count,
timing, and an eye-catching line with the program’s outcome.
General-purpose scripts are in the top-level directory. They were originally copied with minor modifications into each year’s directory; some are still lying around. In addition to running and testing, some scripts automate day-of-coding activities.
- The
runscript ensures the runner library is present, though most programs can be run directly, or built bymake -C year/dayX. - The
rundayscript runs all the solution programs in a directory on both the example and actual inputs, teeing output to theoutsubdirectory for post hoc debugging. - The
watchscript callsrundayevery time a substantive file in a day directory changes; I run that in a terminal while iterating on a solution in a Vim window. - The
testdayscript which can run one or more days, compare output with expected, and report results using the Test Anything Protocol format. fetchinputusescurlto download my input file for a day’s puzzle, taking care not to call the site before the puzzle is released; therundayscript is responsible for saving the output to a private directory (see below) and not requesting it if the file isn’t empty (always cache your inputs, be nice to Eric’s servers!). One failure mode of this approach is if there’s an error writing the input file, e.g. a symlink pointing to nowhere. This will likely result in a read error being displayed or the solution program crashing, so fix the directory structure beforewatchpicks up a lot more changes. The other failure mode is expired cookies (stored in.cookie-jar). I’ve found that cookies are often rejected well before the expiration date in the initial HTTP response, so ifinput.actual.txthas 0 lines after midnight, go to adventofcode.com and inspect your request to grab a fresh cookie.cachepuzzledownloads a copy of the puzzle HTML and saves it indayX/puzzle/puzzle.htmlfor offline browsing and grepping. Thepuzzledirectory is in.gitignore: don’t redistribute puzzle textextractexampleslooks for<pre>formatted text in the puzzle.html file and saves it toinput.example.txt(and example2 etc), which will be automatically used as test input. This isn’t a perfect system; there are sometimes<pre>blocks showing what happens to the input after some operation, so delete any input files that aren’t good test cases.formegathreadformats the code for a solution appropriate for a Solutions Megathread comment. In particular, this uses four-space markdown indent (not a backtick fence) for code formatting and removes imports, comment lines, and boilerplate code. It also inserts a title with the language name and a link to the file on my GitHub repository. The resulting file can be used in a text editor to compose a solutions comment that’s more likely to be short enough and not annoy u/daggerdragon for not following the rules.
Eric Wastl asks that you don’t include your personal input in public source code repositories. If you’d like to use git submodules to keep your input files under source control and available wherever you check out the repository with your code, follow these steps:
-
Create a new GitHub repository and select the
Privateradio button. Give it a name likeadventofcode-input. -
Git submodules don’t work with an empty repository, so make sure to create at least one file in this new repository, such as
.gitignoreorREADME.md. -
In your main Advent of Code repository, add the new repository as a submodule:
git submodule add https://github.com/YourUserName/adventofcode-input input. This will check out the new repo into a directory namedinput. -
Pro tip: To use the same transport (HTTPS or SSH) for the submodule as the main repository, use a relative URL like
../adventofcode-input.git. -
If you run
git statusit will show two new staged entries:.gitmodulesandinput. If you rungit diff --stagedyou’ll see the content of the new.gitmodulesfile (this keeps track of your submodules and their remote URL) and the latest git commit in that repo:diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..7904cad --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "input"] + path = input + url = https://github.com/flwyd/adventofcode-input diff --git a/input b/input new file mode 160000 index 0000000..7fde85f --- /dev/null +++ b/input @@ -0,0 +1 @@ +Subproject commit 7fde85fc0dc5d6334645ebd10978e8755aab4de2 -
Run
git committo add this new submodule to your main repository’s history. -
Run
git push origin mainto upload your main repository to GitHub. Since theinputsubmodule is private, other people won’t be able to see the files inside that directory when they browse github.com or check out your public repository. -
When you check out your main repository on a different computer, you need to initialize the submodule. The easy way to do this is when cloning:
git clone --recurse-submodules https://github.com/YourUserName/adventofcodeIf it’s already checked out, rungit submodule init && git submodule updateGit may prompt you for your GitHub credentials when checking out the submodule if your credentials aren’t cached -
Although the submodule is really a separate git repository, locally it looks like a single file tree. As you work on AoC problems, put your personal input files in the submodule, e.g. in
input/2023/1/input.actual.txt. Your code can read the input files using a relative path, or you can create a symlink from your solution directory:cd solutions2023; ln -s ../input/2023/1/input.actual.txt day1/ -
When your solution’s done it now takes two steps to upload to GitHub. First, commit the input file in your submodule:
cd input git add 2023/1 git commit -m "2023 day 1 input file" git push origin main
Then commit the code and other files in your main repository, as well as the pointer to the new head commit of your input repository:
cd .. git add input git add 2023/day1/* git commit -m "Solution to 2023 day 1" git push origin main
-
If you’d like to check out someone’s Advent of Code solutions repository but use your own input files, you can swap out the submodule URL:
git config submodule.input.url https://github.com/SomwhereElse/aoc-inputand then rungit submodule init && git submodule update.