use std::fs; struct Polymer { units: Vec, } fn reacts(chr1: char, chr2: char) -> bool { // The is_ascii_lowercase probably isn't strictly necessary but ¯\_(ツ)_/¯ (chr1.is_ascii_uppercase() && chr2 == chr1.to_ascii_lowercase()) || (chr1.is_ascii_lowercase() && chr2 == chr1.to_ascii_uppercase()) } impl Polymer { fn new() -> Self { Polymer { units: Vec::new() } } fn last(&self) -> Option { self.units.last().map(|c| *c) } fn push(&mut self, chr: char) { // if the char being pushed is the same as the top of stack then pop it, otherwise push if self.last().map(|top| reacts(top, chr)).unwrap_or(false) { self.units.pop(); } else { self.units.push(chr); } } fn into_string(self) -> String { self.units.iter().collect() } fn len(&self) -> usize { self.units.len() } } fn main() { let input = fs::read_to_string("input/day5.txt").expect("input"); part1(input.trim()); part2(input.trim()); } fn part1(input: &str) { let result = reduce(input); println!("{}", result.len()); } fn part2(input: &str) { let shortest_length = "abcdefghijklmnopqrstuvwxyz" .chars() .map(|letter| { // Remove letter/LETTER and LETTER/letter let input = input .replace(letter, "") .replace(letter.to_ascii_uppercase(), ""); reduce(&input) }) .min_by(|polymer_a, polymer_b| polymer_a.len().cmp(&polymer_b.len())); println!("Part 2: {:?}", shortest_length.map(|polymer| polymer.len())); } fn reduce(input: &str) -> Polymer { input.chars().fold(Polymer::new(), |mut polymer, chr| { polymer.push(chr); polymer }) } #[test] fn test_reacts() { assert!(reacts('a', 'A')); assert!(reacts('A', 'a')); assert!(!reacts('A', 'A')); assert!(!reacts('a', 'a')); assert!(!reacts('a', 'B')); } #[test] fn test_part1() { let input = "dabAcCaCBAcCcaDA"; let result = reduce(input); assert_eq!(result.len(), 10); assert_eq!(result.into_string(), "dabCBAcaDA".to_string()); } #[test] fn test_part1_snippet() { let input = "wUuXxrRbeEaAuUMmJUuXxvoFfMmOTjJmMtVvmMVWpdDPwGgAalVv"; let result = reduce(input); assert_eq!(result.into_string(), "wbJl".to_string()); }