advent-of-code/2023/day1/src/main.rs

132 lines
3.6 KiB
Rust

use std::collections::VecDeque;
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 line_to_digits(line: &str) -> VecDeque<char> {
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<char> = 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::<Vec<(usize, &str)>>())
.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::<Vec<(usize, &str)>>())
.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<VecDeque<char>> {
raw.split('\n')
.map(|line| {
if words {
line_to_digits(line)
} else {
line.chars()
.filter(|c| c.is_digit(10))
.collect::<VecDeque<char>>()
}
})
.collect()
}
fn get_values(raw: &mut Vec<VecDeque<char>>) -> Vec<usize> {
raw.iter_mut()
.filter(|line| line.len() > 0)
.map(|line| {
let first = match line.pop_front() {
Some(c) => c,
None => '0',
};
let last = match line.pop_back() {
Some(c) => c,
None => first,
};
let str: String = Vec::from([first, last]).into_iter().collect();
return usize::from_str_radix(&str, 10).unwrap();
})
.collect()
}
fn sum(raw: Vec<usize>) -> usize {
raw.into_iter().reduce(|accum, item| accum + item).unwrap()
}
fn main() {
let file_str = include_str!("input.txt");
let mut lines = get_lines(file_str, false);
let values = get_values(&mut lines);
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);
}
}