1
1
mirror of https://github.com/theoludwig/advent_of_code.git synced 2025-02-20 14:38:48 +01:00

217 lines
7.8 KiB
Rust

use std::collections::HashMap;
#[derive(Debug, PartialEq, Default, Clone, Copy)]
pub struct GearPosition {
pub index_line: usize,
pub index_character: usize,
}
#[derive(Debug, PartialEq, Default, Clone, Copy)]
pub struct NumberPosition {
pub index_start: usize,
pub index_end: usize,
pub value: usize,
pub gear_position: GearPosition,
}
pub fn get_numbers_positions_from_line(line: &str) -> Vec<NumberPosition> {
let mut result = vec![];
let mut number_string = String::from("");
let mut index_start = 0;
let mut index_end = 0;
for (index_character, character) in line.chars().enumerate() {
if character.is_ascii_digit() {
if number_string.is_empty() {
index_start = index_character;
}
index_end = index_character;
number_string += &character.to_string();
} else if !number_string.is_empty() {
let value: usize = number_string.parse().expect("Should parse as a `usize`");
result.push(NumberPosition {
index_start,
index_end,
value,
gear_position: GearPosition::default(),
});
number_string = String::from("");
index_start = 0;
index_end = 0;
}
}
if !number_string.is_empty() {
let value: usize = number_string.parse().expect("Should parse as a `usize`");
result.push(NumberPosition {
index_start,
index_end,
value,
gear_position: GearPosition::default(),
});
}
result
}
pub fn is_symbol(character: char) -> bool {
!character.is_ascii_digit() && character != '.'
}
pub fn is_gear_symbol(character: char) -> bool {
character == '*'
}
pub fn part_1(input: &str) -> usize {
let lines = input.lines().collect::<Vec<&str>>();
lines
.iter()
.enumerate()
.map(|(index_line, &line)| {
get_numbers_positions_from_line(line)
.iter()
.filter(|&number_position| {
let index_start = number_position.index_start.saturating_sub(1);
let index_end = if number_position.index_end + 1 >= line.len() {
line.len().saturating_sub(1)
} else {
number_position.index_end + 1
};
let has_symbol_on_the_left =
line.chars().nth(index_start).is_some_and(is_symbol);
let has_symbol_on_the_right =
line.chars().nth(index_end).is_some_and(is_symbol);
let has_symbol_on_the_top = lines
.get(index_line.saturating_sub(1))
.is_some_and(|&line| {
line.get(index_start..=index_end)
.is_some_and(|value| value.chars().any(is_symbol))
});
let has_symbol_on_the_bottom = lines.get(index_line + 1).is_some_and(|&line| {
line.get(index_start..=index_end)
.is_some_and(|value| value.chars().any(is_symbol))
});
has_symbol_on_the_left
|| has_symbol_on_the_right
|| has_symbol_on_the_top
|| has_symbol_on_the_bottom
})
.map(|number_position| number_position.value)
.sum::<usize>()
})
.sum()
}
pub fn part_2(input: &str) -> usize {
let lines = input.lines().collect::<Vec<&str>>();
let mut number_positions = vec![];
lines.iter().enumerate().for_each(|(index_line, &line)| {
get_numbers_positions_from_line(line)
.iter()
.for_each(|&number_position| {
let index_start = number_position.index_start.saturating_sub(1);
let index_end = if number_position.index_end + 1 >= line.len() {
line.len().saturating_sub(1)
} else {
number_position.index_end + 1
};
let has_symbol_on_the_left =
line.chars().nth(index_start).is_some_and(is_gear_symbol);
if has_symbol_on_the_left {
let mut number_position = number_position.to_owned();
number_position.gear_position.index_line = index_line;
number_position.gear_position.index_character = index_start;
number_positions.push(number_position);
}
let has_symbol_on_the_right =
line.chars().nth(index_end).is_some_and(is_gear_symbol);
if has_symbol_on_the_right {
let mut number_position = number_position.to_owned();
number_position.gear_position.index_line = index_line;
number_position.gear_position.index_character = index_end;
number_positions.push(number_position);
}
let index_character_top =
lines.get(index_line.saturating_sub(1)).and_then(|&line| {
line.get(index_start..=index_end)
.and_then(|value| value.chars().position(is_gear_symbol))
});
if let Some(index_character_top) = index_character_top {
let mut number_position = number_position.to_owned();
number_position.gear_position.index_line = index_line.saturating_sub(1);
number_position.gear_position.index_character =
index_character_top + index_start;
number_positions.push(number_position);
}
let index_character_bottom = lines.get(index_line + 1).and_then(|&line| {
line.get(index_start..=index_end)
.and_then(|value| value.chars().position(is_gear_symbol))
});
if let Some(index_character_bottom) = index_character_bottom {
let mut number_position = number_position.to_owned();
number_position.gear_position.index_line = index_line + 1;
number_position.gear_position.index_character =
index_character_bottom + index_start;
number_positions.push(number_position);
}
});
});
// Key: "index_line-index_character"
// Value: usize
let mut gear_positions: HashMap<String, Vec<usize>> = HashMap::new();
number_positions.iter().for_each(|&number_position| {
let key = format!(
"{}-{}",
number_position.gear_position.index_line, number_position.gear_position.index_character,
);
match gear_positions.get_mut(&key) {
Some(gear_positions) => {
gear_positions.push(number_position.value);
}
None => {
gear_positions.insert(key, vec![number_position.value]);
}
}
});
let mut result = 0;
for (_, value) in gear_positions.iter() {
if value.len() != 2 {
continue;
}
let first_number = value.first();
let second_number = value.last();
if let (Some(first_number), Some(second_number)) = (first_number, second_number) {
result += first_number * second_number;
}
}
result
}
#[cfg(test)]
mod day_3_tests {
use super::*;
#[test]
fn test_part_1_example() {
assert_eq!(part_1(include_str!("../input_example_1.txt")), 4361);
}
#[test]
fn test_part_2_example() {
assert_eq!(part_2(include_str!("../input_example_1.txt")), 467835);
}
}