diff --git a/day11/Cargo.lock b/day11/Cargo.lock new file mode 100644 index 0000000..73122b2 --- /dev/null +++ b/day11/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "day11" +version = "0.1.0" +dependencies = [ + "itertools", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "itertools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" +dependencies = [ + "either", +] diff --git a/day11/Cargo.toml b/day11/Cargo.toml new file mode 100644 index 0000000..e6ef091 --- /dev/null +++ b/day11/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "day11" +version = "0.1.0" +edition = "2024" + +[dependencies] +itertools = "0.15.0" diff --git a/day11/src/main.rs b/day11/src/main.rs new file mode 100644 index 0000000..4aa36dd --- /dev/null +++ b/day11/src/main.rs @@ -0,0 +1,128 @@ +use itertools::Itertools; + +// const INPUT: &str = "ghijklmn"; +// const INPUT: &str = "vzbxkghb"; +const INPUT: &str = "vzbxxyzz"; + +fn main() { + part1(INPUT); +} + +fn part1(input: &str) { + let mut input = increment(input); + loop { + if is_valid(&input) { + break; + } + input = increment(&input); + } + println!("{}", input); +} + +fn increment(input: &str) -> String { + let chars = input.chars().collect_vec(); + let mut carry = false; + + let mut iter = chars.iter().rev(); + + let mut result = String::new(); + + let c = iter.next().unwrap(); + let ret = handle_char(*c, true, carry); + result.push(ret.0); + carry = ret.1; + + for c in iter { + let ret = handle_char(*c, false, carry); + result.push(ret.0); + carry = ret.1; + } + + result.chars().rev().collect() +} + +fn handle_char(c: char, increase: bool, carry: bool) -> (char, bool) { + let increase_input = if increase { 1 } else { 0 }; + let carry_input = if carry { 1 } else { 0 }; + + let result = c as u8 + increase_input + carry_input; + let carry_result = result >= 123; + + let result = (((result - 97) % 26) + 97) as char; + + (result, carry_result) +} + +fn is_valid(p: &str) -> bool { + for letter in ['i', 'o', 'l'] { + if p.contains(letter) { + return false; + } + } + + let non_overlapping_double_letter_count = p + .chars() + .enumerate() + .tuple_windows() + .filter(|(a, b)| a.1 == b.1) + .map(|(a, _)| a.0) + .collect_vec(); + + let max = non_overlapping_double_letter_count.last().unwrap_or(&0); + let min = non_overlapping_double_letter_count.first().unwrap_or(&0); + + if max - min < 2 { + return false; + } + + let input = p.chars().collect_vec(); + + for i in 0..p.len() - 2 { + let s = i; + let e = s + 2; + if is_straight(&input[s..=e]) { + return true; + } + } + false +} + +fn is_straight(s: &[char]) -> bool { + if s.len() != 3 { + panic!("must check exactly 3 letters"); + } + + let mut chars = s.iter(); + + let a = *chars.next().unwrap() as u8; + let b = *chars.next().unwrap() as u8; + let c = *chars.next().unwrap() as u8; + + b == a + 1 && c == b + 1 +} + +#[cfg(test)] +mod test { + use crate::{handle_char, is_valid}; + + #[test] + fn test_increase_char() { + assert_eq!(handle_char('a', true, false), ('b', false)); + assert_eq!(handle_char('a', true, true), ('c', false)); + + assert_eq!(handle_char('i', true, false), ('j', false)); + + assert_eq!(handle_char('z', true, false), ('a', true)); + assert_eq!(handle_char('z', true, true), ('b', true)); + } + + #[test] + fn test_is_valid() { + assert!(is_valid("abcdffaa")); + assert!(is_valid("ghjaabcc")); + + assert!(!is_valid("hijklmmn")); + assert!(!is_valid("abbceffg")); + assert!(!is_valid("abbcegjk")); + } +}