| use std::collections::HashMap; |
| |
| fn is_symbol(character: &char) -> bool { |
| return !character.is_digit(10) && *character != '.'; |
| } |
| |
| #[derive(Debug, Clone)] |
| struct SchematicNumber { |
| number: u16, |
| surroundings: Vec<char> |
| } |
| |
| impl SchematicNumber { |
| fn is_part_number(&self) -> bool { |
| self.surroundings.iter().any(is_symbol) |
| } |
| } |
| |
| #[derive(Debug)] |
| struct Gear { |
| surroundings: Vec<SchematicNumber> |
| } |
| |
| impl Gear { |
| fn sum(&self) -> u64 { |
| let mut part_number_count = 0; |
| let mut part_number_mul: u64 = 1; |
| |
| for schematic_number in self.surroundings.iter() { |
| if schematic_number.is_part_number() { |
| part_number_count += 1; |
| part_number_mul *= Into::<u64>::into(schematic_number.number); |
| } |
| } |
| |
| if part_number_count != 2 { |
| return 0; |
| } |
| |
| return part_number_mul; |
| } |
| fn add_number(&mut self, number: SchematicNumber) { |
| self.surroundings.push(number); |
| } |
| } |
| |
| fn process_gear(number: &SchematicNumber, gears: &mut HashMap<(usize, usize), Gear>, row: usize, col: usize) { |
| let position = gears.get_mut(&(row, col)); |
| |
| if let Some(gear) = position { |
| gear.add_number(number.clone()) |
| } else { |
| gears.insert((row, col), Gear { surroundings: vec![ number.clone() ] }); |
| } |
| } |
| |
| fn process_number(gears: &mut HashMap<(usize, usize), Gear>, input: &str, digits: String, row: usize, start_col: usize, end_col: usize) -> SchematicNumber { |
| let mut surroundings: Vec<char> = vec![]; |
| |
| let input_lines: Vec<&str> = input.split('\n').collect(); |
| let prev_line: Vec<char> = (if row > 0 { input_lines[row - 1] } else { "" }).chars().collect(); |
| let current_line: Vec<char> = input_lines[row].chars().collect(); |
| let next_line: Vec<char> = (if row < input_lines.len() - 1 { input_lines[row + 1] } else { "" }).chars().collect(); |
| |
| let mut gear_positions: Vec<(usize, usize)> = vec![]; |
| |
| if start_col > 0 { |
| surroundings.push(current_line[start_col - 1]); |
| if current_line[start_col - 1] == '*' { |
| gear_positions.push((row, start_col - 1)); |
| } |
| } |
| |
| if end_col < current_line.len() - 1 { |
| surroundings.push(current_line[end_col + 1]); |
| if current_line[end_col + 1] == '*' { |
| gear_positions.push((row, end_col + 1)); |
| } |
| } |
| |
| if !prev_line.is_empty() { |
| for col in start_col..end_col+1 { |
| surroundings.push(prev_line[col]); |
| if prev_line[col] == '*' { |
| gear_positions.push((row - 1, col)); |
| } |
| } |
| if start_col > 0 { |
| surroundings.push(prev_line[start_col - 1]); |
| if prev_line[start_col - 1] == '*' { |
| gear_positions.push((row - 1, start_col - 1)); |
| } |
| } |
| if end_col < prev_line.len() - 1 { |
| surroundings.push(prev_line[end_col + 1]); |
| if prev_line[end_col + 1] == '*' { |
| gear_positions.push((row - 1, end_col + 1)); |
| } |
| } |
| } |
| |
| if !next_line.is_empty() { |
| for col in start_col..end_col+1{ |
| surroundings.push(next_line[col]); |
| if next_line[col] == '*' { |
| gear_positions.push((row + 1, col)); |
| } |
| } |
| if start_col > 0 { |
| surroundings.push(next_line[start_col - 1]); |
| if next_line[start_col - 1] == '*' { |
| gear_positions.push((row + 1, start_col - 1)); |
| } |
| } |
| if end_col < next_line.len() - 1 { |
| surroundings.push(next_line[end_col + 1]); |
| if next_line[end_col + 1] == '*' { |
| gear_positions.push((row + 1, end_col + 1)); |
| } |
| } |
| } |
| |
| println!("Surroundings: {:?}, digits: {}", surroundings, digits); |
| |
| let schematic_number = SchematicNumber { |
| number: digits.parse().unwrap(), |
| surroundings |
| }; |
| |
| println!("{:#?}: is_part_number: {}", schematic_number, schematic_number.is_part_number()); |
| |
| for pos in gear_positions { |
| process_gear(&schematic_number, gears, pos.0, pos.1); |
| } |
| |
| schematic_number |
| } |
| |
| fn main() { |
| let input = include_str!("../input.txt"); |
| |
| let mut numbers: Vec<SchematicNumber> = vec![]; |
| let mut gears: HashMap<(usize, usize), Gear> = HashMap::new(); |
| |
| let mut number_start: Option<usize> = None; |
| let mut number_digits = "".to_owned(); |
| |
| // Find numbers and their surroundings |
| for (row, line) in input.split('\n').enumerate() { |
| for (col, character) in line.chars().enumerate() { |
| match (number_start, character.is_digit(10)) { |
| (Some(_), true) => { |
| number_digits.push(character); |
| println!("{} is still a digit. number_digits is now {}", character, number_digits); |
| }, |
| (Some(start_col), false) => { |
| numbers.push(process_number(&mut gears, input, number_digits, row, start_col, col - 1)); |
| number_digits = "".to_owned(); |
| number_start = None; |
| }, |
| (None, true) => { |
| number_digits.push(character); |
| number_start = Some(col); |
| println!("{} is a digit. Starting a new number at postition {} with number_digits {}", character, number_start.unwrap(), number_digits); |
| }, |
| (None, false) => {} |
| } |
| } |
| if let Some(start_col) = number_start { |
| numbers.push(process_number(&mut gears, input, number_digits, row, start_col, line.len() - 1)); |
| number_digits = "".to_owned(); |
| number_start = None; |
| } |
| } |
| |
| let mut numbers_total: u64 = 0; |
| let mut gears_total: u64 = 0; |
| |
| for number in numbers { |
| println!("{:?}, part_no: {}", number, number.is_part_number()); |
| if number.is_part_number() { |
| numbers_total += Into::<u64>::into(number.number); |
| } |
| } |
| |
| for gear in gears.values() { |
| println!("{:?}, sum: {}", gear, gear.sum()); |
| gears_total += gear.sum(); |
| } |
| |
| println!("{}", numbers_total); |
| println!("{}", gears_total); |
| } |