From 261a2e89c9d32d832e49fb0df6262d7878ae1ad1 Mon Sep 17 00:00:00 2001 From: Fabian Schmidt Date: Mon, 4 Nov 2024 14:41:15 +0100 Subject: [PATCH] y2015d19 I should look at the input more --- y2015/resources/19_input.txt | 45 ++++++++++++++ y2015/src/bin/d19.rs | 20 +++++++ y2015/src/days/d19.rs | 110 +++++++++++++++++++++++++++++++++++ y2015/src/days/mod.rs | 2 + 4 files changed, 177 insertions(+) create mode 100644 y2015/resources/19_input.txt create mode 100644 y2015/src/bin/d19.rs create mode 100644 y2015/src/days/d19.rs diff --git a/y2015/resources/19_input.txt b/y2015/resources/19_input.txt new file mode 100644 index 0000000..806774f --- /dev/null +++ b/y2015/resources/19_input.txt @@ -0,0 +1,45 @@ +Al => ThF +Al => ThRnFAr +B => BCa +B => TiB +B => TiRnFAr +Ca => CaCa +Ca => PB +Ca => PRnFAr +Ca => SiRnFYFAr +Ca => SiRnMgAr +Ca => SiTh +F => CaF +F => PMg +F => SiAl +H => CRnAlAr +H => CRnFYFYFAr +H => CRnFYMgAr +H => CRnMgYFAr +H => HCa +H => NRnFYFAr +H => NRnMgAr +H => NTh +H => OB +H => ORnFAr +Mg => BF +Mg => TiMg +N => CRnFAr +N => HSi +O => CRnFYFAr +O => CRnMgAr +O => HP +O => NRnFAr +O => OTi +P => CaP +P => PTi +P => SiRnFAr +Si => CaSi +Th => ThCa +Ti => BP +Ti => TiTi +e => HF +e => NAl +e => OMg + +CRnCaSiRnBSiRnFArTiBPTiTiBFArPBCaSiThSiRnTiBPBPMgArCaSiRnTiMgArCaSiThCaSiRnFArRnSiRnFArTiTiBFArCaCaSiRnSiThCaCaSiRnMgArFYSiRnFYCaFArSiThCaSiThPBPTiMgArCaPRnSiAlArPBCaCaSiRnFYSiThCaRnFArArCaCaSiRnPBSiRnFArMgYCaCaCaCaSiThCaCaSiAlArCaCaSiRnPBSiAlArBCaCaCaCaSiThCaPBSiThPBPBCaSiRnFYFArSiThCaSiRnFArBCaCaSiRnFYFArSiThCaPBSiThCaSiRnPMgArRnFArPTiBCaPRnFArCaCaCaCaSiRnCaCaSiRnFYFArFArBCaSiThFArThSiThSiRnTiRnPMgArFArCaSiThCaPBCaSiRnBFArCaCaPRnCaCaPMgArSiRnFYFArCaSiThRnPBPMgAr diff --git a/y2015/src/bin/d19.rs b/y2015/src/bin/d19.rs new file mode 100644 index 0000000..b3919f5 --- /dev/null +++ b/y2015/src/bin/d19.rs @@ -0,0 +1,20 @@ +use std::fs; + +use y2015::days::d19; + +fn main() { + part1(); + part2(); +} + +fn part1() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/19_input.txt")).unwrap(); + println!("{}", d19::process_part1(&content)); +} + +fn part2() { + let root = env!("CARGO_MANIFEST_DIR"); + let content = fs::read_to_string(format!("{root}/resources/19_input.txt")).unwrap(); + println!("{}", d19::process_part2(&content)); +} diff --git a/y2015/src/days/d19.rs b/y2015/src/days/d19.rs new file mode 100644 index 0000000..86d9ae1 --- /dev/null +++ b/y2015/src/days/d19.rs @@ -0,0 +1,110 @@ +use std::collections::HashSet; + +pub fn process_part1(input: &str) -> u32 { + let (replacements_str, molecule) = input.split_once("\n\n").unwrap(); + let mut replacements = Vec::new(); + replacements_str.lines().for_each(|line| { + let (from, to) = line.split_once(" => ").unwrap(); + replacements.push((from, to)); + }); + let mut possible = HashSet::new(); + for (from, to) in replacements { + molecule.match_indices(from).for_each(|(idx, _)| { + let mut new_possible = molecule.to_string(); + new_possible.replace_range(idx..(idx + from.len()), to); + possible.insert(new_possible); + }); + } + possible.len() as u32 +} + +// https://www.reddit.com/r/adventofcode/comments/3xflz8/comment/cy4etju/ +// broken test +//First insight +// +//There are only two types of productions: +// +// e => XX and X => XX (X is not Rn, Y, or Ar) +// +// X => X Rn X Ar | X Rn X Y X Ar | X Rn X Y X Y X Ar +// +//Second insight +// +//You can think of Rn Y Ar as the characters ( , ): +// +//X => X(X) | X(X,X) | X(X,X,X) +// +//Whenever there are two adjacent "elements" in your "molecule", you apply the first production. This reduces your molecule length by 1 each time. +// +//And whenever you have T(T) T(T,T) or T(T,T,T) (T is a literal token such as "Mg", i.e. not a nonterminal like "TiTiCaCa"), you apply the second production. This reduces your molecule length by 3, 5, or 7. +//Third insight +// +//Repeatedly applying X => XX until you arrive at a single token takes count(tokens) - 1 steps: +// +//ABCDE => XCDE => XDE => XE => X +//count("ABCDE") = 5 +//5 - 1 = 4 steps +// +//Applying X => X(X) is similar to X => XX, except you get the () for free. This can be expressed as count(tokens) - count("(" or ")") - 1. +// +//A(B(C(D(E)))) => A(B(C(X))) => A(B(X)) => A(X) => X +//count("A(B(C(D(E))))") = 13 +//count("(((())))") = 8 +//13 - 8 - 1 = 4 steps +// +//You can generalize to X => X(X,X) by noting that each , reduces the length by two (,X). The new formula is count(tokens) - count("(" or ")") - 2*count(",") - 1. +// +//A(B(C,D),E(F,G)) => A(B(C,D),X) => A(X,X) => X +//count("A(B(C,D),E(F,G))") = 16 +//count("(()())") = 6 +//count(",,,") = 3 +//16 - 6 - 2*3 - 1 = 3 steps +// +//This final formula works for all of the production types (for X => XX, the (,) counts are zero by definition.) +pub fn process_part2(input: &str) -> u32 { + let (_replacements_str, molecule) = input.split_once("\n\n").unwrap(); + // An element is always at least an uppercase character and possibly an additional lowercase + // character + let total_elements = molecule.chars().filter(|char| char.is_uppercase()).count(); + let num_rn = molecule.matches("Rn").count(); + let num_ar = molecule.matches("Ar").count(); + let num_y = molecule.matches("Y").count(); + (total_elements - (num_rn + num_ar) - 2 * num_y - 1) as u32 +} + +#[cfg(test)] +mod tests { + use super::*; + + const INPUT1: &str = "H => HO +H => OH +O => HH +e => H +e => O + +HOH"; + + const INPUT2: &str = "H => HO +H => OH +O => HH +e => H +e => O + +HOHOHO"; + + #[test] + fn part1() { + let result = process_part1(INPUT1); + assert_eq!(result, 4); + let result = process_part1(INPUT2); + assert_eq!(result, 7); + } + + #[test] + fn part2() { + let result = process_part2(INPUT1); + assert_eq!(result, 3); + let result = process_part2(INPUT2); + assert_eq!(result, 6); + } +} diff --git a/y2015/src/days/mod.rs b/y2015/src/days/mod.rs index 592f839..301410f 100644 --- a/y2015/src/days/mod.rs +++ b/y2015/src/days/mod.rs @@ -16,3 +16,5 @@ pub mod d6; pub mod d7; pub mod d8; pub mod d9; + +pub mod d19;