use aoc23::prelude::*; fn part_1(input: String) -> Result { let mut sum = 0; for line in input.lines().into_iter() { let chars = line.chars(); let first_num: usize = chars .clone() .into_iter() .skip_while(|c| !c.is_digit(10)) .take(1) // This would get continuous digits: .take_while(|c| c.is_digit(10)) .collect::() .parse()?; let last_num: usize = chars .into_iter() .rev() .skip_while(|c| !c.is_digit(10)) .take(1) .collect::() .parse()?; let value = first_num * 10 + last_num; sum += value; } // println!("Answer: {}", sum); Ok(sum) } fn part_2(input: String) -> Result { let numbers: [(&'static str, u32); 9] = [ ("one", 1), ("two", 2), ("three", 3), ("four", 4), ("five", 5), ("six", 6), ("seven", 7), ("eight", 8), ("nine", 9), ]; let mut sum = 0; for line in input.lines().into_iter() { let mut chars = line.chars(); let mut peek_index = 0; let first_num = loop { // I wanted this to work but peek advances the iterator anyways despite saying it // doesn't. I don't understand // // ``` // if let Some(Some(peek_digit)) = chars.by_ref().peekable().peek().map(|c| c.to_digit(10)) // ``` if let Some(Some(peek_digit)) = line.chars().nth(peek_index).map(|c| c.to_digit(10)) { break peek_digit; } if let Some((_, digit)) = numbers.iter().find(|num| chars.as_str().starts_with(num.0)) { break *digit; } if chars.next().is_none() { break 0; } peek_index += 1; }; let last_num = loop { if let Some((_, digit)) = numbers.iter().find(|num| chars.as_str().ends_with(num.0)) { break *digit; } if let Some(last_char) = chars.next_back() { if let Some(digit) = last_char.to_digit(10) { break digit; } } else { break first_num; } }; let value = first_num * 10 + last_num; sum += value; // println!("Line {line}: a:{first_num}|b:{last_num} = {value} ++ {sum}"); } Ok(sum) } fn part_2_no_peek(input: String) -> Result { // We can just add the text versions of the number as well and this removes the // need to peek at the digit like in the above method. Idk what I like better... let numbers: [(&'static str, u32); 18] = [ ("one", 1), ("1", 1), ("two", 2), ("2", 2), ("three", 3), ("3", 3), ("four", 4), ("4", 4), ("five", 5), ("5", 5), ("six", 6), ("6", 6), ("seven", 7), ("7", 7), ("eight", 8), ("8", 8), ("nine", 9), ("9", 9), ]; let mut sum = 0; for line in input.lines().into_iter() { let mut chars = line.chars(); let first_num = loop { if let Some((_, digit)) = numbers.iter().find(|num| chars.as_str().starts_with(num.0)) { break *digit; } if chars.next().is_none() { break 0; } }; let last_num = loop { if let Some((_, digit)) = numbers.iter().find(|num| chars.as_str().ends_with(num.0)) { break *digit; } if chars.next_back().is_none() { break first_num; } }; let value = first_num * 10 + last_num; sum += value; // println!("Line {line}: a:{first_num}|b:{last_num} = {value} ++ {sum}"); } Ok(sum) } #[tokio::main] async fn main() -> Result<()> { println!("Day 1"); println!("====="); let input = utils::aoc::get_puzzle_input(1).await?; println!("part 1: {}", part_1(input.clone())?); println!("part 2: {}\t[peek method]", part_2(input.clone())?); println!( "part 2: {}\t[no peek method]", part_2_no_peek(input.clone())? ); Ok(()) }