···11+---
22+title: Advent Adventures - Prologue
33+date: 2023-12-19
44+summary: A preparation post for when I begin going through Advent of Code year-by-year
55+cowsay: Happy Holidays!
66+---
77+88+Over the past few years I've done [Advent of Code](https://adventofcode.com) on and off. At the time of writing, I'm on [day 19 of this year's challenge](https://adventofcode.com/2023/day/19) and will (hopefully) complete this year. After that, I want to go back and do all the previous years. I'll be writing a post for each year that I do, with a few highlights for the days I enjoyed and/or struggled with. I'll be using [Rust](https://www.rust-lang.org/) for all of my solutions, as it's a language I really want to learn in-depth.
99+1010+Of course, I can't call myself a programmer if I don't needlessly over-complicate things. In addition to solving each day I want to keep everything organized, I want to make a repo for all of my Advent solutions. In addition to each year, I want to make a central utils crate that I can use across all years, I want this to be a sort of "swiss-army-knife" of competitive programming tactics/algorithms-- something I can look back on later for reference and also use next year.
1111+1212+I also want this repo to be easy to use. That is, I want it to be able to generate most of the boilerplate for me through macros. I want to be able to generate a year with a single command and have all the days of that year ready to be implemented.
1313+1414+This repo should also be able to make a single binary I can use to run any of my solutions across all of the years I've done.
1515+1616+Lets get started!
1717+1818+## The Project Structure
1919+2020+I'm going to be using [cargo workspaces](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html) to organize everything. The structure will look something like this:
2121+2222+```dir
2323+advent/
2424+├─ src/
2525+├─ core/
2626+│ ├─ Cargo.toml
2727+│ ├─ src/
2828+├─ macros/
2929+│ ├─ Cargo.toml
3030+│ ├─ src/
3131+├─ util/
3232+│ ├─ Cargo.toml
3333+│ ├─ src/
3434+├─ years/
3535+│ ├─ 20XX/
3636+│ │ ├─ Cargo.toml
3737+│ │ ├─ src/
3838+│ │ │ ├─ day_X.rs
3939+├─ Cargo.toml
4040+```
4141+4242+My top-level package is `advent`, this will be the main workspace. The `util` package will be a library that I can use across all years. Each year will be its own package, with its own `Cargo.toml` file. This will allow me to have a separate `main.rs` for each year, and also allow me to use the `util` package as a dependency.
4343+4444+### Core Package
4545+4646+This package will contain everything needed to work with days and parts. This should export a `Year` and `Day` trait that all years and days will implement. It will handle parsing arguments, getting the input from stdin, and timing the solutions. This package will be used by the year packages.
4747+4848+### Macros Package
4949+5050+This package will contain any macros I need. It's a proc macro crate.
5151+5252+### Year Packages
5353+5454+Each year will be a binary that I can run with `cargo run -p y_[YEAR] [DAY]:[PART]`. For example, to run the solution for day 1 part 2 of 2020 I would run `cargo run -p y_2022 1:2`. In addition, the binary can take a wild card to mean "run all days". For example, `cargo run -p y_2022 *` would run all days of 2020.
5555+5656+The binary will take input through stdin.
5757+5858+### Main Package
5959+6060+The main package will contain a binary that acts as a runner for all days. It will take a year, day, and part as arguments and run the corresponding binary. This package includes all the day packages as dependencies and simply acts as a runner for them.
6161+6262+### Utils Package
6363+6464+The utils package is pretty self-explanatory. It will contain a library that I can use over the years. This will only include things used in _solving the problems_ not running the days. Ideally, this will require no dependencies and would be easily transferred to other projects.
6565+6666+## The `Day` trait
6767+6868+The `Day` trait will be the main trait that all days will implement. It will have a few methods that will be used by the `Year` trait to run the days. Here's what it looks like:
6969+7070+```rs
7171+pub trait Day {
7272+7373+ type Input;
7474+7575+ const EXAMPLE_INPUT_1: &'static str = "";
7676+ const EXAMPLE_INPUT_2: &'static str = "";
7777+7878+ const EXPECTED_1: &'static str = "";
7979+ const EXPECTED_2: &'static str = "";
8080+8181+ fn get_example_input(part: usize) -> &'static str {
8282+ match part {
8383+ 1 => Self::EXAMPLE_INPUT_1,
8484+ 2 => Self::EXAMPLE_INPUT_2,
8585+ _ => panic!("Invalid part number"),
8686+ }
8787+ }
8888+8989+ fn run_part(part: usize, input: Option<&str>) -> Option<String> {
9090+ let input = input.unwrap_or_else(|| Self::get_example_input(part));
9191+ let input = Self::parse_input(input);
9292+ match part {
9393+ 1 => Self::part_1(input),
9494+ 2 => Self::part_2(input),
9595+ _ => panic!("Invalid part number"),
9696+ }
9797+ }
9898+9999+ // ...
100100+101101+ fn parse_input(input: &str) -> Self::Input;
102102+103103+ fn part_1(_input: Self::Input) -> Option<String> { None }
104104+ fn part_2(_input: Self::Input) -> Option<String> { None }
105105+106106+}
107107+```
108108+109109+The general idea here is we have a few constants that contain the example input for each part. Then we have a few methods that will be used to run the days. The `get_example_input` method will be used to get the example input for a given part. The `run_part` method will be used to run a given part. The `parse_input` method will be used to parse the input into the type used by the day. Finally, the `part_1` and `part_2` methods will be used to run the parts.
110110+111111+Here we expect `part_1` and `part_2` to be implemented. However, they return `None` by default. This is so we can generate all 25 days of a year without having to implement all of them. If a day is not implemented, the runner will simply print a message saying that the day is not implemented.
112112+113113+Sadly `parse_input` will always have to be implemented, as there's no way to implement it by default. `I` here is a `String` by default, and since `parse_input` returns `I` one might think that for a default implementation, we could just return the input as-is. However, rust won't allow this as in the event `I` is changed to another type, the default implementation would no longer work. So we have to implement `parse_input` for each day. Implementing it isn't too bad, and could be part of a derive macro later on.
114114+115115+## The `Year` trait
116116+117117+The `Year` trait will be the main trait that all years will implement. It will have a few methods that will be used by the `core` package to run the days. Here's what it looks like:
118118+119119+```rs
120120+pub trait Year {
121121+ const YEAR: usize;
122122+123123+ fn solve_day(day: usize, part: usize, input: Option<&str>) -> Option<String>;
124124+125125+ fn solve_day_both_parts(day: usize, extra_indent: &str);
126126+127127+ fn solve_all_days() {
128128+ println!("Year {}:", Self::YEAR);
129129+ for day in 1..=MAX_DAY {
130130+ Self::solve_day_both_parts(day, " ");
131131+ }
132132+ }
133133+134134+ fn run_dp(input: Option<&str>, dp: DP) {
135135+ match dp.day {
136136+ Selection::All => {
137137+ Self::solve_all_days();
138138+ },
139139+ Selection::Single(day) => {
140140+ match dp.part {
141141+ Selection::All => {
142142+ Self::solve_day_both_parts(day, "");
143143+ },
144144+ Selection::Single(part) => {
145145+ Self::solve_day(day, part, input);
146146+ },
147147+ }
148148+ },
149149+ }
150150+ }
151151+}
152152+```
153153+154154+The general idea here is we have a few methods that will be used to run the days. The `solve_day` method will be used to run a given day. The `solve_day_both_parts` method will be used to run both parts of a given day. Finally, the `solve_all_days` method will be used to run all days. The extra indent is used to indent the output of the day so that it lines up with the year.
155155+156156+## Trying to implement a year
157157+158158+Now that we have a basic skeleton of what a year should look like, let's try to implement it! I'm going to be using the most recent year, 2023, as my testing grounds.
159159+160160+The layout of each year's package will look like this:
161161+162162+```dir
163163+20XX/
164164+├─ src/
165165+│ ├─ main.rs
166166+│ ├─ lib.rs
167167+│ ├─ day_X.rs
168168+├─ Cargo.toml
169169+```
170170+171171+The `main.rs` file will be used to run the days. The `lib.rs` file will be used to implement the `Year` trait. The `day_X.rs` files will be used to implement the `Day` trait for each day.
172172+173173+Starting out I'm going to manually implement one day of 2023 and get my traits implemented. Then, I'm going to see where I can use macros, derive macros, and other things to make the process easier for subsequent days/years.
174174+175175+### Day 1
176176+177177+```rs
178178+use core::Day;
179179+180180+pub struct Day1;
181181+182182+// Ideally most of this could be handled by a proc macro of some kind
183183+impl Day for Day1 {
184184+185185+ type Input = String;
186186+187187+ const EXAMPLE_INPUT_1: &'static str = "...";
188188+ // Defining examples and such...
189189+190190+ fn parse_input(input: &str) -> Self::Input {
191191+ input.to_string()
192192+ }
193193+194194+ fn part_1(input: Self::Input) -> Option<String> {
195195+ //...
196196+ Some(answer.to_string())
197197+ }
198198+199199+ fn part_2(input: Self::Input) -> Option<String> {
200200+ //...
201201+ Some(answer.to_string())
202202+ }
203203+}
204204+```
205205+206206+I still don't know how I entirely feel about making my Days return `String`s. It's fine for now and will come in handy if for some reason a problem needs a string as an answer. However, I think I might change it to return an `i64` instead. I'll have to see how it goes.
207207+208208+Now that I have a day implemented, I want to try and simplify defining it via a macro. I'm going to try and make a macro that will generate the `Day` trait implementation (or most of it) for me.
209209+210210+```rs
211211+#[macro_export]
212212+macro_rules! ex_for_day {
213213+ ($day:literal, $part:literal) => {
214214+ include_str!(concat!("examples/day_", stringify!($day), "/", stringify!($part), ".txt"))
215215+ };
216216+}
217217+218218+#[macro_export]
219219+macro_rules! day_stuff {
220220+ ($day:literal, $e_1:literal, $e_2:literal) => {
221221+ day_stuff!($day, $e_1, $e_2, String);
222222+223223+ fn parse_input(input: &str) -> Self::Input {
224224+ input.to_string()
225225+ }
226226+ };
227227+228228+ ($day:literal, $e_1:literal, $e_2:literal, $i: ty) => {
229229+ type Input = $i;
230230+231231+ const DAY: usize = $day;
232232+ const EXAMPLE_INPUT_1: &'static str = ex_for_day!($day, 1);
233233+ const EXAMPLE_INPUT_2: &'static str = ex_for_day!($day, 2);
234234+ const EXPECTED_1: &'static str = $e_1;
235235+ const EXPECTED_2: &'static str = $e_2;
236236+ }
237237+}
238238+```
239239+240240+Here we can see I have two macros. The first one, `ex_for_day`, is used to get the example input for a given day and part. The second one, `day_stuff`, is used to generate part of the `Day` trait implementation. It takes the day number, the expected answers, and the type of the input. It then generates the `Input` type, the example inputs, and the expected answers. Finally, it generates the `parse_input` method.
241241+242242+If no input type is given, it defaults to `String`. This is because I want to be able to use this macro for all days, and I don't want to have to specify the input type for each day.
243243+244244+In theory, I could use proc-macros here to generate more of the file. However, proc macros tend to mess with debugging output / hide what scopes things are defined in. I want to be able to debug my code, so I'm going to stick with these macros for now.
245245+246246+Now, I need to be able to generate a `Year` trait implementation. This will be a bit more complicated than the `Day` trait implementation, as I need to generate a `match` statement for each day. I'm going to try and make a macro that will generate the `Year` trait implementation for me.
247247+248248+### Year 2023
249249+250250+After getting the `Year` trait implemented for 2023, here's what it looks like:
251251+252252+```rs
253253+mod day_1;
254254+255255+use core::{Day, Year};
256256+257257+use day_1::Day1;
258258+259259+pub struct Year2023;
260260+261261+impl Year for Year2023 {
262262+263263+ const YEAR: usize = 2023;
264264+265265+ fn solve_day(day: usize, part: usize, input: Option<&str>) -> Option<String> {
266266+ match day {
267267+ 1 => Day1::run_part(part, input),
268268+ _ => None,
269269+ }
270270+ }
271271+272272+ fn solve_day_both_parts(day: usize, extra_indent: &str) {
273273+ match day {
274274+ 1 => Day1::run_all_parts(extra_indent),
275275+ _ => (),
276276+ }
277277+ }
278278+279279+}
280280+```
281281+282282+This looks like it can be placed in a proc macro as all we're _really changing_ here is the year number, the rest should be carbon copy for all years. So I created a simple proc macro that basically has the `pub struct` and `impl` blocks as a template, and will replace the year number with the proper year:
283283+284284+```rs
285285+extern crate proc_macro;
286286+287287+use proc_macro::TokenStream;
288288+289289+const YEAR_TEMPLATE: &str = include_str!("template_year.rs");
290290+291291+#[proc_macro]
292292+pub fn year(item: TokenStream) -> TokenStream {
293293+ let year = item.to_string();
294294+295295+ YEAR_TEMPLATE.replace("__YEAR__", &year).parse().unwrap()
296296+}
297297+```
298298+299299+After expanding this to include mod statements, use statements, and tests, I get the ability to simply do:
300300+301301+```rs
302302+use macros::year;
303303+304304+year!(2023);
305305+```
306306+307307+to automatically make a runner and tester for all the days in 2023.
308308+309309+#### Adding the Year Runner
310310+311311+Up until now, I haven't shown _how_ running problems is going to work. The year binaries will parse the arguments into a struct called `DP` (day, part). This struct will then be used to find the correct day and part to run. Here's what it looks like:
312312+313313+```rs
314314+#[derive(Clone, Debug)]
315315+pub enum Selection {
316316+ All,
317317+ Single(usize), // TODO: Add range maybe?
318318+}
319319+320320+pub struct DP {
321321+ pub day: Selection,
322322+ pub part: Selection,
323323+}
324324+```
325325+326326+The parsing for this is pretty simple, split by `:`, then parse each part. If the part is `*` then it's `Selection::All`, otherwise it's `Selection::Single`.
327327+328328+Now I can simply pass this to the `Year` trait's `run_dp` method and it will run the correct day and part.
329329+330330+For the final bit, we need a way to get the `input` we want to run. To do this, I accept the input as a second argument to the binary. If no input is given, we'll use the example input. If the user passes `-`, we will read from stdin. Otherwise, we will simply use the input given.
331331+332332+Combining parsing the DP and the input we get this handy utility method:
333333+334334+```rs
335335+pub fn get_dp_and_input() -> (DP, Option<String>) {
336336+ let mut args = args().skip(1);
337337+338338+ let dp = args.next().map(|s| DP::parse(&s.trim())).unwrap_or(DP_ALL);
339339+340340+ let input = args.next().map(|s| s.trim().to_string()).map(|i| {
341341+ if i == "-" {
342342+ let mut input = String::new();
343343+ stdin().read_to_string(&mut input).expect("Failed to read input");
344344+ input.trim().to_string()
345345+ } else {
346346+ i
347347+ }
348348+ });
349349+350350+ (dp, input)
351351+}
352352+```
353353+354354+All we need in `main.rs` for our years now is some glue code:
355355+356356+```rs
357357+use core::{Year, get_dp_and_input};
358358+359359+use y_2023::Year2023;
360360+361361+fn main() {
362362+ let (dp, input) = get_dp_and_input();
363363+ Year2023::run_dp(input.as_deref(), dp);
364364+}
365365+```
366366+367367+We can run our year with `cargo run -p y_2023 1:1` and it will run day 1 part 1 of 2023. We can also run `cargo run -p y_2023 1:*` to run both parts of day 1, or `cargo run -p y_2023 *` to run all days of 2023.
368368+369369+It's a bit overkill, but I made a proc macro to generate this glue code for me:
370370+371371+```rs
372372+#[proc_macro]
373373+pub fn year_runner(item: TokenStream) -> TokenStream {
374374+ let year = item.to_string();
375375+376376+ format!("
377377+ use core::{{Year, get_dp_and_input}};
378378+379379+ use y_{year}::Year{year};
380380+381381+ fn main() {{
382382+ let (dp, input) = get_dp_and_input();
383383+ Year{year}::run_dp(input.as_deref(), dp);
384384+ }}").parse::<TokenStream>().unwrap()
385385+}
386386+```
387387+388388+### Generating Days / Years
389389+390390+Now that I've got a basic skeleton for a day and year, I want to be able to generate them. I'm going to make a function in my `core` package that will let me generate a day to a given file given the day's number. Then I'm gonna write a function that can generate an entire year (and its rust project) given the year's number.
391391+392392+So to generate an entire year crate I need to:
393393+394394+1. Create a directory for the year in `years/`
395395+2. Initialize a `Cargo.toml` file
396396+ 1. Name it `y_[YEAR]`
397397+ 2. Add the `core` package as a dependency
398398+ 3. Add the `util` package as a dependency
399399+ 4. Add the `macros` package as a dependency
400400+3. Make a `src/` folder
401401+ 1. Make example files for every day in the year in `examples/day_[DAY]/[PART].txt`
402402+ 2. Make a `day_[DAY].rs` file for each day, with a definition for the Day and a call to `day_stuff!`
403403+ 3. Make a `lib.rs` file that has a single macro call to `year!([YEAR]);`
404404+ 4. Make a `main.rs` file that has a single macro call to `year_runner!([YEAR]);`
405405+4. Add the year as a dependency to the `Cargo.toml` file in the root of the project
406406+407407+The logic for this is pretty rudimentary so I'm going to not include it here. Basically, the main binary will take a year number and generate the year crate for that year. It will also add the year crate as a dependency to the root `Cargo.toml` file.
408408+409409+### The Main Binary
410410+411411+The main binary is used for two things:
412412+413413+1. It's the thing that you run to actually generate years
414414+2. It can run any year, day, and part. It does this by compiling all years into itself
415415+416416+The first step is making a simple CLI parser. I want it to be able to take 2 commands `new` and `solve`. `new` is used to generate a new year, and `solve` is used to run a year, day, and part. Here's what the CLI parser looks like:
417417+418418+```rs
419419+let args = std::env::args().skip(1).collect::<Vec<_>>();
420420+421421+ let command = args.get(0);
422422+423423+ match command {
424424+ Some(command) => {
425425+ match command.as_str() {
426426+ "new" => {
427427+ let year = args.get(1).expect("No year provided");
428428+ make_year(year);
429429+ },
430430+ "solve" | "run" => {
431431+ let (ydp, input) = get_ydp_and_input(args[1..].to_vec());
432432+ run_ydp(ydp, input);
433433+ }
434434+ _ => {
435435+ println!("Unknown command: {}", command);
436436+ println!("Available commands: new, solve");
437437+ }
438438+ }
439439+ },
440440+ None => {
441441+ println!("No command provided");
442442+ println!("Available commands: new, solve");
443443+ }
444444+ }
445445+```
446446+447447+After that, we need to connect `new` to `core::make_year` and then `solve` to a match statement that will run the correct year, day, and part.
448448+449449+To generate this match statement (and use statements) I once again turn to a proc macro. These macros are pretty much the same for days, they have a match that takes a year number and will run the corresponding year.
450450+451451+### Testing
452452+453453+Now that we have a project generation command we're done! This allows us to generate a new year crate, auto-updating the main binary to include it as a dependency. The crate will have all 25 days generated, with the `Day` trait implemented for each day. The `Year` trait will also be implemented for the year. Now we can run the year's binary through `cargo run -p y_[YEAR] [DAY]:[PART]` and it will run the correct day and part. Or we can run `cargo run solve [YEAR]:[DAY]:[PART]` to get the same result.
454454+455455+I'll start by generating all 25 days for 2023. Up until now, I've only been generating one day per year for simplicity.
456456+457457+So now my y_2023 crate has a `day_x.rs` file for every day until the 25th. I can now run `cargo run solve 2023:*` to run every single day of the year. Although they're not implemented yet, it will just print that the day is not implemented.
458458+459459+### Conclusion
460460+461461+So now I have an automated repo for solving Advent of Code. I can generate a new year, and it will generate all 25 days for that year. I then implement the days and run them through the main binary. I'm going to hold off on transferring my 2023 solutions to this repo until it's over. After which I want to take a look at all of the days in 2023 and see what I can extract into the `utils` crate and `macros` crate.
462462+463463+And with that, I'm done with this post! Stay tuned for a "Prologue part 2" / "2023" post where I go over some of the highlights of 2023 for me and describe what logic I'm extracting into the `utils` crate and `macros` crate.
+2-2
src/content/posts/hello_world.mdx
···119119</Layout>
120120```
121121122122-
122122+
123123124124Great! I'll probably fiddle with it in the future but it's a good start. Now we need to make a page for each post.
125125To make my URLs look nice I'm going to create a subfolder within `blog` called `posts` and then place a `[...slug].astro` in there.
···233233}
234234```
235235236236-
236236+
237237238238## The Cowsay
239239