Solving Advent of Code 2025 puzzles in Go with a tiny, file-driven solver and a simple CLI powered by Cobra.
This repository reads inputs from files, runs the selected day/part solver(s), and writes results back to files. It also includes structured logging via Uber Zap.
- Go 1.25+
- macOS/Linux/Windows
- Run from source:
go run .
- Build a binary:
go build -o dist/app .and then run it with./dist/app
The CLI defaults to running all available days and parts, reading from ./data/inputs and writing to ./data/outputs.
For more information run go run . --help.
Examples:
-
Run everything (all days, all parts) using real inputs:
go run .- or
./dist/appif you built a binary
-
Run everything in demo mode (reads
demo-input.txt, writesdemo-output.txt):go run . --demo
-
Run a specific day (both parts):
go run . --day 1orgo run . -d1
-
Run a specific day and part:
go run . --day 2 --part 1orgo run . -d2 -p1
-
Use custom input/output directories:
go run . --inputs ./my-inputs --outputs ./my-outputs
-
Enable verbose debug logs:
DEBUG=1 go run . --day 1 --part 2
By default, the solver expects inputs and writes outputs in these locations:
- Real input for day N:
data/inputs/dayN/input.txt - Demo input for day N (when
--demo):data/inputs/dayN/demo-input.txt
Outputs are written automatically, creating folders if needed:
- Real output for day N, part P:
data/outputs/dayN/partP/output.txt - Demo output for day N, part P (when
--demo):data/outputs/dayN/partP/demo-output.txt
So make sure to create the appropriate folder structure and put your inputs in the data/inputs/dayN/ folders before
running the solver.
Project structure (short):
cmd/— Cobra root command and flagscode/— solvers, orchestrator, and I/O helpersdata/— example inputs and outputsutils/— shared utilities (e.g., logger)main.go— entry point that delegates tocmd/
Key files:
code/main.go— theSolvefunction routes execution to the correct solver(s) using theSolversarray.code/config.go— helper functions for file I/O:readPuzzleInput(day int) stringwritePuzzleOutput(day, part int, output string)
utils/logger.go— configured Zap logger. SetDEBUG=1to see debug logs (uses Zap'sdevelopmentlogger).
-
Create solver functions in
code/, following the existing pattern:dayNPart1()dayNPart2()
Inside each solver you typically:
- Read the input:
input := readPuzzleInput(N) - Compute the result for that part
- Persist the result:
writePuzzleOutput(N, 1, result)(or2for part 2) - Optionally log with context:
logger := utils.Logger.With(zap.Int("day", N), zap.Int("part", 1)) logger.Debug("starting solver")
-
Register the new day in
code/main.goby extending theSolversarray ininit():Solvers = [][]func(){ {day1Part1, day1Part2}, {day2Part1, day2Part2}, {day3Part1, day3Part2}, // <-- add new day here }
-
Add your input files under
data/inputs/dayN/:- Real:
input.txt - Demo:
demo-input.txt
- Real:
-
Run your solver locally:
- Real input:
go run . --day N - Demo input:
go run . --day N --demo
- Real input:
- Use
DEBUG=1to enable verbose logs while developing. - Keep solvers deterministic and side-effect free except for file I/O via the provided helpers.
- Prefer small, testable functions inside each
dayN-partMfiles.
This project is released under the MIT License. See LICENSE for details.