diff --git a/2023/rust/src/day1.rs b/2023/rust/src/day1.rs index 285059d..ac042e8 100644 --- a/2023/rust/src/day1.rs +++ b/2023/rust/src/day1.rs @@ -1,115 +1,102 @@ use crate::prelude::*; +use std::cell::OnceCell; mod prelude; +const DIGITS = OnceCell::new(); + fn main() { Day1::show(day_input(1), day_input(1)) } +const ASCII_ZERO: u8 = 0x30; +const ASCII_NINE: u8 = 0x39; + +let value: &String = digits.get_or_init(|| { +[ + "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", +] +.map(str::as_bytes)}); +assert_eq!(value, "Hello, World!"); +assert!(cell.get().is_some()); + +const DIGITS: [&[u8]; 10] = ; +const DIGITS_REV: [&[u8]; 10] = [ + "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", +] +.map(|s| s.chars().rev().collect::().as_bytes()); + +type Solution = i64; + struct Day1 {} impl Day1 { - fn calibration_value(line: &str) -> i128 { - println!("{line}"); - let bytes = line.as_bytes(); - let mut first_digit: Option = None; - let mut last_digit: Option = None; - for i in 0..bytes.len() { - let n = bytes.len() - 1 - i; - println!("{n} {i}"); - if first_digit.is_none() && (0x30..=0x39).contains(&bytes[i]) { - first_digit = Some(bytes[i] - 0x30); - println!("found first {first_digit:?}"); - } - if last_digit.is_none() && (0x30..=0x39).contains(&bytes[n]) { - last_digit = Some(bytes[n] - 0x30); - println!("found last {last_digit:?}"); - } - if first_digit.is_some() && last_digit.is_some() { - break; - } - } - println!("{:?} {:?}", first_digit, last_digit); - Into::::into(first_digit.or(last_digit).unwrap() * 10) - + Into::::into(last_digit.or(first_digit).unwrap()) + fn is_digit(b: &u8) -> bool { + (ASCII_ZERO..=ASCII_NINE).contains(b) } - fn num_name(s: &[u8]) -> Option { - for (i, w) in [ - "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", - ] - .map(|s| s.as_bytes()) - .iter() - .enumerate() - { - if s.starts_with(w) { - return Some(i as u8); - } - } - return None; + fn to_digit(b: &u8) -> Solution { + i64::from(b - ASCII_ZERO) } - fn num_name_end(s: &[u8]) -> Option { - for (i, w) in [ - "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", - ] - .map(|s| s.as_bytes()) - .iter() - .enumerate() - { - if s.ends_with(w) { - return Some(i as u8); - } - } - return None; + fn calibration_value(line_bytes: &[u8]) -> i64 { + let i1 = line_bytes.iter().position(Self::is_digit).unwrap(); + let i2 = line_bytes[i1..] + .iter() + .rev() + .position(Self::is_digit) + .unwrap_or(i1); + + Self::to_digit(&line_bytes[i1]) * 10 + Self::to_digit(&line_bytes[i2]) } - fn calibration_value_words(line: &str) -> i128 { - println!("{line}"); - let bytes = line.as_bytes(); - let mut first_digit: Option = None; - let mut last_digit: Option = None; - for i in 0..bytes.len() { - let n = bytes.len() - 1 - i; - println!("NI: {n} {i}"); - if first_digit.is_none() { - if (0x30..=0x39).contains(&bytes[i]) { - first_digit = Some(bytes[i] - 0x30); - println!("found first {first_digit:?}"); - } else if let Some(n) = Self::num_name(&bytes[i..]) { - first_digit = Some(n); - println!("found first text {first_digit:?}"); + // part 2 + + fn num_name(s: &[u8], digits: &[&[u8]]) -> Option { + digits + .iter() + .position(|d| s.starts_with(d)) + .map(|n| n as i64) + } + + fn num_or_digit(s: &[u8], digits: &[&[u8]]) -> Option { + if Self::is_digit(&s[0]) { + Some(Self::to_digit(&s[0])) + } else { + Self::num_name(s, digits) + } + } + + fn calibration_value_words(line_bytes: &[u8]) -> i64 { + for i in 0..line_bytes.len() { + if let Some(first_digit) = Self::num_or_digit(&line_bytes[i..], &DIGITS) { + for j in (i..line_bytes.len()).rev() { + if let Some(last_digit) = Self::num_or_digit(&line_bytes[i..j], &DIGITS_REV) { + return first_digit * 10 + last_digit; + } } - } - if last_digit.is_none() { - println!("{:?}", &bytes[..=n]); - if (0x30..=0x39).contains(&bytes[n]) { - last_digit = Some(bytes[n] - 0x30); - println!("found last {last_digit:?}"); - } else if let Some(n) = Self::num_name_end(&bytes[..=n]) { - last_digit = Some(n); - println!("found last text {last_digit:?}"); - } - } - if first_digit.is_some() && last_digit.is_some() { - break; + return first_digit * 10 + first_digit; } } - println!("{:?} {:?}", first_digit, last_digit); - Into::::into(first_digit.or(last_digit).unwrap() * 10) - + Into::::into(last_digit.or(first_digit).unwrap()) + return 0; } } impl AoCSolution for Day1 { type Input = String; - type Solution = i128; + type Solution = i64; fn part1(input: Self::Input) -> Self::Solution { - input.lines().map(Self::calibration_value).sum() + input + .lines() + .map(|l| Self::calibration_value(l.as_bytes())) + .sum() } fn part2(input: Self::Input) -> Self::Solution { - input.lines().map(Self::calibration_value_words).sum() + input + .lines() + .map(|l| Self::calibration_value_words(l.as_bytes())) + .sum() } }