Zytkowski's Thought Dumpster

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())
}