diff --git a/2023/day1/README.md b/2023/day1/README.md index 4825402..0fe6d5e 100644 --- a/2023/day1/README.md +++ b/2023/day1/README.md @@ -1,5 +1,7 @@ # Day 1: Trebuchet?! +## Part 1 + Something is wrong with global snow production, and you've been selected to take a look. The Elves have even given you a map; on it, they've used stars to mark the top fifty locations that are likely to be having problems. @@ -40,3 +42,24 @@ In this example, the calibration values of these four lines are 12, 38, 15, and Adding these together produces 142. Consider your entire calibration document. **What is the sum of all of the calibration values?** + +## Part 2 + +Your calculation isn't quite right. It looks like some of the digits are actually spelled out +with letters: one, two, three, four, five, six, seven, eight, and nine also count as valid "digits". + +Equipped with this new information, you now need to find the real first and last digit on each line. For example: + +``` +two1nine +eightwothree +abcone2threexyz +xtwone3four +4nineeightseven2 +zoneight234 +7pqrstsixteen +``` + +In this example, the calibration values are 29, 83, 13, 24, 42, 14, and 76. Adding these together produces *281*. + +**What is the sum of all of the calibration values?** diff --git a/2023/day1/src/main.rs b/2023/day1/src/main.rs index 70dcf34..afd5820 100644 --- a/2023/day1/src/main.rs +++ b/2023/day1/src/main.rs @@ -1,24 +1,78 @@ use std::collections::VecDeque; - -fn get_lines(raw: &str) -> Vec> { - raw.split('\n').map(|line| line.chars().collect()).collect() +fn word_to_digit(s: &str) -> char { + match s { + "one" | "1" => '1', + "two" | "2" => '2', + "three" | "3" => '3', + "four" | "4" => '4', + "five" | "5" => '5', + "six" | "6" => '6', + "seven" | "7" => '7', + "eight" | "8" => '8', + "nine" | "9" => '9', + _ => '0', + } } -fn filter_lines(raw: &Vec>) -> Vec> { - raw.iter() +fn line_to_digits(line: &str) -> VecDeque { + let numbers = Vec::from([ + "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "1", "2", "3", "4", + "5", "6", "7", "8", "9", "0", + ]); + + let mut digits: VecDeque = VecDeque::new(); + + // Find the numbers/number words in the line + let first_matches: Vec<(usize, &str)> = numbers + .iter() + .map(|search| line.match_indices(*search).collect::>()) + .filter(|search| search.len() > 0) + .map(|search| search[0]) + .collect(); + + let last_matches: Vec<(usize, &str)> = numbers + .iter() + .map(|search| line.rmatch_indices(*search).collect::>()) + .filter(|search| search.len() > 0) + .map(|search| search[0]) + .collect(); + + // Find the matches of the string in order, + // and return the cleaned up matches + for min in 0..line.len() { + if let Some(m) = first_matches.iter().find(|m| m.0 == min) { + digits.push_back(word_to_digit(m.1)); + break; + } + } + + for max in (0..line.len()).rev() { + if let Some(m) = last_matches.iter().find(|m| m.0 == max) { + digits.push_back(word_to_digit(m.1)); + break; + } + } + + digits +} + +fn get_lines(raw: &str, words: bool) -> Vec> { + raw.split('\n') .map(|line| { - let line: Vec = line - .into_iter() - .filter(|c| c.is_digit(10)) - .map(|c| c.to_owned()) - .collect(); - VecDeque::from(line) + if words { + line_to_digits(line) + } else { + line.chars() + .filter(|c| c.is_digit(10)) + .collect::>() + } }) .collect() } fn get_values(raw: &mut Vec>) -> Vec { raw.iter_mut() + .filter(|line| line.len() > 0) .map(|line| { let first = match line.pop_front() { Some(c) => c, @@ -41,9 +95,37 @@ fn sum(raw: Vec) -> usize { fn main() { let file_str = include_str!("input.txt"); - let lines = get_lines(file_str); - let mut lines = filter_lines(&lines); + let mut lines = get_lines(file_str, false); let values = get_values(&mut lines); - println!("Total: {}", sum(values)); + println!("Part 1 Total: {}", sum(values)); + + let mut lines = get_lines(file_str, true); + let values = get_values(&mut lines); + + println!("Part 2 Total: {}", sum(values)); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_first_result() { + let file_str = include_str!("input.txt"); + let mut lines = get_lines(file_str, false); + let values = get_values(&mut lines); + + assert_eq!(sum(values), 54990); + } + + #[test] + fn test_second_example_values() { + let example = "two1nine\neightwothree\nabcone2threexyz\nxtwone3four\n4nineeightseven2\nzoneight234\n7pqrstsixteen"; + let mut lines = get_lines(example, true); + let values = get_values(&mut lines); + + assert_eq!(values, Vec::from([29, 83, 13, 24, 42, 14, 76])); + assert_eq!(sum(values), 281); + } }