diff --git a/Cargo.lock b/Cargo.lock index cf6ca99..fc3bc61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,8 +19,10 @@ dependencies = [ "phf", "pretty_assertions", "rayon", + "serde_json", "thiserror", "tinystr", + "void", "wide", "winnow", ] @@ -486,6 +488,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "safe_arch" version = "0.7.2" @@ -495,6 +503,38 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "shlex" version = "1.3.0" @@ -577,6 +617,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + [[package]] name = "wide" version = "0.7.28" diff --git a/Cargo.toml b/Cargo.toml index b46e6aa..ca64d7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,8 @@ fnv = "1.0.7" clap = { version = "4.5.17", features = ["derive"] } petgraph = { version = "0.6.5", features = ["rayon"] } itertools = "0.13.0" +serde_json = "1.0.128" +void = "1.0.2" [dev-dependencies] pretty_assertions = "1.4.0" diff --git a/src/2015/09.rs b/src/2015/09.rs index 12e50c7..de6c5b8 100644 --- a/src/2015/09.rs +++ b/src/2015/09.rs @@ -1,4 +1,4 @@ -use eyre::{eyre, Result}; +use eyre::{eyre, Report, Result}; use itertools::Itertools; use petgraph::{graph::NodeIndex, Graph, Undirected}; use rayon::prelude::*; @@ -10,32 +10,27 @@ use winnow::prelude::*; use crate::types::{problem, Problem}; -pub const ALL_IN_A_SINGLE_NIGHT: Problem = problem!(part1, part2); - -fn part1(input: &str) -> Result { - let stops = input - .lines() - .map(Leg::try_from) - .collect::>() - .map_err(|e| eyre!("{e}"))?; - - Ok(stops.shortest_distance()) -} - -fn part2(input: &str) -> Result { - let stops = input - .lines() - .map(Leg::try_from) - .collect::>() - .map_err(|e| eyre!("{e}"))?; - - Ok(stops.longest_distance()) -} +pub const ALL_IN_A_SINGLE_NIGHT: Problem = problem!( + |input| Locations::try_from(input).map(|locs| locs.shortest_distance()), + |input| Locations::try_from(input).map(|locs| locs.longest_distance()) +); /// A list of locations Santa has to visit and how far apart they are from each other. #[derive(Debug)] struct Locations<'s>(Graph<&'s str, usize, Undirected, u8>); +impl<'s> TryFrom<&'s str> for Locations<'s> { + type Error = Report; + + fn try_from(input: &'s str) -> std::result::Result { + input + .lines() + .map(Leg::try_from) + .collect::>() + .map_err(|e| eyre!("{e}")) + } +} + impl<'s> Locations<'s> { /// Construct a new list of stops. fn new(graph: Graph<&'s str, usize, Undirected, u8>) -> Self { diff --git a/src/2015/10.rs b/src/2015/10.rs index 76ab27c..908bb8b 100644 --- a/src/2015/10.rs +++ b/src/2015/10.rs @@ -1,24 +1,14 @@ use eyre::Result; use itertools::Itertools; +use void::Void; use crate::types::{problem, Problem}; -pub const ELVES_LOOK_ELVES_SAY: Problem = problem!(part1, part2); +pub const ELVES_LOOK_ELVES_SAY: Problem = problem!(look_and_say_n::<40>, look_and_say_n::<50>); -fn part1(input: &str) -> Result { - let mut n = input.to_string(); - dbg!(&n); - for _ in 0..40 { - n = look_and_say(&n); - dbg!(&n); - } - - Ok(n.len()) -} - -fn part2(input: &str) -> Result { - let mut n = input.to_string(); - for _ in 0..50 { +fn look_and_say_n(n: &str) -> Result { + let mut n = n.to_string(); + for _ in 0..N { n = look_and_say(&n); } diff --git a/src/2015/11.rs b/src/2015/11.rs index 716704f..a833eaa 100644 --- a/src/2015/11.rs +++ b/src/2015/11.rs @@ -11,18 +11,12 @@ use rayon::prelude::*; use crate::types::{problem, Problem}; -pub const CORPORATE_POLICY: Problem = problem!(part1, part2); -const ASCII_LETTER_OFFSET: u8 = b'a'; - -fn part1(input: &str) -> Result { - input.parse::().map(Password::next_valid) -} +pub const CORPORATE_POLICY: Problem = problem!( + |input: &str| input.parse::().map(Password::next), + |input: &str| input.parse::().map(|pwd| pwd.next().next()) +); -fn part2(input: &str) -> Result { - input - .parse::() - .map(|pwd| pwd.next_valid().next_valid()) -} +const ASCII_LETTER_OFFSET: u8 = b'a'; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] struct Password([Letter; 8]); @@ -249,7 +243,7 @@ impl Password { } /// Returns the next password that passes validation, if one exists. - fn next_valid(mut self) -> Password { + fn next(mut self) -> Password { self.increment(); self.into_par_iter() .find_first(Password::is_valid) diff --git a/src/2015/12.rs b/src/2015/12.rs new file mode 100644 index 0000000..feaf861 --- /dev/null +++ b/src/2015/12.rs @@ -0,0 +1,28 @@ +use rayon::prelude::*; +use serde_json::Value; + +use crate::types::{problem, Problem}; + +pub const JS_ABACUS_FRAMEWORK_DOT_IO: Problem = problem!( + |input| serde_json::from_str(input).map(|root| sum_numbers(root, false)), + |input| serde_json::from_str(input).map(|root| sum_numbers(root, true)) +); + +fn sum_numbers(value: Value, ignore_red: bool) -> i64 { + match value { + Value::Null => 0, + Value::Bool(_) => 0, + Value::Number(number) => number.as_i64().expect("number to be an integer"), + Value::String(_) => 0, + Value::Array(vec) => vec + .into_par_iter() + .map(|val| sum_numbers(val, ignore_red)) + .sum(), + Value::Object(map) if ignore_red && map.values().any(|val| val == "red") => 0, + Value::Object(map) => map + .into_iter() + .par_bridge() + .map(|(_, value)| sum_numbers(value, ignore_red)) + .sum(), + } +} diff --git a/src/2015/mod.rs b/src/2015/mod.rs index ddf5422..a2462d8 100644 --- a/src/2015/mod.rs +++ b/src/2015/mod.rs @@ -3,7 +3,7 @@ use phf::phf_map; use crate::types::ProblemSet; use crate::util::mod_days; -mod_days!(01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11); +mod_days!(01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12); pub const PROBLEMS: ProblemSet = ProblemSet(phf_map! { 1u8 => NOT_QUITE_LISP, @@ -16,5 +16,6 @@ pub const PROBLEMS: ProblemSet = ProblemSet(phf_map! { 8u8 => MATCHSTICKS, 9u8 => ALL_IN_A_SINGLE_NIGHT, 10u8 => ELVES_LOOK_ELVES_SAY, - 11u8 => CORPORATE_POLICY + 11u8 => CORPORATE_POLICY, + 12u8 => JS_ABACUS_FRAMEWORK_DOT_IO, }); diff --git a/src/types/mod.rs b/src/types/mod.rs index 6b3d739..74373c6 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -55,7 +55,7 @@ macro_rules! problem { Problem::new(None, None) }; - ($part1:ident) => { + ($part1:expr) => { Problem::new( Some(|input| { let output = $part1(input)?; @@ -65,7 +65,7 @@ macro_rules! problem { ) }; - ($part1:ident, $part2:ident) => { + ($part1:expr, $part2:expr) => { Problem::new( Some(|input| { let output = $part1(input)?; diff --git a/tests/2015.rs b/tests/2015.rs index e8f5026..a7b2aaa 100644 --- a/tests/2015.rs +++ b/tests/2015.rs @@ -131,3 +131,15 @@ mod day11 { crate::util::aoc!(2015/11-2: "vzcaabcc"); } } + +mod day12 { + #[test] + fn part1() { + crate::util::aoc!(2015/12-1: 111754); + } + + #[test] + fn part2() { + crate::util::aoc!(2015/12-2: 65402); + } +}