From cc42c33b5db5d8cb286dcffb95286550b17ffa25 Mon Sep 17 00:00:00 2001 From: LeMoonStar Date: Sat, 30 Nov 2024 13:39:19 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=89=20Initialized=20repository=20based?= =?UTF-8?q?=20on=202023=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + Cargo.toml | 21 + LICENSE | 21 + README.md | 49 ++ aoc-macro/.gitignore | 1 + aoc-macro/Cargo.lock | 46 ++ aoc-macro/Cargo.toml | 13 + aoc-macro/src/lib.rs | 108 ++++ images/auto.png | Bin 0 -> 18609 bytes images/run.png | Bin 0 -> 19798 bytes images/test.png | Bin 0 -> 18537 bytes src/days/d01.rs | 32 + src/days/d02.rs | 32 + src/days/d03.rs | 32 + src/days/d04.rs | 32 + src/days/d05.rs | 32 + src/days/d06.rs | 32 + src/days/d07.rs | 32 + src/days/d08.rs | 32 + src/days/d09.rs | 32 + src/days/d10.rs | 32 + src/days/d11.rs | 32 + src/days/d12.rs | 32 + src/days/d13.rs | 32 + src/days/d14.rs | 32 + src/days/d15.rs | 32 + src/days/d16.rs | 32 + src/days/d17.rs | 32 + src/days/d18.rs | 32 + src/days/d19.rs | 32 + src/days/d20.rs | 32 + src/days/d21.rs | 32 + src/days/d22.rs | 32 + src/days/d23.rs | 32 + src/days/d24.rs | 32 + src/days/d25.rs | 32 + src/days/mod.rs | 250 ++++++++ src/days/test_inputs/test01.txt | 1000 +++++++++++++++++++++++++++++++ src/days/test_inputs/test02.txt | 5 + src/days/test_inputs/test03.txt | 10 + src/days/test_inputs/test04.txt | 6 + src/days/test_inputs/test05.txt | 33 + src/days/test_inputs/test06.txt | 2 + src/days/test_inputs/test07.txt | 5 + src/days/test_inputs/test08.txt | 752 +++++++++++++++++++++++ src/days/test_inputs/test09.txt | 3 + src/days/test_inputs/test10.txt | 10 + src/days/test_inputs/test11.txt | 10 + src/days/test_inputs/test12.txt | 6 + src/days/test_inputs/test13.txt | 15 + src/days/test_inputs/test14.txt | 10 + src/days/test_inputs/test15.txt | 1 + src/days/test_inputs/test16.txt | 10 + src/days/test_inputs/test17.txt | 13 + src/days/test_inputs/test18.txt | 14 + src/days/test_inputs/test19.txt | 0 src/days/test_inputs/test20.txt | 0 src/days/test_inputs/test21.txt | 0 src/days/test_inputs/test22.txt | 0 src/days/test_inputs/test23.txt | 0 src/days/test_inputs/test24.txt | 0 src/days/test_inputs/test25.txt | 0 src/days/utils.rs | 161 +++++ src/lib.rs | 261 ++++++++ src/main.rs | 259 ++++++++ 65 files changed, 3897 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 aoc-macro/.gitignore create mode 100644 aoc-macro/Cargo.lock create mode 100644 aoc-macro/Cargo.toml create mode 100644 aoc-macro/src/lib.rs create mode 100644 images/auto.png create mode 100644 images/run.png create mode 100644 images/test.png create mode 100644 src/days/d01.rs create mode 100644 src/days/d02.rs create mode 100644 src/days/d03.rs create mode 100644 src/days/d04.rs create mode 100644 src/days/d05.rs create mode 100644 src/days/d06.rs create mode 100644 src/days/d07.rs create mode 100644 src/days/d08.rs create mode 100644 src/days/d09.rs create mode 100644 src/days/d10.rs create mode 100644 src/days/d11.rs create mode 100644 src/days/d12.rs create mode 100644 src/days/d13.rs create mode 100644 src/days/d14.rs create mode 100644 src/days/d15.rs create mode 100644 src/days/d16.rs create mode 100644 src/days/d17.rs create mode 100644 src/days/d18.rs create mode 100644 src/days/d19.rs create mode 100644 src/days/d20.rs create mode 100644 src/days/d21.rs create mode 100644 src/days/d22.rs create mode 100644 src/days/d23.rs create mode 100644 src/days/d24.rs create mode 100644 src/days/d25.rs create mode 100644 src/days/mod.rs create mode 100644 src/days/test_inputs/test01.txt create mode 100644 src/days/test_inputs/test02.txt create mode 100644 src/days/test_inputs/test03.txt create mode 100644 src/days/test_inputs/test04.txt create mode 100644 src/days/test_inputs/test05.txt create mode 100644 src/days/test_inputs/test06.txt create mode 100644 src/days/test_inputs/test07.txt create mode 100644 src/days/test_inputs/test08.txt create mode 100644 src/days/test_inputs/test09.txt create mode 100644 src/days/test_inputs/test10.txt create mode 100644 src/days/test_inputs/test11.txt create mode 100644 src/days/test_inputs/test12.txt create mode 100644 src/days/test_inputs/test13.txt create mode 100644 src/days/test_inputs/test14.txt create mode 100644 src/days/test_inputs/test15.txt create mode 100644 src/days/test_inputs/test16.txt create mode 100644 src/days/test_inputs/test17.txt create mode 100644 src/days/test_inputs/test18.txt create mode 100644 src/days/test_inputs/test19.txt create mode 100644 src/days/test_inputs/test20.txt create mode 100644 src/days/test_inputs/test21.txt create mode 100644 src/days/test_inputs/test22.txt create mode 100644 src/days/test_inputs/test23.txt create mode 100644 src/days/test_inputs/test24.txt create mode 100644 src/days/test_inputs/test25.txt create mode 100644 src/days/utils.rs create mode 100644 src/lib.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8a44e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +.aoc23_cache \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3609d01 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "aoc24" +version = "0.1.0" +edition = "2024" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "2.34", features = ["suggestions", "color", "wrap_help"] } +colored = "2.0" +aoc-macro = {path="aoc-macro"} +reqwest = { version = "0.11", features=["cookies", "blocking"] } +mut_static="5.0" +lazy_static="1.4" + +[profile.release] +opt-level = 3 +lto = true +codegen-units = 1 +panic = 'abort' +strip = true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ef476cc --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 LeMoonStar + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d321dcf --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Advent of Code 2024 + +[![About](https://img.shields.io/badge/Advent%20of%20Code-2024-brightgreen?style=flat-square)](https://adventofcode.com/2024/about) +[![Language: Rust](https://img.shields.io/badge/Language-Rust-orange.svg?style=flat-square)](https://en.wikipedia.org/wiki/Rust_(programming_language)) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)](https://mit-license.org/) +![Days completed](https://img.shields.io/badge/Days%20completed-0-red?style=flat-square) +![Stars](https://img.shields.io/badge/Stars-0-yellow?style=flat-square) + +> ⚠️ This README is copied from my previous years solution. It is not fully adopted to 2024 yet. + +Last Years README: +> This is the third year in a row I (plan to) use rust to solve Advent of Code. But I must warn you: I haven't programmed a lot over the past year, so I am likely not in the best shape (School has been rough - I need a break) - expect the worst. +> Just like last year, I am lazy and currently setting this project up in a hurry (In class, actually) just so I have the basic framework once the event starts. So this project is literally a cleaned-up copy of my [solutions for the previous year](https://github.com/LeMoonStar/AoC22), which in term are a cleaned up version of my [Advent of Code 2021 solutions](https://github.com/LeMoonStar/AoC21). +> **The Following therefore still contains screenshots of the 2021 binary. The usage is the same, but the name is `aoc24` instead of `aoc21`** +> +> Later on, maybe even before AoC starts, I'll clean this up more, maybe create a crate out of the framework so I don't have to copy each year over and over again. + +Well, not much changed. This is now the fourth year I'll use Rust - after not using Rust in a while and likely with a lot less time at hand. We'll see how it goes. I am finally out of school tough - that's a relief! + +## Usage + +There are multiple ways to run my solutions, the easiest and most comfortable one is the `auto` command: +It automatically downloads your input. For this it requires you to provide your Advent of Code session id, which you can find in the websites cookies after logging in. +Simply provide the session token by setting the `AOC_SESSION` environment variable or using the -s argument: +`AOC_SESSION=XXXMYSESSION ./aoc24 [DAY] auto` or `./aoc24 [DAY] auto -s XXXMYSESSION`. +In this example, the environment variable for the AoC session is set using `export AOC_SESSION=XXXMYSESSION`, so I can run the command without specifying the session token again: +![auto command in action](./images/auto.png) + +If you don't want to automatically download the input, you can also use the `run` command, which uses a locally stored file or the stdin input: +`./aoc24 [DAY] run -f my_input.txt`: +![run command in action](./images/run.png) + +If you just want to run the day's example, simply use the `test` command, as this project already includes the examples: +`./aoc24 [DAY] test`: +![test command in action](./images/test.png) + +## Compiling + +This project uses `Cargo`, so compiling is pretty easy: +`cargo build --release` +The resulting binary can be found at `./targets/release/aoc22`. You can also directly run the project using `cargo run --release [arguments for aoc24]` +the `--release` option is not required, but it results in better performance. + +## Check out other AoC24 solutions + +| Repository | Language | +|----------------------------------------------------------------------------------|----------| +| [Trojaner's AdventofCode2024](https://github.com/TrojanerHD/AdventofCode2024) | Rust | +| [Niklas' Advent-of-Code-2024](https://github.com/derNiklaas/Advent-of-Code-2024) | Kotlin | diff --git a/aoc-macro/.gitignore b/aoc-macro/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/aoc-macro/.gitignore @@ -0,0 +1 @@ +/target diff --git a/aoc-macro/Cargo.lock b/aoc-macro/Cargo.lock new file mode 100644 index 0000000..264c9d7 --- /dev/null +++ b/aoc-macro/Cargo.lock @@ -0,0 +1,46 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aoc-macro" +version = "0.1.0" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" diff --git a/aoc-macro/Cargo.toml b/aoc-macro/Cargo.toml new file mode 100644 index 0000000..9d3a4f5 --- /dev/null +++ b/aoc-macro/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "aoc-macro" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro=true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +quote = "^1.0" +syn = "^1.0" diff --git a/aoc-macro/src/lib.rs b/aoc-macro/src/lib.rs new file mode 100644 index 0000000..672cf04 --- /dev/null +++ b/aoc-macro/src/lib.rs @@ -0,0 +1,108 @@ +use proc_macro::TokenStream; +use quote::quote; + +// Again, huge thanks to andi_makes +// this is basically a copy of his macros, with very slight modifications. + +#[proc_macro] +pub fn mod_days(_input: TokenStream) -> TokenStream { + let mut res = String::new(); + for i in 1..26 { + res += &format!("mod d{:02};", i); + } + res.parse().unwrap() +} + +/*#[proc_macro] +pub fn match_and_run_day(_input: TokenStream) -> TokenStream { + let mut res = "match day {".to_string(); + for i in 1..26 { + res += &format!("{} => Day::<{}>::run_timed(input),", i, i); + } + res += "_ => panic!(\"Days out of Bounds! No presents for you!\")};"; + res.parse().unwrap() +}*/ + +#[proc_macro] +pub fn match_and_run_day_both(_input: TokenStream) -> TokenStream { + let r = 1_u8..26; // == [1,25] + let res = quote! { + match day { + #(#r => { + Day::<#r>::run_timed(input.trim_end()) + })* + _ => panic!("Days out of Bounds! No presents for you!"), + } + }; + res.into() +} + +#[proc_macro] +pub fn match_and_run_day_one(_input: TokenStream) -> TokenStream { + let r = 1_u8..26; // == [1,25] + let res = quote! { + match day { + #(#r => { + Day::<#r>::run_one_timed(input.trim_end()) + })* + _ => panic!("Days out of Bounds! No presents for you!"), + } + }; + res.into() +} + +#[proc_macro] +pub fn match_and_run_day_two(_input: TokenStream) -> TokenStream { + let r = 1_u8..26; // == [1,25] + let res = quote! { + match day { + #(#r => { + Day::<#r>::run_two_timed(input.trim_end()) + })* + _ => panic!("Days out of Bounds! No presents for you!"), + } + }; + res.into() +} + +#[proc_macro] +pub fn match_and_test_day_both(_input: TokenStream) -> TokenStream { + let r = 1_u8..26; // == [1,25] + let res = quote! { + match day { + #(#r => { + Day::<#r>::test() + })* + _ => panic!("Days out of Bounds! No presents for you!"), + } + }; + res.into() +} + +#[proc_macro] +pub fn match_and_test_day_one(_input: TokenStream) -> TokenStream { + let r = 1_u8..26; // == [1,25] + let res = quote! { + match day { + #(#r => { + Day::<#r>::test_one() + })* + _ => panic!("Days out of Bounds! No presents for you!"), + } + }; + res.into() +} + +#[proc_macro] +pub fn match_and_test_day_two(_input: TokenStream) -> TokenStream { + let r = 1_u8..26; // == [1,25] + let res = quote! { + match day { + #(#r => { + Day::<#r>::test_two() + })* + _ => panic!("Days out of Bounds! No presents for you!"), + } + }; + res.into() +} diff --git a/images/auto.png b/images/auto.png new file mode 100644 index 0000000000000000000000000000000000000000..79c767677b2e7e9e4f455669f229ae842cf02b01 GIT binary patch literal 18609 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 literal 0 HcmV?d00001 diff --git a/images/run.png b/images/run.png new file mode 100644 index 0000000000000000000000000000000000000000..4c93c40c49489e14d02e4b9e23244bc4fae93919 GIT binary patch literal 19798 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 literal 0 HcmV?d00001 diff --git a/images/test.png b/images/test.png new file mode 100644 index 0000000000000000000000000000000000000000..60ece3e3f9325bce974d36e0eadbf66cfb6d7dd5 GIT binary patch literal 18537 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 +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test01.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d02.rs b/src/days/d02.rs new file mode 100644 index 0000000..bc6dd3d --- /dev/null +++ b/src/days/d02.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 2; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test02.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d03.rs b/src/days/d03.rs new file mode 100644 index 0000000..59c0ad7 --- /dev/null +++ b/src/days/d03.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 3; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test03.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d04.rs b/src/days/d04.rs new file mode 100644 index 0000000..0cda657 --- /dev/null +++ b/src/days/d04.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 4; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test04.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d05.rs b/src/days/d05.rs new file mode 100644 index 0000000..c0f3fc9 --- /dev/null +++ b/src/days/d05.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 5; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test05.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d06.rs b/src/days/d06.rs new file mode 100644 index 0000000..86d4a69 --- /dev/null +++ b/src/days/d06.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 6; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test06.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d07.rs b/src/days/d07.rs new file mode 100644 index 0000000..f8e20e3 --- /dev/null +++ b/src/days/d07.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 7; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test07.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d08.rs b/src/days/d08.rs new file mode 100644 index 0000000..e504bf8 --- /dev/null +++ b/src/days/d08.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 8; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test08.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d09.rs b/src/days/d09.rs new file mode 100644 index 0000000..116e3a8 --- /dev/null +++ b/src/days/d09.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 9; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test09.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d10.rs b/src/days/d10.rs new file mode 100644 index 0000000..d01bcab --- /dev/null +++ b/src/days/d10.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 10; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test10.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d11.rs b/src/days/d11.rs new file mode 100644 index 0000000..0f737b0 --- /dev/null +++ b/src/days/d11.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 11; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test11.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d12.rs b/src/days/d12.rs new file mode 100644 index 0000000..9f26cd5 --- /dev/null +++ b/src/days/d12.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 12; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test12.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d13.rs b/src/days/d13.rs new file mode 100644 index 0000000..5d38c35 --- /dev/null +++ b/src/days/d13.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 13; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test13.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d14.rs b/src/days/d14.rs new file mode 100644 index 0000000..052771b --- /dev/null +++ b/src/days/d14.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 14; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test14.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d15.rs b/src/days/d15.rs new file mode 100644 index 0000000..c854d98 --- /dev/null +++ b/src/days/d15.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 15; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test15.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d16.rs b/src/days/d16.rs new file mode 100644 index 0000000..ff2ca9f --- /dev/null +++ b/src/days/d16.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 16; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test16.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d17.rs b/src/days/d17.rs new file mode 100644 index 0000000..a560223 --- /dev/null +++ b/src/days/d17.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 17; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test17.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d18.rs b/src/days/d18.rs new file mode 100644 index 0000000..b20618d --- /dev/null +++ b/src/days/d18.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 18; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test18.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d19.rs b/src/days/d19.rs new file mode 100644 index 0000000..a135609 --- /dev/null +++ b/src/days/d19.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 19; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test19.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d20.rs b/src/days/d20.rs new file mode 100644 index 0000000..cacc372 --- /dev/null +++ b/src/days/d20.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 20; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test20.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d21.rs b/src/days/d21.rs new file mode 100644 index 0000000..96be39b --- /dev/null +++ b/src/days/d21.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 21; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test21.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d22.rs b/src/days/d22.rs new file mode 100644 index 0000000..99a17bc --- /dev/null +++ b/src/days/d22.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 22; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test22.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d23.rs b/src/days/d23.rs new file mode 100644 index 0000000..505505a --- /dev/null +++ b/src/days/d23.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 23; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test23.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d24.rs b/src/days/d24.rs new file mode 100644 index 0000000..b892460 --- /dev/null +++ b/src/days/d24.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 24; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test24.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/d25.rs b/src/days/d25.rs new file mode 100644 index 0000000..dc2783a --- /dev/null +++ b/src/days/d25.rs @@ -0,0 +1,32 @@ +use super::{Answer, Day, DayImpl}; + +const CURRENT_DAY: u8 = 25; + +type Data = Vec; +impl DayImpl for Day { + fn init_test() -> (Self, Data) { + Self::init(include_str!("test_inputs/test25.txt")) + } + + fn expected_results() -> (Answer, Answer) { + (Answer::Number(0), Answer::Number(0)) + } + + fn init(input: &str) -> (Self, Data) { + ( + Self {}, + input + .lines() + .map(|v| v.parse::().expect("error while parsing input.")) + .collect(), + ) + } + + fn one(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } + + fn two(&self, data: &mut Data) -> Answer { + Answer::Number(data.len() as u64) + } +} diff --git a/src/days/mod.rs b/src/days/mod.rs new file mode 100644 index 0000000..59268e1 --- /dev/null +++ b/src/days/mod.rs @@ -0,0 +1,250 @@ +use aoc_macro::mod_days; +use std::time::{Duration, Instant}; + +pub mod utils; + +// Thanks to andi-makes with his AoC project https://github.com/andi-makes/aoc2021, +// this system is heavily inspired by his system. + +pub struct Day; + +#[allow(dead_code)] +#[derive(Debug, Clone, PartialEq)] +pub enum Answer { + Number(u64), + String(String), + Bitmap(Vec>), +} + +impl Answer { + fn append_per_line(str: String, prefix: &str) -> String { + str.lines() + .map(|v| prefix.to_owned() + v + "\n") + .collect::() + .strip_suffix('\n') + .unwrap() + .to_owned() + } + + fn bm_get(bm: &[Vec], x: usize, y: usize) -> bool { + if let Some(line) = bm.get(y) { + if let Some(v) = line.get(x) { + return *v; + } + return false; + } + false + } + + fn minify_bitmap(bm: &Vec>) -> String { + let height = bm.len(); + let width = bm[0].len(); + + let mut out = String::new(); + + for y in (0..height).step_by(2) { + if y != 0 { + out += "\n" + } + for x in (0..width).step_by(2) { + let m = ( + Self::bm_get(bm, x, y), + Self::bm_get(bm, x + 1, y), + Self::bm_get(bm, x, y + 1), + Self::bm_get(bm, x + 1, y + 1), + ); + + out += match m { + (false, false, false, false) => " ", + (true, false, false, false) => "▘", + (false, true, false, false) => "▝", + (true, true, false, false) => "▀", + (false, false, true, false) => "▖", + (true, false, true, false) => "▌", + (false, true, true, false) => "▞", + (true, true, true, false) => "▛", + (false, false, false, true) => "▗", + (true, false, false, true) => "▚", + (false, true, false, true) => "▐", + (true, true, false, true) => "▜", + (false, false, true, true) => "▄", + (true, false, true, true) => "▙", + (false, true, true, true) => "▟", + (true, true, true, true) => "█", + } + } + } + + Self::append_per_line(out, "\t\t") + } +} + +impl std::fmt::Display for Answer { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Number(n) => write!(f, "{}", n), + Self::String(s) => write!(f, "{}", s), + Self::Bitmap(bm) => { + writeln!(f).unwrap(); + write!(f, "{}", Self::minify_bitmap(bm)) + } + } + } +} + +impl From for Answer { + fn from(n: u64) -> Self { + Self::Number(n) + } +} + +impl From for Answer { + fn from(s: String) -> Self { + Self::String(s) + } +} + +pub trait DayImpl +where + T: Clone, +{ + /// Parses the test input. + fn init_test() -> (Self, T) + where + Self: Sized; + + fn expected_results() -> (Answer, Answer); + + /// Parse input + fn init(input: &str) -> (Self, T) + where + Self: Sized; + + /// Compute part 1 + fn one(&self, data: &mut T) -> Answer; + + /// Compute part 2 + fn two(&self, data: &mut T) -> Answer; + + /// Parse input and measure the time it took + fn init_timed(input: &str) -> ((Self, T), Duration) + where + Self: Sized, + { + let s = Instant::now(); + (Self::init(input), s.elapsed()) + } + + /// Compute part 1 and measure the time it took + fn one_timed(&self, data: &mut T) -> (Answer, Duration) { + let s = Instant::now(); + (self.one(data), s.elapsed()) + } + + /// Compute part 2 and measure the time it took + fn two_timed(&self, data: &mut T) -> (Answer, Duration) { + let s = Instant::now(); + (self.two(data), s.elapsed()) + } + + /// Compute both parts + fn run(input: &str) -> (Answer, Answer) + where + Self: Sized, + { + let (day, mut data) = Self::init(input); + (day.one(&mut data.clone()), day.two(&mut data)) + } + + /// Init and compute part 1 + fn run_one(input: &str) -> Answer + where + Self: Sized, + { + let (day, mut data) = Self::init(input); + day.one(&mut data) + } + + /// Init and compute part 1 + fn run_two(input: &str) -> Answer + where + Self: Sized, + { + let (day, mut data) = Self::init(input); + day.two(&mut data) + } + + /// Init and compute part 1 + fn run_one_timed(input: &str) -> (Answer, Duration, Duration) + where + Self: Sized, + { + let ((day, mut data), init_t) = Self::init_timed(input); + let (one, one_t) = day.one_timed(&mut data); + (one, init_t, one_t) + } + + /// Init and compute part 1 + fn run_two_timed(input: &str) -> (Answer, Duration, Duration) + where + Self: Sized, + { + let ((day, mut data), init_t) = Self::init_timed(input); + let (two, two_t) = day.two_timed(&mut data); + (two, init_t, two_t) + } + + /// Compute both parts, and measure the time each step took + fn run_timed(input: &str) -> (Answer, Answer, Duration, Duration, Duration) + where + Self: Sized, + { + let ((day, mut data), i_t) = Self::init_timed(input); + let (one, one_t) = day.one_timed(&mut data.clone()); + let (two, two_t) = day.two_timed(&mut data); + + (one, two, i_t, one_t, two_t) + } + + /// Test part one + fn test_one() -> (bool, Answer, Answer) + where + Self: Sized, + { + let (day, mut data) = Self::init_test(); + let one = day.one(&mut data); + + let (one_e, _) = Self::expected_results(); + + (one_e == one, one, one_e) + } + + /// Test part two + fn test_two() -> (bool, Answer, Answer) + where + Self: Sized, + { + let (day, mut data) = Self::init_test(); + let two = day.two(&mut data); + + let (_, two_e) = Self::expected_results(); + + (two_e == two, two, two_e) + } + + /// Run both tests + fn test() -> ((bool, Answer, Answer), (bool, Answer, Answer)) + where + Self: Sized, + { + let (day, mut data) = Self::init_test(); + let one = day.one(&mut data.clone()); + let two = day.two(&mut data); + + let (one_e, two_e) = Self::expected_results(); + + ((one_e == one, one, one_e), (two_e == two, two, two_e)) + } +} + +mod_days!(); diff --git a/src/days/test_inputs/test01.txt b/src/days/test_inputs/test01.txt new file mode 100644 index 0000000..8a2eb54 --- /dev/null +++ b/src/days/test_inputs/test01.txt @@ -0,0 +1,1000 @@ +51591twosix4dhsxvgghxq +425nine +llvmhjtr8nbbhrfone +lpbjvpbtdfvtxtdvkpjs7qrvddkzmjtlqtg +3sixnineseven +rfmsqbkms7three +33291six +oneonevstpxxrjpnine7six +75sevennine14mzqljsjfbb7two +three6two9jckvk +zprj8394threehczfkncntk +mkqtlrzmzfsix2ccqsnnxtwo4sevenxp9 +tdszrfzspthree2ttzseven5seven +two3fiveckrsjr +four14three7 +4fdkcclmxmxsevenfiver +5sjnnfivefourzxxfpfivenine7five +77sixfive +twofivecrkvmpcpvzddvzcmjhjlthree8fcrrninefive +fivetwo562five +2ninekvdbfnmjmd6ninentpktmgseven92 +2twonine +5fivesprm4ndqzbqnjqx +7jfq9 +1hgnkmx91 +2jcrmhfvntc3lqnine4five4 +zvqmpjrpninejhqrnineggghcrjfd3onefive +11three64qjjhqdnonetwo +rmvzjnbonetwo6 +eightfive698threenine9 +8eighttrfngsklrkfivefourninefourqhclfcp +1z +fivegfslvcvtvs4 +sfq1twosevendbjq2nine4rzmdpmgcx +seven816one8jmcqqhfnthree +twor4325rsl5 +four2vqhninexqqhlfhfouronejks1 +7hjmmxhdnine8 +1eight3hqgshtb147 +four6sixeightone +twomvkjxbqxzhnnmjxstkgfhzzdsc1fivefivesix +3slhhsfnkhhlxtvqfrhvdzzkvlzcnlxgd4ninechp +meightseven8onegghfsfeight84 +kmxckzqdfeightsqfivefivetfg77 +dc8 +mktwonecvqsxhqrjfninethreethreedkllgfxrxrffzvdbqdj2c3 +8shsxjmqmb3kpvj485nd +rbdlrxsmjvnkttnpfour6twoseven +bkt1seven +threehkjhm9 +threeeight8sevennine7nlx +jmvj4ctqpccsixseven +4fourmtwoxcsvlvlhctdpxns +eight7three4729eightbslrgrhs +26seven1 +7ffseven +9rsgmlgcsevenxkmsd17three +snzn6htcqxqj7bf +ppr7six73znmtmgrzrp9 +61mj7dvddfseveneight +threevfive3fourseven3 +xznsxcxsbggvctqqb82two +five4none +foursevenrl12threegpbvdsspkrfour +9sevensixthreerftbxgthree +6fph +996three +64pkndfphqrglbgcpseven +72749twoeightcpqfzqcrns +xnzzhvssixtrlfftrjr5two9threenine +four8hzkkrpb71glpqsnkvcb +sixsix1 +9lfdtwo4sevenjhv911 +chxjljdh8jztwo61three +twothree11 +hft2dmntjgzg1 +rtfckbnqxfdldgttbltdr33kqhxhkeight4 +ninefourseven4nine5 +25fsmkbsevenpzhgdhnxnbsix3 +bd1frglljngg8one1 +34six +96cpcmztffqdrfrfslfour4lpnbmrfm +psxrlseven2zmkvrhckqcxdl91 +ninexkfjcjkjb8three4xeighttqsrgbmfdt +2threesixeight1 +ggxzf3gkdvsqxggltxdhdbhm5 +sixtwo89six +eightnine3 +threemdrhqpone6 +sncpmhtwozsfpcpxgq9three +rtwone37three7xhzlckfclbltdkrgqbcrhpqzg +68sixbgsz35 +kdeightwo69 +four7bfq3four +sixone3fivedgmrrvjqrr7three7seven +sixthree6 +onethree3onevdtlkrfour4 +eight4seven5cszgblfseven +two8ddmjtg +fivejnvrdmghnvrmrjeightnnxpv8 +2threezvklqkpml +two76cdfrmfckbjc +zqxpg42sixeightfour76 +3eightvqpxzsn2bnbkrfb49 +eight85five +sixngs8xjseight4 +296pfsgnlpbmngzlrrldznmrj1mjgvtxkr +six4bvrdgxh +34eight56eightsixhphnbgscfb +two4twosixthreeggfdzlbkxxqmtxtczdfj +four4tqrjxxcrztwoeight +one73zpfhjvjh2 +7eight35phcfcdzhhpzn49eight +zqcmlcqfneightsixrpmmhcrssk6dclcpmhzqhrbcnkxjdfjvrf +1threegkhpq7nfrksvm69nxpvgvthfzoneighttc +gtjsevensixsixb9 +nine73nine +cbhsddcmzmdv2bnnffour +85hhqkqmv +six2jfxfgpg9 +fiveeightskrn1 +khvcf3five8qj5 +sevenfive7ptb +tscc9psz3gtnsnksn +xzlh7zfivefivejgr +one4nine8three3 +2kjkbbvftktkvpbp8gkcnrrkr6r +565three718five +twojjgcfz3fiveoneftvnldxdhr2 +13three +phbvxsnnineksttklfljm85 +tvzpbgzhmbnine1eightbpjtkflllg +7five3 +2623four +nvfchhrbcone1nine +mjv5vtmlmsfcfmt65five1 +7one2 +4six4 +dlbzctlxpjxxqk96pmdgdfblbfjnsxjmkoneqgeight +seven4nzvxldvzvjpzlzxb4eightnine6 +2vzblzhrxnc8qtgvxxzgkeightblqfftjtnxrbpvlsgvd +thfdzxfqcthqfcnrt2 +qxfpbbnfczfourh4twodjrhp +4m2fivelklgfgr6three +4tsbs74 +one6twomzfncms2lrgzgsspkjmcgqxp +six5fourgzrhbvdnzzllh +9seventwothree8sevenfive4two +7eightdprzmjvsevenfourfiveeight +twosjzhsb29lqzkjbtshp5 +8four6two +bklkfctjb84one +sevensevenfive7seven6 +vklhzsjz5fiveeight5 +fourmlthreecjspnchzrthree8tb +7gsfvptnpcl2n82 +58mdllbtjkkzcmdgzbeightbzqmfour +2vtlzkzlsfive92vbpnq +jbcgqvr28ksbphtfourfourfksnxjhtb +21ljfcmgtmdqfive +threejhhstcxfive64sixeightthree +7tqpqngnrmnnxt3pjbcqjmktvj +fmqqc9twoone15four +5six13dnkxtqrxrh +8xxpnpfivethreeh5ktnpctlqrgtl +9hgmlnzsbmbsbjbg9 +8mnlsqkpqp18jkftxzfcklsgkvjr4threergdbrrzbb +eightglhhjsfl819lkzlxjvcshdtjhxvjs8 +gshmsmzfbcrhvnfnpppmmfmngcv2snrponeqgxvtx +9sprkdkk3cpjzlzphfdbhczbpssix +nine8954five +oneonettsixckhgprszjckhx2 +seveneightgnhzhm9pkthree3three +5mjkpk +sevenfour832five +fivellhglxm2twoneq +fqtfqdgeight2lfklmhrnfive +flqdfkmlfthdvzncvbfour7gq +793 +fourninetwojrfp4three5three5 +snnnjlhcnine84eightjthbkgtslhp4skglpzhz +mnxfgpg862ninefive26 +bmdtbfjonegprzlxqlkb4mcscbqnv +nine9fourfourfive1fivethree2 +eightfqrqz8 +fourpkd1bpxczlzthreecptvjlgzfmtqnr +25onejrksrlccgrqtkcsmzccnine +5nine26three88 +25four299fourfour +hlrbll8vnhjlfjrkd +ffourthreefourkgjbsnbc6 +eight8mpqpsgxthreegmxrhqkqbhgstcz +three82five1 +4dxvzg29fourdsh +ffgdlggchpcq7four34lfive +seveneight9eightnpjmh9eightfive +bnjrnsdfjg6 +76five +five4nine6six +q339eightkhnjsixttvqzlbchleight +koneightonenine2nftlzxnkbctmjvzk98 +511 +4cghqsix3bqr +958three +seven2vmrkjqgrlb5 +9three6one5jdrnlfour6nine +four2xdrqqsktone +1sevendxkhzglzxgcmonesixrplhlplhnone +6threefourhshvnszlqrzb +7hctfsqf2jzjkxbljg +sevencxncnc68 +49lfdkv2zbhddmbfive +sevenfdsmsmmgzv29two6rpxsgpdsjxljcthree +hgfqhdfb4mninesevenzngfljcvpvvkmghqxoneseven +gtlqcggrp6lzjfive3rtrqvszcrsevennine +qvhztzsfddxbbxsr88 +nine5kfnninelknrbf5bjrrthv +pfnmdzksjcjcbvlvgcbheight47 +sixfour8 +cdbxkcqseven1 +onefive65mdbrnlg +vlcxxcxhfkjhm6two5 +bfpnhlzq3tgcmhd4 +6three69eightgmxsseven +three1six +fourgqqgvltwofkxzmcfdmz4 +24four68nxs84spjnpfsdv +m1threesvlhmgsbn +three7jcvclg84sixfjvvtkfk7 +hznlsixjqnlvpmninejrvfpplbm5 +mgfrn6 +xb9469 +48713fivefour6 +sevenfiverzlhdzr59lbtvltccgbstzxksb +rxkppsh771 +dldhcgdvtwosix3 +one4phkrftwo +rrdrpfddtphv88jldqht +vgcpcmrj6 +9zdqnzlthreelxzmlhsgdc1pddz +669onesix +5eighthlbhzlpms7qvbftcrone +8mttfjqjnsf528hzvljcx +eight9tseven25csfeight1 +7threethreetcdxnhnnftwosevennine +54tsrjthq831sixcmxzqg +61eight9kb5 +qlxmg4zhcpfxvrhlgjlcbxvxsix +95two +four75eight +6fbrnsflmxstwo +25 +eightcdl979gxzv97eightwogdv +eightgbx85sevenseven +3rfvzeightsjcctzxvtcrsix +fivevqgm14 +dbfkfnfbckncvseven83drnkzt1 +11ninesevenxlxfr +4ptvstqt7fivebznine +5qmcnfour9 +four5mndpqsmxninethree56ctnlcpbtzmthree +four2bqnbcbninemccbzhrfourbgrccrsznskjn +rjdfqd24seveneightwom +9ztmbftvvzlqlr4 +qprftkd3vkfhtwofour33five +fourdtmjbtvvdxdhj5 +jpttch9 +9jkngffbxhg +one79twoonesix +2eight528nine2 +psncczvbeight3 +4seven7nlgrlzsqjsdb51 +five98lqc8 +nine377rcgvvhdghrg5scmff1 +8fvcxpr6kx5cpcrjdpnjcsj +ltlbphmcc7six2 +4threek +8hzmfhrnvt +tbmzgpgkkxc89z4sqpd +fivefourdjpdgfournine37eightzqbpgcnfrq +sqh6llxn9z +five1sixrqqclkhx334sixone +289 +nqcspsixone822five +bq19 +mxdqptgg99982vpthreesgdgsf +vp334fourhdtjvlgtwo9seven +3five157sixgzgxxfive2 +dvmtwone3sixfivetwo6ninezpjv +9trtrdkldqj429hsrtd8 +6three3sxsbbhpgsix1 +3gccsixcghh +six4nnbg +ppvljnh5 +sevenmghppptnrtrzh7 +4sevenszkgbpldhklrtclm +9qjsxmrmdm +twoskbhlqhnkmgzkpvtft9five2qrkeight +1v +988fkxgkbonerglfsknxr5eight6 +xkdklvjbhv977lvlnmrj3gnfjrnttm +1fourthreesevensevenxfdmtdone +fourmg4xtvjbfmqkhtbslq1onehmtrtvsslrjdfncr +twosgdlxsixfive1 +bscpkcfp8mhpvzn7threefqgcpfpmftsrmktwo +sixeight4vthcxd48two +1ninehclngfzbktvpjone +one2sixmzxdqxvg4oneoneztbmplctc +5tkhrmfrvonetwo +lfgveightnqdmrv9six +zzgmcr1jgzjjljhqmtnbrnbt +one3vfour7eight +fourseven3ninenine5four +92kpdtggsb +onecsfdhsn3gqmtfhc +bhflvgdfkhprqqdgs6one +1five93pmrhdvmgp7onesix +56mgkt +rksixfourfourthreefive3fdrbtgmdt2 +8one7 +rgvccbsrphxkmdgjkrjjztdzzmkgqbcdkseven69 +three4eightsixjgbnthfxvltwosevenxjkd +5cffmxhdbtgtlffdb +8fivetwoneb +5trtgsgkvplfvqccknine +two4gjjmptwo +two845lltssr6 +six8xngnhbctl +nhbxglqfjrhmhneightqmvlnqtwo53 +7nsr +one2seven +8eight2jphsz9 +9fivebv7kmxqtdvxq +ninec4four +vrtczjzbzg8bgf +pvhnfhvsixbppv4oneshhqp8jprm +tnxqmfive1khnmb +onefivechd69nine8 +sxeightwoeightkpjxbkrvtbllbxfshseven4threemkbx7 +twofgcfp7ddjqvhrbvmkjdsltfps9bttz6 +sixninezpsbrvhvlz4 +four6sbtdldjcjrn7 +79drd742 +mmccvnldbhxcfnpxrftqc1sixlncsqs +jjdpbsm2 +9fnhthreefourtftrmqhkmxrpnnhbxvk +five168sixs4 +65fourkpzbhhgsghxvhlvlftfmck62 +8fivesixsvmzjhdmdqsixntzrckdk +gfzjxptdqnineonepqqsldlvjrg5pdgthrslpfcfive +38mmkcseven2four +vghjzfchxsqq8fivefourone1 +tttjzcm3 +jpn764tsmrfvtjp7four +onelhbhkmf7 +5ssfzvfrrvgleight71 +52lkxkxdjch5 +3eighttffhffht4 +832 +2gbhzrgchz +threessdmstrhchfq73ftcfbrtrvf +8xqs9scc +xglmdxq24two28kcckrsqpzone +16mkcl +ldhmz3 +xzvttst5 +drkkc3 +8zhbplhvcr4 +one1xzbrkd59 +xzzbsnktjhjltsfrrrz3four +8vlfgxgzsnfljbnnmlpz5nine3three +99onehzdqm +5zdtrvccn +75sixsixkxldjnxslnvggnvvjbzcrqveight +9sixztkk +three9sevenone +zxsgxfxpkpp45 +9hkshgthreekkvrplztccvb +sixbvdxrhrc16 +1jbjbqhnqgg5shmgfm9seven99fvpnrrpks +sixstsxgbmcx12qhhdr +9trfxqzkdstwo2dzone5three +one3drbrbtsevenonekmfdlffive +nsnchfkxttwo2zgpbvkvktwofour +cnzgzdvfm9 +8fst5jsgmxhbgklninebkkrs577 +xskvpkzcx2twoninebslrbdmqseven +qlbpjqmptkseven9lqmcdvjcxqsix4 +eight7six2 +33h8rxmtlctrz67dz +3rmkrn83seven4bxthree +mmznine55 +1onefour +two4fivenine +fivesevenpp2onexcxjvllseight +onehtfzdtjxskmzsbc4sixfourz +mfkttfxqvtdqdrl93 +567 +qqpnrfninetwo55eight +4qqmlrfbfrvpxvphjpbc +nltblqfourbxxrrgvgkcbb5hzzgfour5dmdxl +three2526ngfzssixsixseven +1tffj2fcddnfxrzx34 +59jd +sevenr8sevenvxltmxcssttwo9 +svklhbtwoxbbmk6ninekptrln7sixjqbjgxdhzr +krbmcxpnxfrn4 +pnkfournjgpggxzjd97two +cplfrzbgbleight2z7six +ninep8xxxnine +xqlrnzlz92ninehblgkztwonerc +1nine7 +one8three361two98 +nine367qqslvgcxftwo +3fournine3twofkcdmx6 +1b2ninethree +6one4sevencnlsqzzmcxkxfnqjone9csmkx +oneeightgh44eight66 +6x527threethreerggpfcfqlf +eight1tnine7gjvtpvmqb1seven6 +six45six7fourmnhbskfthree4 +ltf4 +3hgqgdsmnsnine +hjqneight5threeeight +onekxbrlhkxgxbmbn6eightfourltspzkqdnone +llfphldmghqrcnineeightonefive9vldfskjbhl1 +sixpjf5gjkdqkvvqhj +2five685kvh17four +pgslffcdvsix7oneightcs +lznbhzonefivezgj45crzfl +twosjggnvbxcmtwo8four5 +fourhtstxbkcz6fclrvgfrgbv +nine4ninefourfdkdmjvlvzgsnjsfseven +three318lmzcfive57 +5pmcnc +861 +qbbhpjsg5r2 +jtxcphmrjnscfhqhk6seven2threefive +qd1sixninefour +four7fivethree65 +7dlzrddgrgfour3hxsttlnkbm29 +four4twonine8 +5zblkgmxninesevenbrhntxm +xfvl9589 +five2seventhree6gbzb2 +cvmqqtskxlthree1 +sevenseventwo9xthreevzfdmfqvdf6 +six72sixeightnine9 +xrjnqqtlxz8bbgsqrseven6two9 +29seven153eightzl7 +sevengntsfmzkfb8 +1fiveseveneightdbvbtszptqlnineseventhree +four6rks1v +52eightninez +5jspztgxrpt +bq8jpttmm2 +fourctnlpkbrsfive9qd +85twothreebbpsjcfh12oneeightwov +xhxgfrcljq7six +zh9 +9sixfiveseveneight +rgqzbrldqrhtzjbsbzggktnc4nine8one1 +xcmfbrprxr72qqtnine +tjphvghjfthree1one +twosixeight9fourone +eight48xronebc +t6two34nkmfgmsnnvlbq3 +eighteightkx86 +82b6fcvvxpsx84 +threeeight66two +six4pkbhtxlteighteightsix +three7snrrdfb8sixninetwo4mltj +5jqbgnqthreemjzgbvpdeighteight8tvf +nine6zrbpsfrfqnsixlkmxrkxln +2fivedvkt2998 +j2eightzqspbs +mmp694eighteight +3chzvrsrhzrcrtwoone2 +qqqzxcqmbb8nine +sixsone7onesix1vvrqszvmnrh +8threegtfnmkgthreesixthreeglhzqq +threefiveone2three6seven4 +sixzkqbcg1 +two7seven85five +cfhg3zmpfd644 +btsrthdbkt6242dhl +hqlgnine5two +2zgftzs8twovt +mmpcgngmjjvbnsix35threezxjjxsqnlv +lmmqsevenvjcvcqj55fiveninernmn9 +two7rgsbxnmddtjd +36foursixsevencjffhxrsq +4jxzfhxrphtgbjqfivefive +99pqtnvtwo9crsbvsnrmtpltthree +threedhk3hgvkp8nvbk6 +fivelkxdgvsq6twofive9hzjkdz +ps7two9pqnnnvqpdbcpttjgrjl +jjcfkvqdcl4twothreetwosix +47one525 +23three2 +eight9four7gddxxfxcm9gdmmnfvbxr1 +745 +gkblgnrzsblklpb5bc8ftrnineoneseven +jghbzdlmqbjm5glsqfffvzksc +eightsix1five +nznrzbdsv4 +16vdxm2hlrgtvgtdrbsix +txk26eightnrjxrrlvffour2 +threesevenddmrsghzsjk2bdcqx +eightthree9blrfjvrpfnjcfqrxcnine1 +two7three81htcdvdnxg +seven1three6 +kplnzdjmkcrthreefour38 +bvf1sevenmcdfnv9lpnnh +6eightqcznninesix +two3sevensnpf6v +eightthreeeight4986 +9seven7nine6nineseven6 +3two91 +six97three26hlxczxp +65twopggttdksmtsblfpjbcphzc8eightwopx +mbxtfmkfourrkspbkvbrnljsjhzpqsgtvrr2 +sixseven9one5 +three7twob98 +6c +5797xjtwo83 +chrbcms6vpmrpctssbvqmvreight76 +mthgpstgchone6two +597 +1799gjjdgzhg9eight +vzqkgj27zpvxltzvrfcl47 +381lmxkxrfhbxqeight89tdhpbpqnx +sevennine4 +29ninetwo1tvhpr6three +dlbqfsdmdxhszmcpzshdthree3hvc2csbtfthree +19khmsnhone +twofourbtlsl1 +3sevenssevenfivefour +8seventhreesevenseven +three27rseven +1jbdtfcdvvbzhgfsixrzqxkfktjmhkhfive5 +onetnznrrcthreefmmfivesbsvg4twothreetwoneqq +sbjfxeight2tzbdkvqmmdd2 +vc4xfsjmt +bfive9dmqgfnzvtngkbvthlrfjmsjtclbzbszfour +31nbzfqvckd +26kcdkzbjnpkpljxhgnhjgmtbrvkntjbqtwo +3eightthree +eighteight88 +five6bsix3rkchjzlfour94 +one9rrrbx3eightone +6fourkxpdmt852eight +8xhfbqqklnfive2one +4ninetwoone6 +seven5seven +1vzprkvk +mczkrcdx7rzvgdd6ktcnktpfkpp +sixnine988one +68shx +jqfoneight4sevensevenhmcrqjlzhnl6vcsqgmgnnxqhvchx8 +1fourgqbjvjhzdppdq +lcxjghp2 +sevenone8nlh5 +nine94krdvjv5seven3 +ninenineseven8threentgtlzfnrfbhxxzn8jt +nine37 +hbzztninexrrktrlktwohr1765 +5sixjdvfxdxslhthreethree +2eighteight +dfnbf65 +4onenineqjnlpninekgk +oneoneeight974 +4nqqm83ninezkmbcblh +7tpjhbeight1 +fiveninefdcnfourvdjcnsshnine3fivefive +344 +crnqzpztstxvcgjdmkp6 +hgcvbscglp63tpfive +seven5fivesixcbtblmlsixgvfbqttzonedld +43threep +zshzcplxth4nine2zcgsbzdlxtzbxggnine +cqtwoneone9ckhnzmzfsevenfour8j3 +fiveshs8ttpcsznh +hdpqds3seventwokbzlhxpj +joneight9 +hmxxnxhqfivefiveseventtgxqcxc3 +59sixoneonezjgbshzpfg +1gqqz +8two38fourjm7 +1fiveeight1four69 +88eightwoffg +threenine3threegqvtvtxgninerdprd +7sixthreesixsixmgzlqlbsslseven7 +threegmrbgmsfourfourbvmhzpctkd5rzx98 +five193146mmqmf +41fourxone +2jhgqrkmmgbthreeeighttxg +5xxbgpmm3 +5twonineffsevenfivetwonine +seven98 +one7532rcc +8djrctpqvpcd +eightqqnhfnn2pjsevensixx7nine +jkzjrbhsevenseven1 +4tnkqxkl69nrktzz +six88five9nplcm +xngrktpqclsmkqggj935oneeight +8fourgbns +eight47nine1two147 +vpshrqvfkjctpmhcfc22eightmqdxlsngbfn +rfive1kxfbcpq +twompnqfvqxqninevgvhqlpfqc15seven5eight +9fournine4ntvhbkbhqn48mqhqhltbjpzrrgb +ninesix2four937 +6cxpmgqnchk33mfdxfqdhxbfiveonethree +9mqmldz3kjnsgfoureight +4dmxtdvqvrvdrbfour5q +8eightmcsbfive11four2thqxmbktdl +zddeightwosixlmhrzfksevensevenfive3seven7four +lvgfftzceightsevensevennineseven5twobkd +nine7three +zlmfvjfrxrrdmtxg965msd4 +two9five4sevennmrvdgzrf1bjdzbtrdlb1 +tjbnzrpzrflpfsrprsg9 +fourzcqfonesix279 +75fxlvc +ldvmnfqpll7fourqxzpfctfxp1nhrhpvkbtmone +seven4ninebpbdfjsxxmbqvtwo6one8 +fivefive47seven +rxeightwomgzhcbnlmj72 +eight34xhbcmeightfour3 +nxszsbltqhthree4 +7threefiveninecqzfdjpdcg +kdfour2 +ktworhdsfhhfqcrbzdslthmt2 +dpjbqmd7689 +threesix8 +61113threenine +ninenine77fhtvsmfh23onefour +bccdxg5three +2eight1bnrx +eight7sixdvvrqgt46874 +35onekpnthreeprznkpfvlneightwom +52five6 +one3foursjnngjznqkfqklhjn +eighttwomzrxzfqgtdthree3scbjdnq +9fivecjgdnkxrt +5nine442fb8 +nine6one +241fouronenrlb6 +97sstjznxfcpccngfl7cxmzzcvptfive +gvtsxbqzcone52 +5845836fourfgrvrchv +7247zxfkqhvd +fourxtwonine2eightfive +znjxhfour229three +fiveone8qjfmjbnnc +1sqdjlfiveh +fivegxfj9pfbxf5 +cnxmvmcrm4cfivedcnrnneightwosd +four3oneone +hbtfjlnplcsrxkt6rcqpfdseveneightwoh +jqknine9xfour43sevenbgznxhh +pvtztgjv47 +one9oneeight +3npsq +ninekmzdninejk7 +6threeninefhcpbb +266 +pvtwocthreeeight1 +lpmshscqztgsgsb2zfcddqnvkseven +dmoneighteight78oneeight9zqszseven +eight8eight +fcdlflbtfiverxfx9 +five7six5 +hhvmsspr4four8hhcjdhnthree +three8qfzjpdztwodrkg5cg +919lhzrx1 +foursevenfourfive6lvfour57 +crzmt7 +two71pzxnxtjkngfzm +sixbmzlvcnine9 +hfbeightwo3eightfoursixfive +j5twotwo57nine +rmqq3sbjqnqmdjhmbp91hp +3threezr +fivesixfour81jqxx +2bfeight7nine7 +fourfiveqjfkzsdxmgjmpmtone3hbgcvq +7nrshoneninetwocpphnrfprz2five +twoseven1bqjgdz2 +one7qonevmvb4sixhsbqgp +fivethreethreegmdh1 +lv7d89 +2tbrnmflxfhhsixddn +6sevennlhxsevensixt +pd2kkfvtjbbgvfrcts +djfoursevenflp2three6 +bppkvmxcrvffssdxgrr6mtlthree9six +4szvlsixsevenxttwo +ppggtqx8 +two3three6five +threegplsmsjzjfcctwo6tkrkzmnk +srtj2tsnxthreeonethreep +szhnzblxslqthreejveightfvrrhbcqjqmmvczh1 +five2five25 +btvlzjhrxxdqdmbbcrmbffpmlmr9four +1jdrpjpvkmmseven +85bbbht +2onel2p +hdsfftwotpqcgdbdjbgnvfone3eight +fourdgtccrxdnvfszgkjsj38 +twonine6twotvqxpskhonejsnpmtpctjfive6 +29eightcgskgdnmnhkpp63seventwo +2seventwofhktwo9 +8kx +32zxdzvfzjvtwofive9sevenfive +2seven8pmdnnthreehnvltl7 +33sevenfournvnjp24 +two3m9kcsppfour +hjfb9six5gtbppmzlfmfourtwodqzppcldd2 +4qlkz3 +4ninejzgpnkvq +fbcpkspbcgrn6dmclmnqldeight +sixtwodphp833 +sevenonesix5eightsrzpjzpj45rczn +9fiveltfqjmkzbrm8oneightbq +mxfqsrs5fpeightfive7mvclrlqhhkptpbqq +438rktthreegvhltmsv +dcl9xnntwosixthreesix7 +csxhsvkkkhfour4slkvjk1eight89 +three2eightfiveone +17kqnstzmbthree9seventpld35 +ztxjhftwo1f +ccfvc3three9five +fmngmbkjppkxjnzqbqldmspbrrrhhrkrsix4 +9fivejthreepjtmtfive455 +4ncdvqfiveonefive5zxgjpptbnbsmbrfdpqrjnx +ljctvztrxncpxsdtttwo2tnkkt +five544mlzkztsblt17fivesix +9five35bkbpjc +6fourtwo +nine5hqlbjp1onenvvnvsseven +5qbvsbxtktqhgqxfgprsgsrrpdtpcmr8 +hhsgjxlz3gzxhz14fourtwofq +6three8rmtrbnhvr +9bhxvmhldnm6 +vkjjpkgdmxrzzfour7 +rhvrsrthsevenfour3eight5one73 +msqdzmfzfeight6qhrvzcdxfourrleight84 +pjtfflgfhqnpdk6three63five9six +1sixcpcvbnnine9eightonenlnjfdfdmxcmcb +zpgrbsevenmqskhgtrm6 +8hnqmmgrnsxhrxjjc2qjbbvpknh82 +5six7threepx +bmlxvnqrxtqzkdlnnine5two9bpphfdhqj +mtxrblghxrsbxhmgnbvgvffivenine49ztktxvfvseven +tnqclrllq9fivebddnv +56seven222pkmqk +74eight9pfqghssgk +nhmzdpbnlreightqgljp6 +nf3three2 +eight9glknfltsqsfdsixfour2tcfnzfmf +four1ninetwo4sckcbgzrx663 +eightbcfsevennqznfmfivetwo7qgdjtwo +64three +8vgggxllsrhqjh2 +7jhthmpmlxlptgbnscxcthreeeighttwo3seven +ktqlfveight1eightjnfxvvjk +99qjcxgnpdvnbjqprbqnsjone3ninenmxkslmdsx +9gn2 +qtlztwo7eight2 +nvzhsjsdp4sgrbmx36 +fldvmnlkq52nqtsccgfgtwolm7mmb +tzvgkfx6ssnxfeightwov +rrl8pxjj6onefour +3hzhqbrdspfrn2eight8 +jcrcxtqh8three4 +ttwone449cxvvljcpzpqmngtzslnq +3gnkgbjnsmxv2fourd8 +9zt +kckeightsix8oneeightsix36 +vjcg4p +jkzr3 +twofiveone1four9seven5cnhzm +6one6 +s2twosixfvgrglfive2mtkrxznllj1 +cnhbzthlzllgghkxjgtrph45 +six5two +nine5threefournine +1rkbvrk8threevfkdbvxgj +28six7dvone8 +74seven +6threeseven92rvpg1 +hxvthvgk8vhn6kmljfnl +fourztpprfkonemnjbsfdjlj8 +djnhxfxxl1five2threezgthn7 +vmhmlgljrpsix93sssixsevensix +dqk8nrsrk +jjtdjkfivegpfspqqjthreedkntgjslqreight7n8 +8four12 +8vlcfsixthreelvvdv +zsjqvmghp6seventwo +6tnjfl2cfzcbgqp5 +vgjxleightzqfklqdx4vlpvp2gqxdxpzk4 +12mshtkbgsndrdx7three +htckznt2threeninej4fhmrtthreeeight +four8hdbrmptjnine +2xbk +ljgcscxxjgdbpdpzfcchrqnmqqthree8 +three7six51 +four5five92ftqhjhx +eightfthreebzbhk4tceight +2fivethreefiveeight9fbk +9four613two8cnpzgcrnmc3 +fqfive38 +nine88mkthree99lkvzgvpjk7 +784dxxcpszbzkdlsrgnnqfsixone7twonemvh +onetwo5tzggnsix2 +xsclx3twojhslqrnc32pxfqsjxctnb4 +zrdbcccznzlfgz8jfpgllczxtnpmp58one +four21one925rntdr +38six +seven3nfttdgkjhklmdqbkv8threefour +75kblsbqhonevsx +fourztseven8twothtwo +gmktvrd44twonine4pnbrkhhzpl8five +nineslj1six8two +one98mxnkrbv +9zmfour +8dkfmthstlqvs9r +cjmcjjjvmceight9rslcctdslsxthreexsjbffnine +5eight677 +4twothree +cvqbrkmgthree4tbmzrhqtpx +24twoone +nphprsbcfhcfrggktgzgvrninesixdnchjd2 +9sevennmxzpjbhct +sbchgzqgdk8twosix667 +hkjlpqgtmglv3eighttwo7two +gflktjvlfivetbgmbsxzdpdxjdgkzvn851sixfour +five2ffivefiveonekldfpcfjg +mlnvqltfsixnxqxsgfpxrpsxmmlnzsnineeight5 +six1z +sevenpvhgctkcvgtjstwoeightzgqlcgq1gxzj +fivenine13rbbxfs1zpvrstlkceight +6zmcggdqbfptwofourfphdbdg99four +one65ninetljqcfive8 +six57fourthree4fzctnjzcdm +181one +3one315gftsbtnggrq +gcdvnzbvv55onemgcftkmzrxf +697cnzql8eightvdsngsglkfour +fourfiveqjptdn6 +pq83six7fprdbzvmgrhkzmjmxgnkcr +nineeightfivesix8 +fiveseven931xhtnvrtr9three +seven9ninetkhldts +6threehlzfbppqmhrtxnfoursix2cxn9 +mphgdnggjt3flmdz14ninejjskfivenine +boneight7one19179 +npdjlhskrdmvsvhtldsthree5tpt +6rvpnvdqqpkkfvgclctqcmpdfvftt68 +91four3twoones +seven793 +8h2four6cdsevencsjvcgdlrj +4bzxqvp74mmdvjfdvq4fivec +31tlvlrb75 +nndfct5ninesix +ncqsnf6six +fiveonefour8 +sixtwo6zjhvhkoneeighttbfv +three5dcpmcsnmfhtwo5 +eightnqqkpnine4ninegb +lgnsrbbtkgtwo7eightthree +rlpcrnscknpjpqm2qqpllrzk8p9 +tvpqncpmffpmv9nineninesixnjbhbhgdlf2 +lzrhjdkfhn1 +jgjfckbrpvc3fouronefourfive3eight +twozhkpzcdkjfour8eight +5sevenfourtwo4 +1sevenfivehvppxvxvcnsnhdrflonefour +4pshnstwo46fivetwoone +three1onemdlc8pcqkzsrnhqone +qvgfvfxnrp5bqdrlqd +jzgcnslrfxxc72bqqqrzltqm18four +nmjlnxktmvzzrgkbrffourdddbbt7eight +3two9twothreerlztqftcgkznfour +8fivefive5tkv +4vtsnlclbb37eight2xprhpnddzj9 +8hbmfjxmqckxqrdjqxrnhg +onef6 +8fivezcjtworgbxrfkjkbk5mdkdgfbnkkkljlv +5six6prgqhbvcrr1msjcct +mvzdrmdlrsdz9nqmbkb6286 +367 +94vjktbt +twofivefour7three454 +2n +six224 +z841dzxcthdx +fourvnlj7twocchdcn976four +threexmxq5five +dscbqrjqmssnzbbdtx34 +8sevenhrvzqskpczsfhzrhrfch +4qx +6eightnine +cnbxjrbsseven8hbdfmjvk6sevendrglnfq3six +82threengstmq +19f +cvjrhrnxnfiveksrjgh8 +2six1dlpdds +5xzhhnqpnsix8qthckdhggfdfour7 +9threetwoqznvtb4 +one8dseven +3fivefourjzbk4seven8tvqgvh +9kffjzvfxtwofive3seventhree5 +5onerxdcrrlgvfbrvrphnine5eight +614pnrbdp +eightfour1cvxjbztxgzznlpr8qbzkz35 +four5seventwo3 +9fourzzjfdqdkmxbeight41eightwopr +eight6rfdhz793twonevkf +vmrkgjgthreethree8ninesevend6hmjmkfdqz +vqstkklpj4j8 +72jglrpp57nine7vcr +sevenfbbgpxlvk7tmlvtjsixdtrnseven3d +5jlb4 +jhkslf976fpseven +jpfive1 +two2jpfztkeightssfsevennine56 +seven6rjchpgvmqgrxdddq +four7eight +zqoneight9mqmjtjcb +ljcmvjf1ngrgzmttttqprks2kphjtgqvmeightvfmzqhdhgj +77ninejjxxgbffive6zzgd +4pthree5zp3one +mhdzcmmsseven4three3bngxxqzclpkmcppxtwo +bneightwo33ptmpcbfournine4five +sevenfrzffbklg4dmdngxmsixnzjgpkngsfivetwo6 +six2onethreethree +xhx4gvkbtcjbqvrhtwo6 +1mdfvninevchtrqthree +onecrnninedjchhzkmzthree4qjnclshjlbn +threeccgbz6sixlhnkcpfdzk119 +61dtwo +ncsbdxnp43jlrxfhgzlrfzqjkzbkd +nxfztwo9lhcc +mds9vkninehzcdk +twofive4jdcndg +374 +threedrsh9eightcqkrslljl5tthree +fourtwotwo8kl1bvplqfrc7 +threekldmmjkmc92sixsixonenine6 +bllccpcksq59zznr +n35lxtnq3 +25hmzzxv +38sevenkqgtfcpnj +ntfceightmvhvnzjvv37bsj +4sixfvrtgdsjvxsevenfouronefiveddns2 +eight29lnznht2oneonekgjkq +4lthkrtj1onesevenninerfplsixfhxdvlhoneighth +1seven8eightgxzvgmnqj +7qfqvxcjttwoeighteightqqsgbsftseight +slfqkljnine1oneeightlgskmlqgstwo +sppqhlxqzkdtdqc6qtxsssdcf41 +tnggpjrnmrrgzpssptpk32 +6pcjglgdknjoneightssb +ninefivefrglbccjmrchvbpsgxrq15fourfive +7five1lmndzphnj4fouronesj +jvhkb24htseven7 +7sevensjnrvxmsixthreesevengrh +9h5xbdgpdjjkt1 +c14vbjvcv +ninetwohfbm6fourjpgz676 +gqdtlzqvkplj48cxvtvjjlkmbstcfxd5hkbfdgtf +ninelpxd8eightvmmbvgmhs3six1 +9mmlj4vvcxlnbsfoureight +nzrjlxqhnffour1 +hl5fzsxdbvklx4five +182ninedghflcgrqz4ns +eightdmbqddsqmfive7qshcvdjx +vdfzsevenoneone4ninemhclrkjspkxt +2three1qzqhscbvmpnine86three +1ninevvlzft +1ghfnrvkn97sixqzgtm32 +sixnine4rsjghcsznfvs +ph5mzknlknp5 +3eight3twotchnsqdtbkh +63threerjlgv6three +fivevvcmvrqfkdfshxjnfgsevensix3seven8 +21onebcsgvhtm6two +bnjpqcqdzmeight2gtjhqeight +1rsjbbhtkbbfourqzdhlone4eighttwo +6jpnnlbstgjfjdrdgnthreepgh49hdxqghr +five3oneonefrvnbnnlz +gbseven9five6 +nine2hdltdjdp73phzrjnonegx +3two3eightjszbfourkxbh5twonepr +5cfprzgxtf3465five diff --git a/src/days/test_inputs/test02.txt b/src/days/test_inputs/test02.txt new file mode 100644 index 0000000..1cd7d33 --- /dev/null +++ b/src/days/test_inputs/test02.txt @@ -0,0 +1,5 @@ +Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green +Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue +Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red +Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red +Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green \ No newline at end of file diff --git a/src/days/test_inputs/test03.txt b/src/days/test_inputs/test03.txt new file mode 100644 index 0000000..624ea4f --- /dev/null +++ b/src/days/test_inputs/test03.txt @@ -0,0 +1,10 @@ +467..114.. +...*...... +..35..633. +......#... +617*...... +.....+.58. +..592..... +......755. +...$.*.... +.664.598.. \ No newline at end of file diff --git a/src/days/test_inputs/test04.txt b/src/days/test_inputs/test04.txt new file mode 100644 index 0000000..71f208a --- /dev/null +++ b/src/days/test_inputs/test04.txt @@ -0,0 +1,6 @@ +Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53 +Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19 +Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1 +Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83 +Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36 +Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11 \ No newline at end of file diff --git a/src/days/test_inputs/test05.txt b/src/days/test_inputs/test05.txt new file mode 100644 index 0000000..f756727 --- /dev/null +++ b/src/days/test_inputs/test05.txt @@ -0,0 +1,33 @@ +seeds: 79 14 55 13 + +seed-to-soil map: +50 98 2 +52 50 48 + +soil-to-fertilizer map: +0 15 37 +37 52 2 +39 0 15 + +fertilizer-to-water map: +49 53 8 +0 11 42 +42 0 7 +57 7 4 + +water-to-light map: +88 18 7 +18 25 70 + +light-to-temperature map: +45 77 23 +81 45 19 +68 64 13 + +temperature-to-humidity map: +0 69 1 +1 0 69 + +humidity-to-location map: +60 56 37 +56 93 4 diff --git a/src/days/test_inputs/test06.txt b/src/days/test_inputs/test06.txt new file mode 100644 index 0000000..28f5ae9 --- /dev/null +++ b/src/days/test_inputs/test06.txt @@ -0,0 +1,2 @@ +Time: 7 15 30 +Distance: 9 40 200 diff --git a/src/days/test_inputs/test07.txt b/src/days/test_inputs/test07.txt new file mode 100644 index 0000000..e3500c3 --- /dev/null +++ b/src/days/test_inputs/test07.txt @@ -0,0 +1,5 @@ +32T3K 765 +T55J5 684 +KK677 28 +KTJJT 220 +QQQJA 483 diff --git a/src/days/test_inputs/test08.txt b/src/days/test_inputs/test08.txt new file mode 100644 index 0000000..bc44497 --- /dev/null +++ b/src/days/test_inputs/test08.txt @@ -0,0 +1,752 @@ +LLRRLRRRLLRLRRLLLLRLRRLRRRLRLRRRLLRRLRRRLLRRLRRLRRLLRRRLRRLRRLRRRLRRLRLRLRRLRRLRRRLLRRLLLRRLRRRLRRRLRRRLRRLRRRLRLLRLRRRLRLRRLLRLRRRLRRRLRLRRRLRRRLRLRLRRLRRLRLRRLLRRRLRRRLRRRLLRRRLRLRLRLRLLRRRLRRRLRRLRRRLLRLRRLRRLRRRLRRRLRRLRLRLRRRLRRLRRLRRRLLRRLRLRLRRRLRLRLRRLRRLLRRLRRRLLRLLRLRLRRRR + +FGF = (HTC, DTX) +PTP = (MCL, BDN) +LHL = (LJF, BDX) +XMM = (KCX, JHL) +GLD = (RNN, MXG) +HSR = (JPX, CXF) +CRF = (BMJ, FHT) +QSS = (KPH, FVD) +RHV = (LRD, XDS) +KTT = (XPS, VCX) +LQK = (TLF, VJX) +MMK = (VJV, HQV) +RKX = (RKG, XJB) +HDM = (NFK, JNB) +PFJ = (QDJ, NDH) +JKB = (MDK, MRJ) +BSP = (QBQ, JPM) +FQH = (HJD, VHF) +QQL = (VDB, KGM) +TRB = (KTC, RGN) +VXC = (NDK, MVK) +BCS = (PSX, PLK) +FHK = (MLK, RDP) +TVB = (JXV, SSR) +GXD = (KSF, BRK) +MNJ = (MHG, CRF) +RLC = (TGD, CHV) +LBQ = (NQK, MHP) +JLH = (FGB, KNM) +PCN = (CQF, NDF) +FVP = (NKS, RCB) +GHL = (TTB, KLQ) +MTB = (VDM, FKT) +LLB = (VXL, TRT) +RSS = (GDP, TKD) +SFH = (FCM, GKF) +KSF = (VQB, JXJ) +LJH = (PNS, DGC) +TJC = (KQM, BVL) +PRL = (TCG, GCQ) +NBG = (GNR, SRM) +CST = (FXL, BDF) +XXH = (KVH, KSM) +FJP = (PKX, DSF) +DTS = (FFF, DQM) +CMG = (VBJ, DBF) +NHD = (TCJ, DHF) +KKF = (RVP, FVR) +LDS = (VPJ, MPN) +GHC = (DBK, SCS) +KVK = (NFV, MXJ) +NTN = (TDC, VNC) +FCR = (DCR, FQH) +PLK = (GHT, PBT) +VJF = (VJN, PVB) +TKR = (GHS, TTP) +PQJ = (VGB, SGP) +TGM = (JQM, PPK) +LFQ = (QGB, QXB) +RDP = (HSF, MQV) +SGP = (HVK, XMV) +FTB = (RFV, MLT) +LCX = (RSB, RSB) +VGD = (XJB, RKG) +PFD = (RGK, JGB) +DBK = (RMP, RSH) +TTC = (NDH, QDJ) +PVF = (QRG, QCV) +BGV = (TDS, DRK) +VHF = (XTB, TGM) +DBF = (GGT, BRQ) +TFG = (SVV, FCV) +MDK = (THF, PLQ) +NDF = (BSP, STC) +SMZ = (KHB, TST) +SLC = (BKM, BCS) +NSV = (VVM, VQG) +GCA = (XQT, RCD) +FVD = (NXJ, MBJ) +HCH = (MRF, RQJ) +PLN = (BNK, CLF) +TTP = (BCP, SLC) +BRF = (SHR, CTK) +KCX = (PDR, HHQ) +GMG = (NFV, MXJ) +SNN = (XDS, LRD) +SBB = (JXL, BFK) +NCV = (KVH, KSM) +DQQ = (FFF, DQM) +THT = (MFP, VSD) +GVH = (KHF, TKB) +HPC = (MKX, SVS) +KSS = (QQQ, HSR) +NCP = (FCV, SVV) +FGB = (LGN, LKJ) +TST = (DFV, GND) +PJR = (VMQ, HPB) +BHB = (VMQ, HPB) +LMG = (SHS, XJC) +GGT = (TQQ, XQK) +PKR = (QQR, JGG) +FLR = (LJP, VQT) +NFK = (HKG, BPJ) +MGG = (SST, MCT) +BVD = (SNV, TTH) +GHX = (GMK, JXG) +PDR = (HCD, HNG) +XJN = (NHR, CBN) +XRG = (LBQ, MTV) +VFM = (MMS, VXT) +PPP = (LHR, HJH) +FJG = (NLS, CCF) +NDK = (RHD, BVD) +VSB = (LKB, CCX) +NJG = (PTK, MCD) +MLS = (XCQ, FCR) +QSM = (CBJ, GRG) +PTC = (NJG, FLT) +MRJ = (PLQ, THF) +HJD = (XTB, TGM) +XCQ = (DCR, FQH) +HPN = (CQD, CTP) +BDJ = (JND, JRB) +PXL = (VXT, MMS) +NFV = (SFX, BDQ) +SNQ = (PQJ, TNG) +LSL = (KFN, JFL) +SMK = (FQS, FJJ) +QDV = (BDX, LJF) +GMK = (TSJ, SVF) +GDP = (MML, MML) +GRG = (PVF, CKG) +CXF = (MNJ, TBZ) +LKM = (HJH, LHR) +GHS = (BCP, SLC) +XBH = (BDS, NDQ) +NLL = (NJT, QCJ) +MXJ = (BDQ, SFX) +FCF = (QQQ, QQQ) +SXG = (LFQ, TTZ) +PLP = (MJL, FPL) +FSV = (SBB, JFC) +NLH = (TDS, DRK) +QDM = (GHX, GGM) +JJB = (MSQ, MMK) +TRS = (BXN, RLM) +DMP = (RTP, CHG) +XGG = (PCC, XKH) +MCT = (NLK, DTM) +DLC = (JTQ, BLF) +VBJ = (GGT, BRQ) +XQD = (NTN, XRC) +FKM = (QKK, JBQ) +PNN = (RGN, KTC) +LCM = (DTG, GHC) +LKJ = (LFV, RNG) +HRR = (JND, JRB) +BDN = (SNQ, NVC) +RSH = (PJS, LHG) +CTP = (DJM, NHF) +XMV = (CFJ, MDV) +HXM = (PTP, XVQ) +CKG = (QCV, QRG) +GKK = (CRG, MPJ) +LNS = (VSB, GNQ) +RVP = (JJB, LCN) +DVH = (QLD, QGV) +CPH = (PMN, QSC) +BXQ = (CPH, DSQ) +SDM = (DSB, CNV) +RNN = (SHM, LSL) +VQC = (GKH, DMP) +KXV = (GKN, PTC) +TNK = (NMK, PKR) +TSL = (MTV, LBQ) +VSD = (LGH, HNM) +QRQ = (QXH, GRQ) +NKS = (NSL, HPN) +TQC = (JQK, TXF) +THF = (TVM, KKF) +VSL = (PLN, GDS) +SSN = (FTB, NXF) +NHF = (BFG, XTZ) +HSP = (QRQ, DFC) +JQM = (BNP, PMD) +GDV = (RVK, MQF) +MTK = (TGD, CHV) +FFX = (DSN, XXJ) +HKG = (THT, KNJ) +RCN = (DCB, XFN) +HCD = (TDF, BQM) +TKJ = (JPV, GRR) +RVK = (HXX, PRL) +HDN = (TRT, VXL) +KHQ = (DQQ, DTS) +MPJ = (PLP, KBG) +SFX = (KTT, NTG) +NMJ = (BGV, NLH) +LXK = (CRB, GXD) +KBG = (FPL, MJL) +GNQ = (LKB, CCX) +MRV = (RVK, MQF) +PKH = (NPN, MHQ) +NLK = (LTJ, RXP) +LBH = (VTP, SGG) +NTG = (VCX, XPS) +CCD = (GVF, SSJ) +BBB = (SSR, JXV) +NSL = (CQD, CQD) +BNK = (SGS, JQD) +TTB = (KVK, GMG) +FDR = (RBS, TXD) +PJG = (RTV, JQS) +LGV = (LSB, NPF) +XVQ = (MCL, BDN) +BMJ = (KRT, XXN) +XCM = (LXK, VKJ) +CCF = (SDR, PTQ) +GSS = (PJR, BHB) +CJL = (QKK, JBQ) +FBT = (VJF, SGL) +DSB = (QKQ, RBK) +SGS = (MLQ, PXT) +PKX = (KPF, CNQ) +JND = (SDG, SND) +DJM = (BFG, BFG) +LHK = (SBB, JFC) +QLD = (SGX, KDP) +NPF = (VDC, KHQ) +BRQ = (XQK, TQQ) +NGQ = (JNB, NFK) +NFH = (MPN, VPJ) +KHF = (LKG, GLD) +XKC = (XTX, XTX) +DJG = (XGL, BTF) +LGN = (RNG, LFV) +HQV = (CJL, FKM) +JPV = (SVD, XJN) +TSJ = (NMD, MGG) +QRG = (HBD, CST) +FXL = (PFX, HVV) +TNG = (VGB, SGP) +HXX = (GCQ, TCG) +RTP = (CDD, QJR) +RXN = (LFQ, LFQ) +MVK = (BVD, RHD) +PLR = (FVP, DHP) +GRR = (XJN, SVD) +DHJ = (VRN, DTR) +HSF = (TSS, JSQ) +TXN = (FGF, NMV) +CBJ = (CKG, PVF) +GHT = (RCN, NTR) +SMH = (FJG, LQD) +CVN = (PLR, DDF) +SJK = (TTF, SLQ) +LMN = (XHT, CFF) +KLQ = (GMG, KVK) +KXC = (CHP, NLL) +KNM = (LKJ, LGN) +QHK = (LNV, GKK) +BTH = (KVR, FNQ) +QFV = (PRK, LTV) +DBH = (HND, HFC) +CRG = (KBG, PLP) +TCG = (DVF, BXQ) +SJQ = (KPR, QKP) +JMJ = (TTB, KLQ) +RFV = (XBH, CGX) +AAA = (QDM, GMV) +JTQ = (HRR, BDJ) +MHQ = (HPQ, KXC) +SDG = (HGR, RJN) +PNS = (FMC, JKB) +KHB = (DFV, GND) +NXJ = (MLS, HLT) +HND = (LVF, BBJ) +FCZ = (RCD, XQT) +NFP = (FVD, KPH) +NTH = (FVH, DJG) +GMV = (GHX, GGM) +FKT = (CLC, BTH) +LTV = (BKP, HVT) +VLF = (DLX, FMB) +NNP = (NFH, LDS) +GQV = (RTR, NMJ) +MCD = (PFK, HPC) +XML = (RNR, GJF) +TXD = (LGT, QSM) +DKG = (RTV, JQS) +MQF = (PRL, HXX) +XJB = (HKV, FLR) +SHS = (VVH, CHN) +PMD = (NSV, BMQ) +TLJ = (DMP, GKH) +CMA = (TST, KHB) +CRB = (KSF, BRK) +XQT = (TXN, LNN) +QHF = (XKH, PCC) +FQM = (PKR, NMK) +DHP = (NKS, RCB) +KSM = (DKP, SDM) +LKB = (RQV, BRF) +BVL = (TRB, PNN) +GJF = (NTH, JRX) +HTC = (HRL, HXM) +JGB = (QVX, GMR) +VJV = (CJL, FKM) +RGK = (QVX, GMR) +KPF = (JCK, XDV) +LNV = (CRG, MPJ) +QNP = (LXK, VKJ) +MFT = (NJK, VVL) +PBT = (RCN, NTR) +CKV = (XHT, CFF) +RCD = (TXN, LNN) +MBJ = (HLT, MLS) +QDC = (NMJ, RTR) +SVV = (FJC, MJR) +STN = (LQC, HFR) +QXB = (RPJ, MHL) +VJN = (TVB, BBB) +GKH = (RTP, CHG) +XGL = (PMX, FFX) +KNH = (FGB, KNM) +TVN = (QKP, KPR) +JFC = (BFK, JXL) +LHG = (TFG, NCP) +HFT = (HND, HFC) +DKP = (DSB, CNV) +LGH = (DCG, QPN) +LGC = (VVL, NJK) +HSC = (NTJ, QQL) +CFF = (JMJ, GHL) +PFH = (SGL, VJF) +LGT = (GRG, CBJ) +JSQ = (SFH, RVQ) +VDM = (CLC, BTH) +NTR = (DCB, XFN) +KVH = (DKP, SDM) +BVQ = (LSH, SDX) +NRC = (QCH, TRS) +DCB = (KBT, TSR) +DGC = (FMC, JKB) +VMQ = (LJR, LJR) +MXG = (LSL, SHM) +CPM = (TRS, QCH) +DLX = (LQN, TJC) +GDS = (BNK, CLF) +JRK = (PGJ, NHD) +JXV = (DRH, CVN) +SVD = (CBN, NHR) +LCN = (MSQ, MMK) +MJL = (RQL, RGF) +JLT = (QRQ, DFC) +BGP = (DPV, VXC) +PSQ = (PJR, BHB) +NTJ = (KGM, VDB) +SGX = (VFM, PXL) +QBQ = (MCM, VNV) +CHV = (QJN, CMG) +DRK = (BKT, LBH) +SVS = (NPV, HCH) +BCP = (BKM, BCS) +BXN = (PSQ, GSS) +GQJ = (SMH, XHX) +NPV = (RQJ, MRF) +NMV = (HTC, DTX) +LKR = (SPG, SJK) +HLT = (XCQ, FCR) +SKX = (HSC, CFR) +SDR = (BLQ, FKV) +FCN = (FKT, VDM) +VPB = (CJR, FHK) +MHL = (PFJ, TTC) +XQK = (BBK, PBQ) +MDV = (SCJ, BKF) +VDC = (DQQ, DTS) +RTV = (LDP, PCN) +GRQ = (FDR, DHG) +NDH = (JGX, SSN) +NCG = (GNR, SRM) +LQN = (BVL, KQM) +NHR = (FQM, TNK) +LJR = (GDP, GDP) +JXL = (VKV, MJQ) +QCH = (BXN, RLM) +QGB = (MHL, RPJ) +DDN = (GLT, DHJ) +SHR = (BGP, FCX) +DPV = (MVK, NDK) +XHT = (GHL, JMJ) +QKK = (LCX, LCX) +TRT = (QFV, NHT) +PBQ = (DPX, LNS) +JHL = (HHQ, PDR) +DCG = (JLX, VSL) +SST = (NLK, DTM) +QPN = (JLX, VSL) +GXS = (FJP, DCM) +VJX = (DKB, FCZ) +XXJ = (HDN, LLB) +JPM = (VNV, MCM) +QGV = (SGX, KDP) +QNA = (QGB, QXB) +CQD = (DJM, DJM) +TTF = (TVN, SJQ) +CHP = (QCJ, NJT) +BBJ = (LVT, TKJ) +VKJ = (CRB, GXD) +LJF = (FFL, KXV) +TKD = (MML, ZZZ) +QKP = (CKV, LMN) +CQF = (STC, BSP) +PHM = (FHK, CJR) +BTF = (PMX, FFX) +JRX = (FVH, DJG) +PMN = (NBG, NCG) +LKG = (RNN, MXG) +NXB = (JLH, KNH) +MLQ = (FBT, PFH) +JML = (XKC, GLX) +NHT = (PRK, LTV) +BLQ = (SKX, HGL) +BDX = (FFL, KXV) +DVN = (SCR, JML) +FPL = (RQL, RGF) +MMS = (SVR, KSV) +FLT = (PTK, MCD) +PTQ = (BLQ, FKV) +DHG = (RBS, TXD) +QSC = (NCG, NBG) +LFV = (NXP, GMF) +XDS = (KQP, STN) +PVB = (BBB, TVB) +JGX = (NXF, FTB) +CNB = (PRR, JRK) +TQQ = (PBQ, BBK) +GMF = (MKH, CRH) +SDP = (KCQ, PKV) +MHP = (HMR, LCM) +RRS = (QGV, QLD) +PKV = (XKG, HTH) +JLX = (GDS, PLN) +RJN = (VGD, RKX) +NMK = (QQR, JGG) +PFK = (SVS, MKX) +NXF = (MLT, RFV) +VVM = (HDM, NGQ) +VSJ = (QDG, VKM) +RBS = (QSM, LGT) +KFN = (RRS, DVH) +XXN = (XMM, SKM) +TXF = (LKM, PPP) +DFV = (VLF, GPC) +RMP = (PJS, LHG) +LVP = (QDG, VKM) +DFC = (QXH, GRQ) +GPC = (DLX, FMB) +LJP = (CCD, FSM) +MPN = (PFD, RSN) +SGG = (RHV, SNN) +BDF = (HVV, PFX) +QQR = (GVH, QSB) +RPJ = (TTC, PFJ) +NDQ = (NXV, XQD) +RGF = (BVQ, GMB) +TXM = (DCM, FJP) +VRN = (XFV, MFG) +FCX = (VXC, DPV) +RQJ = (HFT, DBH) +MLT = (CGX, XBH) +TSR = (QSS, NFP) +BCD = (VQC, TLJ) +JQK = (PPP, LKM) +DSF = (CNQ, KPF) +CVS = (LDS, NFH) +VTP = (SNN, RHV) +FTA = (MHG, CRF) +RNR = (NTH, JRX) +DRH = (PLR, DDF) +KSV = (SMK, NHC) +VKM = (HFV, LMG) +DTR = (XFV, MFG) +MML = (QDM, GMV) +HPQ = (CHP, NLL) +GKF = (QHK, TDM) +TDF = (DHX, LPT) +PXT = (FBT, PFH) +BRK = (VQB, JXJ) +VXT = (KSV, SVR) +HNM = (QPN, DCG) +HHQ = (HNG, HCD) +MPG = (JJS, SMZ) +CGS = (QDC, GQV) +GMR = (GQJ, RMH) +BKF = (CNB, NKP) +VDB = (VPB, PHM) +DSQ = (PMN, QSC) +DTX = (HRL, HXM) +QXH = (FDR, DHG) +LPT = (QDV, LHL) +DKB = (XQT, RCD) +QJN = (VBJ, DBF) +FVR = (JJB, LCN) +MFG = (CPM, NRC) +FMC = (MDK, MRJ) +JLL = (RXN, SXG) +NQK = (LCM, HMR) +KBT = (QSS, NFP) +DHX = (QDV, LHL) +SKM = (JHL, KCX) +FSM = (SSJ, GVF) +NVC = (TNG, PQJ) +SRM = (HSP, JLT) +FCM = (TDM, QHK) +NPN = (HPQ, KXC) +JGG = (QSB, GVH) +DCM = (DSF, PKX) +BLF = (BDJ, HRR) +JXJ = (JCC, LGV) +PSX = (PBT, GHT) +CRH = (NXX, BCD) +SCS = (RSH, RMP) +LTJ = (FSV, LHK) +PSV = (PKV, KCQ) +TVM = (FVR, RVP) +LSH = (DDN, HNX) +XTX = (RXN, RXN) +RKG = (FLR, HKV) +JNB = (BPJ, HKG) +GKN = (NJG, FLT) +RLM = (PSQ, GSS) +CNQ = (XDV, JCK) +HFR = (TQC, KLB) +RQV = (SHR, CTK) +DSN = (HDN, LLB) +FFF = (KSR, KSR) +FJJ = (FCF, KSS) +KPH = (NXJ, MBJ) +KCQ = (XKG, HTH) +PPK = (BNP, PMD) +TKB = (LKG, GLD) +JQD = (MLQ, PXT) +BPJ = (THT, KNJ) +RBK = (FCN, MTB) +PDK = (GHS, TTP) +SDX = (DDN, HNX) +BQM = (DHX, LPT) +KNJ = (MFP, VSD) +CLC = (FNQ, KVR) +XPS = (NXB, SXK) +CLF = (JQD, SGS) +DQM = (KSR, LQK) +XKG = (MRV, GDV) +RHD = (TTH, SNV) +RTR = (BGV, NLH) +QSB = (TKB, KHF) +BKP = (PJG, DKG) +RMH = (XHX, SMH) +PGR = (PNS, DGC) +GND = (VLF, GPC) +PCC = (QNP, XCM) +KDP = (PXL, VFM) +SPG = (TTF, SLQ) +SXK = (KNH, JLH) +PFX = (MFT, LGC) +BKM = (PLK, PSX) +TGD = (CMG, QJN) +NXV = (NTN, XRC) +MFP = (HNM, LGH) +MQQ = (RSB, MPG) +TBZ = (CRF, MHG) +XDV = (TKR, PDK) +DVF = (CPH, DSQ) +SNV = (GXS, TXM) +DCR = (HJD, VHF) +QDG = (LMG, HFV) +MHG = (FHT, BMJ) +NKP = (PRR, JRK) +XRC = (VNC, TDC) +JFL = (RRS, DVH) +KRT = (SKM, XMM) +CDD = (DVN, PDM) +NXP = (MKH, CRH) +LVT = (GRR, JPV) +NJT = (RLC, MTK) +HVV = (MFT, LGC) +RNG = (GMF, NXP) +SVR = (SMK, NHC) +QDJ = (SSN, JGX) +RQL = (BVQ, GMB) +HMR = (GHC, DTG) +CGX = (BDS, NDQ) +MJQ = (LVP, VSJ) +XJC = (CHN, VVH) +FFL = (GKN, PTC) +XJS = (SJK, SPG) +JQS = (PCN, LDP) +SHM = (JFL, KFN) +HPB = (LJR, RSS) +JXG = (SVF, TSJ) +STC = (QBQ, JPM) +FHT = (XXN, KRT) +LRD = (KQP, STN) +VCX = (SXK, NXB) +KSR = (TLF, TLF) +KQM = (PNN, TRB) +KTC = (TSL, XRG) +VKV = (VSJ, LVP) +XTB = (PPK, JQM) +BFK = (MJQ, VKV) +GLT = (VRN, DTR) +VPJ = (PFD, RSN) +BKT = (SGG, VTP) +BMQ = (VVM, VQG) +KVR = (QHF, XGG) +LNH = (QDC, GQV) +SLQ = (SJQ, TVN) +CJR = (RDP, MLK) +JCK = (TKR, PDK) +GVF = (XXH, NCV) +LQC = (TQC, KLB) +TSS = (SFH, RVQ) +HRL = (XVQ, PTP) +TDM = (GKK, LNV) +BNP = (BMQ, NSV) +BFG = (RNQ, PKH) +CFJ = (BKF, SCJ) +QQQ = (JPX, JPX) +MQV = (JSQ, TSS) +XHX = (LQD, FJG) +PMX = (XXJ, DSN) +KGM = (VPB, PHM) +GNR = (JLT, HSP) +NLS = (PTQ, SDR) +MSQ = (VJV, HQV) +FMB = (TJC, LQN) +XFV = (NRC, CPM) +RSN = (JGB, RGK) +PRK = (HVT, BKP) +HFV = (XJC, SHS) +SVF = (NMD, MGG) +QVX = (GQJ, RMH) +TCF = (BLF, JTQ) +MLK = (HSF, MQV) +RVQ = (FCM, GKF) +GLX = (XTX, JLL) +PDM = (SCR, JML) +HGR = (RKX, VGD) +JRB = (SND, SDG) +FKV = (SKX, HGL) +TDC = (LNH, CGS) +FVH = (XGL, BTF) +NHC = (FQS, FJJ) +BBK = (LNS, DPX) +VVH = (NNP, CVS) +NJK = (DLC, TCF) +SGL = (VJN, PVB) +MTV = (NQK, MHP) +PJS = (TFG, NCP) +DTG = (SCS, DBK) +TCJ = (SDP, PSV) +VGB = (HVK, XMV) +MCL = (SNQ, NVC) +NXX = (TLJ, VQC) +HVK = (MDV, CFJ) +NMD = (MCT, SST) +TTZ = (QXB, QGB) +VNC = (LNH, CGS) +GMB = (LSH, SDX) +CHN = (NNP, CVS) +FCV = (MJR, FJC) +HNG = (TDF, BQM) +SCR = (XKC, GLX) +DDF = (DHP, FVP) +CTK = (BGP, FCX) +CHG = (QJR, CDD) +VXL = (QFV, NHT) +SSJ = (XXH, NCV) +RNQ = (NPN, MHQ) +FJC = (XJF, XML) +XTZ = (PKH, RNQ) +CBN = (TNK, FQM) +RSB = (JJS, JJS) +TTH = (GXS, TXM) +HJH = (LKR, XJS) +VQB = (JCC, LGV) +HKV = (LJP, VQT) +RGN = (XRG, TSL) +MKX = (NPV, HCH) +JJS = (TST, KHB) +JPX = (MNJ, MNJ) +HTH = (MRV, GDV) +VQT = (CCD, FSM) +PGJ = (DHF, TCJ) +DHF = (SDP, PSV) +XJF = (GJF, RNR) +LQD = (CCF, NLS) +MRF = (DBH, HFT) +FNQ = (XGG, QHF) +GGM = (JXG, GMK) +BDS = (NXV, XQD) +MCM = (PGR, LJH) +LDP = (NDF, CQF) +QCJ = (RLC, MTK) +CCX = (RQV, BRF) +HBD = (BDF, FXL) +CBA = (RNQ, PKH) +MJR = (XJF, XML) +JCC = (LSB, NPF) +VQG = (NGQ, HDM) +RCB = (NSL, HPN) +QCV = (HBD, CST) +TDS = (LBH, BKT) +CNV = (QKQ, RBK) +KLB = (JQK, TXF) +XKH = (QNP, XCM) +SCJ = (CNB, NKP) +HGL = (CFR, HSC) +BDQ = (NTG, KTT) +PRR = (PGJ, NHD) +KPR = (CKV, LMN) +HFC = (LVF, BBJ) +TLF = (DKB, DKB) +SND = (HGR, RJN) +VNV = (PGR, LJH) +PLQ = (KKF, TVM) +LVF = (LVT, TKJ) +QJR = (PDM, DVN) +HVT = (DKG, PJG) +CFR = (QQL, NTJ) +DTM = (LTJ, RXP) +HNX = (GLT, DHJ) +QKQ = (FCN, MTB) +PTK = (HPC, PFK) +FQS = (FCF, KSS) +JBQ = (LCX, MQQ) +RXP = (LHK, FSV) +SSR = (DRH, CVN) +LSB = (VDC, KHQ) +DPX = (GNQ, VSB) +ZZZ = (GMV, QDM) +XFN = (TSR, KBT) +LNN = (NMV, FGF) +KQP = (LQC, HFR) +LHR = (XJS, LKR) +GCQ = (DVF, BXQ) +VVL = (TCF, DLC) +MKH = (NXX, BCD) diff --git a/src/days/test_inputs/test09.txt b/src/days/test_inputs/test09.txt new file mode 100644 index 0000000..539a763 --- /dev/null +++ b/src/days/test_inputs/test09.txt @@ -0,0 +1,3 @@ +0 3 6 9 12 15 +1 3 6 10 15 21 +10 13 16 21 30 45 diff --git a/src/days/test_inputs/test10.txt b/src/days/test_inputs/test10.txt new file mode 100644 index 0000000..8f950ae --- /dev/null +++ b/src/days/test_inputs/test10.txt @@ -0,0 +1,10 @@ +FF7FSF7F7F7F7F7F---7 +L|LJ||||||||||||F--J +FL-7LJLJ||||||LJL-77 +F--JF--7||LJLJ7F7FJ- +L---JF-JLJ.||-FJLJJ7 +|F|F-JF---7F7-L7L|7| +|FFJF7L7F-JF7|JL---7 +7-L-JL7||F7|L7F-7F7| +L.L7LFJ|||||FJL7||LJ +L7JLJL-JLJLJL--JLJ.L diff --git a/src/days/test_inputs/test11.txt b/src/days/test_inputs/test11.txt new file mode 100644 index 0000000..986aad4 --- /dev/null +++ b/src/days/test_inputs/test11.txt @@ -0,0 +1,10 @@ +...#...... +.......#.. +#......... +.......... +......#... +.#........ +.........# +.......... +.......#.. +#...#..... diff --git a/src/days/test_inputs/test12.txt b/src/days/test_inputs/test12.txt new file mode 100644 index 0000000..e925935 --- /dev/null +++ b/src/days/test_inputs/test12.txt @@ -0,0 +1,6 @@ +???.### 1,1,3 +.??..??...?##. 1,1,3 +?#?#?#?#?#?#?#? 1,3,1,6 +????.#...#... 4,1,1 +????.######..#####. 1,6,5 +?###???????? 3,2,1 diff --git a/src/days/test_inputs/test13.txt b/src/days/test_inputs/test13.txt new file mode 100644 index 0000000..3b6b5cc --- /dev/null +++ b/src/days/test_inputs/test13.txt @@ -0,0 +1,15 @@ +#.##..##. +..#.##.#. +##......# +##......# +..#.##.#. +..##..##. +#.#.##.#. + +#...##..# +#....#..# +..##..### +#####.##. +#####.##. +..##..### +#....#..# diff --git a/src/days/test_inputs/test14.txt b/src/days/test_inputs/test14.txt new file mode 100644 index 0000000..5a24dce --- /dev/null +++ b/src/days/test_inputs/test14.txt @@ -0,0 +1,10 @@ +O....#.... +O.OO#....# +.....##... +OO.#O....O +.O.....O#. +O.#..O.#.# +..O..#O..O +.......O.. +#....###.. +#OO..#.... diff --git a/src/days/test_inputs/test15.txt b/src/days/test_inputs/test15.txt new file mode 100644 index 0000000..4f58f74 --- /dev/null +++ b/src/days/test_inputs/test15.txt @@ -0,0 +1 @@ +rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7 diff --git a/src/days/test_inputs/test16.txt b/src/days/test_inputs/test16.txt new file mode 100644 index 0000000..d6805ce --- /dev/null +++ b/src/days/test_inputs/test16.txt @@ -0,0 +1,10 @@ +.|...\.... +|.-.\..... +.....|-... +........|. +.......... +.........\ +..../.\\.. +.-.-/..|.. +.|....-|.\ +..//.|.... diff --git a/src/days/test_inputs/test17.txt b/src/days/test_inputs/test17.txt new file mode 100644 index 0000000..f400d6e --- /dev/null +++ b/src/days/test_inputs/test17.txt @@ -0,0 +1,13 @@ +2413432311323 +3215453535623 +3255245654254 +3446585845452 +4546657867536 +1438598798454 +4457876987766 +3637877979653 +4654967986887 +4564679986453 +1224686865563 +2546548887735 +4322674655533 diff --git a/src/days/test_inputs/test18.txt b/src/days/test_inputs/test18.txt new file mode 100644 index 0000000..fc7612e --- /dev/null +++ b/src/days/test_inputs/test18.txt @@ -0,0 +1,14 @@ +R 6 (#70c710) +D 5 (#0dc571) +L 2 (#5713f0) +D 2 (#d2c081) +R 2 (#59c680) +D 2 (#411b91) +L 5 (#8ceee2) +U 2 (#caa173) +L 1 (#1b58a2) +U 2 (#caa171) +R 2 (#7807d2) +U 3 (#a77fa3) +L 2 (#015232) +U 2 (#7a21e3) diff --git a/src/days/test_inputs/test19.txt b/src/days/test_inputs/test19.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/days/test_inputs/test20.txt b/src/days/test_inputs/test20.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/days/test_inputs/test21.txt b/src/days/test_inputs/test21.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/days/test_inputs/test22.txt b/src/days/test_inputs/test22.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/days/test_inputs/test23.txt b/src/days/test_inputs/test23.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/days/test_inputs/test24.txt b/src/days/test_inputs/test24.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/days/test_inputs/test25.txt b/src/days/test_inputs/test25.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/days/utils.rs b/src/days/utils.rs new file mode 100644 index 0000000..4c100e8 --- /dev/null +++ b/src/days/utils.rs @@ -0,0 +1,161 @@ +/// A general purpose struct able to store 2-Dimensional maps of Tiles. +/// +/// Can be parsed from a multi-line &str, if the T type implements [`From`](std::convert::From). +#[derive(Debug, Clone, Hash)] +pub struct Map { + tiles: Vec>, + dim: (usize, usize), +} + +#[allow(dead_code)] +impl Map { + /// Returns the dimensions of the map as a tuple `(x: usize, y: usize)` + pub fn dimensions(&self) -> (usize, usize) { + self.dim + } + + /// Gets the tile of a specified position. + /// + /// May return None, if the position is outside the dimensions. + pub fn get(&self, x: usize, y: usize) -> Option<&T> { + self.tiles.get(y)?.get(x) + } + + /// Sets the tile of a specified position. + pub fn set(&mut self, x: usize, y: usize, value: T) { + self.tiles[y][x] = value; + } + + /// Get the internally stored tile data. + pub fn get_raw_tiles(&self) -> &Vec> { + &self.tiles + } + + /// Get the internally stored tile data as mutable reference. + pub fn get_raw_tiles_mut(&mut self) -> &mut Vec> { + &mut self.tiles + } +} + +impl From>> for Map { + /// Creates a Map from a `Vec>`. + /// The dimensions are derived from the length of the y-Axis and first x line. + fn from(value: Vec>) -> Self { + Self { + dim: (value[0].len(), value.len()), + tiles: value, + } + } +} + +impl From<&str> for Map +where + T: From, +{ + fn from(value: &str) -> Self { + let tiles: Vec> = value + .lines() + .map(|v| v.chars().map(|v| v.into()).collect()) + .collect(); + + Self { + dim: (tiles[0].len(), tiles.len()), + tiles, + } + } +} + +#[allow(dead_code)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum Direction { + North, + East, + South, + West, +} + +impl Direction { + pub fn opposite(&self) -> Direction { + match self { + Self::North => Self::South, + Self::South => Self::North, + Self::West => Self::East, + Self::East => Self::West, + } + } + + pub fn walk_pos( + &self, + pos: (usize, usize), + upper_limit: (usize, usize), + ) -> Option<(usize, usize)> { + match self { + Self::North => { + if pos.1 != 0 { + Some((pos.0, pos.1 - 1)) + } else { + None + } + } + Self::South => { + if pos.1 < upper_limit.1 { + Some((pos.0, pos.1 + 1)) + } else { + None + } + } + Self::West => { + if pos.0 != 0 { + Some((pos.0 - 1, pos.1)) + } else { + None + } + } + Self::East => { + if pos.0 < upper_limit.0 { + Some((pos.0 + 1, pos.1)) + } else { + None + } + } + } + } + + pub fn walk_pos_signed( + &self, + pos: (isize, isize), + lower_limit: (isize, isize), + upper_limit: (isize, isize), + ) -> Option<(isize, isize)> { + match self { + Self::North => { + if pos.1 > lower_limit.1 { + Some((pos.0, pos.1 - 1)) + } else { + None + } + } + Self::South => { + if pos.1 < upper_limit.1 { + Some((pos.0, pos.1 + 1)) + } else { + None + } + } + Self::West => { + if pos.0 > lower_limit.0 { + Some((pos.0 - 1, pos.1)) + } else { + None + } + } + Self::East => { + if pos.0 < upper_limit.0 { + Some((pos.0 + 1, pos.1)) + } else { + None + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..0ced62d --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,261 @@ +use crate::days::Answer; +use crate::days::Day; +use crate::days::DayImpl; +use aoc_macro::*; +use colored::*; +use lazy_static::lazy_static; +use mut_static::MutStatic; +use std::time::Duration; + +mod days; + +#[derive(Debug, Clone, PartialEq)] +pub enum Verbosity { + None, + Verbose, + Development, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum Part { + One, + Two, + Both, +} + +#[derive(Debug, Clone)] +pub struct Settings { + verbosity: Verbosity, +} + +impl Settings { + fn set_verbosity(&mut self, new: Verbosity) { + self.verbosity = new; + } +} + +lazy_static! { + static ref VERBOSITY: MutStatic = MutStatic::from(Settings { + verbosity: Verbosity::None + }); +} + +pub fn set_verbosity(new_verbosity: Verbosity) { + let mut handle = VERBOSITY.write().unwrap(); + handle.set_verbosity(new_verbosity); +} + +pub fn get_verbosity() -> Verbosity { + VERBOSITY.read().unwrap().verbosity.clone() +} + +#[macro_export] +macro_rules! vprintln { + ($($arg:tt)*) => { + #[cfg(debug_assertions)] + if $crate::get_verbosity() == $crate::Verbosity::Verbose || $crate::get_verbosity() == $crate::Verbosity::Development { + println!( + $($arg)* + ) + } + }; +} + +#[macro_export] +macro_rules! vprint { + ($($arg:tt)*) => { + #[cfg(debug_assertions)] + if $crate::get_verbosity() == $crate::Verbosity::Verbose || $crate::get_verbosity() == $crate::Verbosity::Development { + print!( + $($arg)* + ) + } + }; +} + +#[macro_export] +macro_rules! dprintln { + ($($arg:tt)*) => { + #[cfg(debug_assertions)] + if $crate::get_verbosity() == $crate::Verbosity::Development { + println!( + $($arg)* + ) + } + }; +} + +#[macro_export] +macro_rules! dprint { + ($($arg:tt)*) => { + #[cfg(debug_assertions)] + if $crate::get_verbosity() == $crate::Verbosity::Development { + print!( + $($arg)* + ) + } + }; +} + +fn dynamic_range_time_format(d: &Duration) -> String { + let nanos = d.as_nanos(); + + if nanos < 1000 { + // less than one microsecond + format!("{} ns", nanos) + } else if nanos < 100000 { + // less than 10 microseconds + format!("{:.3} µs", nanos as f64 / 1000.0) + } else if nanos < 1000000 { + // less than one millisecond + format!("{} µs", nanos / 1000) + } else if nanos < 10000000 { + // less than 10 milliseconds + format!("{:.3} ms", nanos as f64 / 1000000.0) + } else if nanos < 1000000000 { + // less than a second + format!("{} ms", nanos / 1000000) + } else if nanos < 10000000000 { + // less than 10 seconds + format!("{:.3} s", nanos as f64 / 1000000000.0) + } else { + // more than 10 seconds + format!("{} s", nanos / 1000000000) + } +} + +pub fn run_day(day: u8, part: Part, input: &str) { + println!("{} Day {}", "Starting".green().bold(), day); + println!("{}", "-----------------------".green().bold()); + let (one, two, init_t, one_t, two_t) = match part { + Part::Both => match_and_run_day_both!(), + Part::One => { + let (one, init_t, one_t) = match_and_run_day_one!(); + (one, Answer::Number(0), init_t, one_t, Duration::ZERO) + } + Part::Two => { + let (two, init_t, two_t) = match_and_run_day_two!(); + (Answer::Number(0), two, init_t, Duration::ZERO, two_t) + } + }; + + println!("{}:", "Results".green().bold()); + println!( + "\t{}: {}", + "Parsing time".green(), + dynamic_range_time_format(&init_t).bold().blue() + ); + if part == Part::Both || part == Part::One { + println!("\t{}:", "Part 1".green()); + println!("\t\tSolution: {}", format!("{}", one).bold().blue()); + println!( + "\t\tTook: {}", + if cfg!(debug_assertions) { + (dynamic_range_time_format(&one_t) + " (DEBUG)") + .bold() + .red() + } else { + dynamic_range_time_format(&one_t).bold().blue() + } + ); + } + if part == Part::Both || part == Part::Two { + println!("\t{}:", "Part 2".green()); + println!("\t\tSolution: {}", format!("{}", two).bold().blue()); + println!( + "\t\tTook: {}", + if cfg!(debug_assertions) { + (dynamic_range_time_format(&two_t) + " (DEBUG)") + .bold() + .red() + } else { + dynamic_range_time_format(&two_t).bold().blue() + } + ); + } +} + +pub fn test_day(day: u8, part: Part) -> bool { + println!("{} Day {}", "Testing".green().bold(), day); + println!("{}", "-----------------------".green().bold()); + match part { + Part::Both => { + let ((one_p, one_r, one_e), (two_p, two_r, two_e)) = match_and_test_day_both!(); + + println!("{}:", "Results".green().bold()); + println!( + "\t{}: {}", + "Part 1".green(), + match one_p { + true => { + "PASSED".green().bold() + } + false => { + "FAILED".red().bold() + } + } + ); + println!("\t\tResult: {}", format!("{}", one_r).bold().blue()); + println!("\t\tExpected: {}", format!("{}", one_e).bold().blue()); + + println!( + "\t{}: {}", + "Part 2".green(), + match two_p { + true => { + "PASSED".green().bold() + } + false => { + "FAILED".red().bold() + } + } + ); + println!("\t\tResult: {}", format!("{}", two_r).bold().blue()); + println!("\t\tExpected: {}", format!("{}", two_e).bold().blue()); + + !(!two_p || !one_p) + } + Part::One => { + let (one_p, one_r, one_e) = match_and_test_day_one!(); + + println!("{}:", "Results".green().bold()); + println!( + "\t{}: {}", + "Part 1".green(), + match one_p { + true => { + "PASSED".green().bold() + } + false => { + "FAILED".red().bold() + } + } + ); + println!("\t\tResult: {}", format!("{}", one_r).bold().blue()); + println!("\t\tExpected: {}", format!("{}", one_e).bold().blue()); + + !one_p + } + Part::Two => { + let (two_p, two_r, two_e) = match_and_test_day_two!(); + + println!("{}:", "Results".green().bold()); + println!( + "\t{}: {}", + "Part 2".green(), + match two_p { + true => { + "PASSED".green().bold() + } + false => { + "FAILED".red().bold() + } + } + ); + println!("\t\tResult: {}", format!("{}", two_r).bold().blue()); + println!("\t\tExpected: {}", format!("{}", two_e).bold().blue()); + + !two_p + } + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..a377111 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,259 @@ +use aoc23::{run_day, set_verbosity, test_day, Part, Verbosity}; +use clap::{App, AppSettings, Arg, SubCommand}; +use colored::*; +use reqwest::blocking::Client; +use reqwest::cookie::Jar; +use reqwest::header::USER_AGENT; +use std::env; +use std::fs; +use std::path::Path; +use std::sync::Arc; + +// NOTE: Since this CLI was coded using clap in december of 2021, there seem to +// have been huge breaking changes, requiring me to rewrite the CLI completely +// to be able to update Clap. As I am on a limited time schedule RN, I will +// probably do this later on. +// 2023-11-29: still haven't done this, maybe I'll do is this year. +// TODO: Rewrite CLI and update Clap + +fn main() { + let matches = App::new("Advent Of Code 2023") + .author("LeMoonStar ") + .about("My Advent Of Code 2023 solutions.") + .setting(AppSettings::SubcommandRequiredElseHelp) + .arg( + Arg::with_name("day") + .help("The number of the day to execute") + .required(true) + .takes_value(true) + .validator(|v| match v.parse::() { + Ok(day) => { + if 0 < day && day <= 25 { + Ok(()) + } else { + Err("The day must be between 1 and 25.".to_string()) + } + } + Err(_) => Err("The day must be a number between 1 and 25.".to_string()), + }), + ) + .arg( + Arg::with_name("part") + .help("Specifies the part of the day to compute.") + .long("part") + .short("p") + .default_value("b") + .possible_values(&["1", "2", "b"]) + .takes_value(true), + ) + .arg( + Arg::with_name("verbose") + .help("Print verbose information") + .long("verbose") + .short("v") + .conflicts_with("development")) + .arg( + Arg::with_name("development") + .help("Print development information") + .long("dev") + .short("d") + .conflicts_with("verbose")) + .subcommand( + SubCommand::with_name("test").about("Test the day with the example input data."), + ) + .subcommand( + SubCommand::with_name("auto") + .about("Automatically download input from AoC using the provided session and run the solution.") + .arg(Arg::with_name("session") + .help("The AoC browser session string. If not provided, uses the AOC_SESSION environment variable.") + .short("s") + .long("session") + .takes_value(true)) + .arg(Arg::with_name("no_cache") + .help("Don't cache the input, and delete any current cache for this day.") + .short("N") + .long("no-cache"))) + .subcommand( + SubCommand::with_name("run") + .about("Use either a file or stdin as input and run the solution.") + .arg(Arg::with_name("file") + .help("Specify a file to be used as input, otherwise use stdin.") + .short("f") + .long("file") + .takes_value(true) + ) + ) + .get_matches(); + + if cfg!(debug_assertions) { + println!( + "{}", + "This binary was built in debug mode. To improve performance, please add --release to the build command." + .red() + .bold() + ); + } + + let day = matches + .value_of("day") + .unwrap() + .parse::() + .expect("Failed to parse day argument."); + + let part: Part = match matches.value_of("part") { + Some("1") => Part::One, + Some("2") => Part::Two, + Some("b") => Part::Both, + _ => panic!("unexpected part argument."), + }; + + if matches.args.contains_key("verbose") { + #[cfg(not(debug_assertions))] + println!("{}", "verbosity and development prints are only available in unoptimized builds (compiled without --release).".red().bold()); + set_verbosity(Verbosity::Verbose); + } + + if matches.args.contains_key("development") { + #[cfg(not(debug_assertions))] + println!("{}", "verbosity and development prints are only available in unoptimized builds (compiled without --release).".red().bold()); + set_verbosity(Verbosity::Development); + } + + match matches.subcommand() { + ("run", c_matches) => { + let input = match c_matches { + Some(c_matches) => { + if let Some(f) = c_matches.value_of("file") { + fs::read_to_string(Path::new(f)).expect("Error while reading input file") + } else { + get_stdin_day_input(day) + } + } + None => get_stdin_day_input(day), + }; + run_day(day, part, &input); + } + ("auto", c_matches) => { + let session: Option = match c_matches { + Some(c_matches) => match c_matches.value_of("session") { + Some(v) => Some(v.to_owned()), + None => env::var("AOC_SESSION").ok(), + }, + None => env::var("AOC_SESSION").ok(), + }; + let cache = if let Some(c_matches) = c_matches { + !c_matches.args.contains_key("no_cache") + } else { + true + }; + + let input = get_auto_input(day, session.as_ref(), cache); + run_day(day, part, &input); + } + ("test", _) => { + if !test_day(day, part) { + std::process::exit(1); + } + } + _ => panic!("Unexpected Subcommand."), + } +} + +fn get_stdin_day_input(day: u8) -> String { + let mut input = String::new(); + let stdin = std::io::stdin(); + + println!( + "Please paste your input for day {}, and then press {}", + day, + match cfg!(windows) { + true => "CTRL-Z", + _ => "CTRL-D", + } + ); + + loop { + match stdin.read_line(&mut input) { + Ok(l) => { + if l == 0 { + break; + } + } + Err(err) => panic!("Error encountered while trying to read stdin: {}", err), + } + } + + input +} + +fn download_input(day: u8, session: &String) -> Result { + println!("Downloading input for day {}", day); + + let cookie_jar = Jar::default(); + cookie_jar.add_cookie_str( + format!("session={}; Domain=adventofcode.com", session).as_ref(), + &"https://adventofcode.com/".parse::().unwrap(), + ); + let client = Client::builder() + .https_only(true) + .cookie_provider(Arc::new(cookie_jar)) + .build()?; + + let response = client + .get(format!("https://adventofcode.com/2023/day/{}/input", day)) + .header( + USER_AGENT, + "https://github.com/LeMoonStar/AoC23 aoc23@unitcore.de", + ) + .send()?; + + if !response.status().is_success() { + panic!("Server error or invalid session."); + } + + response.text() +} + +fn get_auto_input(day: u8, session: Option<&String>, cache: bool) -> String { + let cache_str = &format!("./.aoc23_cache/input{:02}.txt", day); + let cache_path: &Path = Path::new(cache_str); + match cache { + true => match fs::read_to_string(cache_path) { + Ok(input) => input, + Err(_) => { + if let Some(session) = session { + match download_input(day, session) { + Ok(input) => { + let _ = fs::create_dir(Path::new("./.aoc23_cache")); + match fs::write(cache_path, &input) { + Ok(_) => {} + Err(err) => { + println!("Warning! couldn't save input cache!{:?}", err) + } + } + input + } + Err(err) => { + panic!("Error while downloading input: {:?}", err); + } + } + } else { + panic!("Neither a session argument nor the AOC_SESSION environment variable were provided, and there is no cache of the day's input."); + } + } + }, + false => { + let _ = fs::remove_file(cache_path); + if let Some(session) = session { + match download_input(day, session) { + Ok(input) => input, + Err(err) => { + panic!("Error while downloading input: {:?}", err); + } + } + } else { + panic!("Neither a session argument nor the AOC_SESSION environment variable were provided, and there is no cache of the day's input."); + } + } + } +}