Advent of Code 2023 - Day 03
I'ts that time of year again, the Advent of Code! I'm completing the challenges this year using Rust 🦀 Day 3 is quite a challenge for index haters 😅.
I'll be posting them here, just to document how I'm solving each challenge, and I highly encourage you to check them out before reading my solutions!
Here's the Day 3 Challenge
BTW: Complete Part 1 to have access to Part 2 😉
Solution for Part 1
use std::fs::File;
use std::io::{BufRead, BufReader, Lines, Result};
use std::path::Path;
use std::str::FromStr;
fn main() {
let input_file_path = "absolute_path_to_your_input_file";
let mut curr_sum = 0;
let iterator = for_each_line(input_file_path)
.unwrap_or_else(|_| panic!("Could not find input file {}", input_file_path));
let mut engine_matrix: Vec<Vec<char>> = vec![];
for line in iterator.flatten() {
let mut row_vec: Vec<char> = vec![];
for char in line.chars() {
row_vec.push(char);
}
engine_matrix.push(row_vec);
}
for (row_num, row) in engine_matrix.iter().enumerate() {
println!("Row {}", row_num);
for (char_idx, char) in row.iter().enumerate() {
// Current char is not numeric, so we skip
if !char.is_numeric() {
continue;
}
// We are in the middle of a number, that we should have already processed.
if char_idx > 0 && row.get(char_idx - 1).unwrap().is_numeric() {
continue;
}
// We hit the first digit of a number
let (num_len, num) = determine_full_number(&char_idx, &row);
let mut has_adjacent_symbol = false;
// Look up
if row_num > 0 {
has_adjacent_symbol = contains_symbols_on_idx(&engine_matrix[row_num - 1], char_idx, num_len)
}
// Look down
if row_num < engine_matrix.len() - 1 && !has_adjacent_symbol {
has_adjacent_symbol = contains_symbols_on_idx(&engine_matrix[row_num + 1], char_idx, num_len)
}
// Look left
if char_idx > 0 && !has_adjacent_symbol {
has_adjacent_symbol = !row[char_idx - 1].is_alphanumeric() && row[char_idx - 1] != '.'
}
// Look right
if char_idx + num_len < row.len() && !has_adjacent_symbol {
has_adjacent_symbol = !row[char_idx + num_len].is_alphanumeric() && row[char_idx + num_len] != '.'
}
if has_adjacent_symbol {
println!("({}, {}) {} has adjacent symbols", row_num, char_idx, num);
curr_sum += num;
} else {
println!("({}, {}) {} do not have adjacent symbols", row_num, char_idx, num);
}
}
}
println!("Sum of all numbers with adjacent symbols is: {}", curr_sum);
}
fn contains_symbols_on_idx(vec: &Vec<char>, start_idx: usize, len: usize) -> bool {
// 0123456789
// ......153.
let row = vec.as_slice();
let start_index = if start_idx > 0 {
start_idx - 1
} else {
0
};
let end_index = if start_index + len + 1 > row.len() - 1 {
start_index + len
} else {
start_index + len + 1
};
for char in &row[start_index..end_index + 1] {
if !char.is_alphanumeric() && char != &'.' {
return true;
}
}
false
}
fn determine_full_number(char_idx: &usize, vec: &&Vec<char>) -> (usize, i32) {
let row = vec.as_slice();
let mut num_as_str = String::from(row[*char_idx]);
let mut idx_carrier = Some(char_idx + 1);
while let Some(idx) = idx_carrier {
if row[idx].is_numeric() {
num_as_str.push(row[idx]);
}
idx_carrier = Some(idx + 1);
if row.len() == idx + 1 || !row[idx + 1].is_numeric() {
idx_carrier = None;
}
}
(
num_as_str.len(),
i32::from_str(&num_as_str)
.unwrap_or_else(|_| panic!("{} is an invalid i32", num_as_str))
)
}
fn for_each_line<P: AsRef<Path>>(filename: P) -> Result<Lines<BufReader<File>>> {
let file = File::open(filename)?;
Ok(BufReader::new(file).lines())
}
Solution for Part 2
use std::collections::HashMap;
use std::fs::File;
use std::io::{BufRead, BufReader, Lines, Result};
use std::path::Path;
use std::str::FromStr;
fn main() {
let input_file_path = "absolute_path_to_your_input_file";
let mut curr_sum = 0;
let iterator = for_each_line(input_file_path)
.unwrap_or_else(|_| panic!("Could not find input file {}", input_file_path));
let mut engine_matrix: Vec<Vec<char>> = vec![];
for line in iterator.flatten() {
let mut row_vec: Vec<char> = vec![];
for char in line.chars() {
row_vec.push(char);
}
engine_matrix.push(row_vec);
}
let mut gears : HashMap<(usize, usize), Vec<i32>> = HashMap::new();
for (row_num, row) in engine_matrix.iter().enumerate() {
for (char_idx, char) in row.iter().enumerate() {
if char == &'*' {
println!("Inserting vec {}, {}", char_idx, row_num);
gears.insert((char_idx, row_num), Vec::new());
}
}
}
for (row_num, row) in engine_matrix.iter().enumerate() {
println!("Row {}", row_num);
for (char_idx, char) in row.iter().enumerate() {
// Current char is not numeric, so we skip
if !char.is_numeric() {
continue;
}
// We are in the middle of a number, that we should have already processed.
if char_idx > 0 && row.get(char_idx - 1).unwrap().is_numeric() {
continue;
}
// We hit the first digit of a number
let (num_len, num) = determine_full_number(&char_idx, &row);
// Look up
if row_num > 0 {
let star_indices = star_indices(&engine_matrix[row_num - 1], char_idx, num_len);
for star_idx in star_indices {
let gear_vec = gears.get_mut(&(star_idx, row_num - 1)).unwrap();
gear_vec.push(num);
}
}
// Look down
if row_num < engine_matrix.len() - 1 {
let star_indices = star_indices(&engine_matrix[row_num + 1], char_idx, num_len);
for star_idx in star_indices {
let gear_vec = gears.get_mut(&(star_idx, row_num+1)).unwrap();
gear_vec.push(num);
}
}
// Look left
if char_idx > 0 && row[char_idx - 1] == '*' {
let gear_vec = gears.get_mut(&(char_idx - 1, row_num)).unwrap();
gear_vec.push(num);
}
// Look right
if char_idx + num_len < row.len() && row[char_idx + num_len] == '*' {
let gear_vec = gears.get_mut(&(char_idx + num_len, row_num)).unwrap();
gear_vec.push(num);
}
}
}
for (gear, vec) in gears.iter() {
if vec.len() < 2 {
continue;
}
let vec_slice = vec.as_slice();
curr_sum += vec_slice[0] * vec_slice[1]
}
println!("Sum of all gear ratios is: {}", curr_sum);
}
fn star_indices(vec: &Vec<char>, start_idx: usize, len: usize) -> Vec<usize> {
// 0123456789
// ..*...*...
let row = vec.as_slice();
let mut star_indices : Vec<usize> = Vec::new();
let start_index = if start_idx > 0 {
start_idx - 1
} else {
0
};
let end_index = if start_index + len + 1 > row.len() - 1 {
start_index + len
} else {
start_index + len + 1
};
for idx in start_index..=end_index {
if row[idx] == '*' {
star_indices.push(idx);
}
}
star_indices
}
fn determine_full_number(char_idx: &usize, vec: &&Vec<char>) -> (usize, i32) {
let row = vec.as_slice();
let mut num_as_str = String::from(row[*char_idx]);
let mut idx_carrier = Some(char_idx + 1);
while let Some(idx) = idx_carrier {
if row[idx].is_numeric() {
num_as_str.push(row[idx]);
}
idx_carrier = Some(idx + 1);
if row.len() == idx + 1 || !row[idx + 1].is_numeric() {
idx_carrier = None;
}
}
(
num_as_str.len(),
i32::from_str(&num_as_str)
.unwrap_or_else(|_| panic!("{} is an invalid i32", num_as_str))
)
}
fn for_each_line<P: AsRef<Path>>(filename: P) -> Result<Lines<BufReader<File>>> {
let file = File::open(filename)?;
Ok(BufReader::new(file).lines())
}