Add day 3
Whew, they said it got harder on weekends but I do not remember day 3
ever being this difficult. I think, at this point, I'd struggle
recommending 2023 to someone who hadn't done much programming before
which is a real shame.
Today was effectively a parsing problem, with part 2 following on fine
from part one (but not in quite the way I'd hoped, so while my
modifications were small they were larger than I estimated). All in all
I didn't allocate enough time and ended up finishing at a few minutes
past-midnight; lesson learned I guess...
I'm not sure if I missed part of the challenge here, I've had a friend
who had a problem that worked on their input but failed on a mutual's,
and I've also had them tell me that the test case wasn't good enough to
catch their pitfall: clearly there's some mistake I avoided here,
although they also thought part 2 was a cakewalk which I did not.
My solution today isn't particularly beautiful, but it works and
includes both part 1 and part 2. I guess that's all I can hope for, for
quite a rushed solution to a harder problem than estimated. Here's to
hoping for a beautiful challenge in day 4!
Change-Id: I8f75e7b659b424c8afb2895546689cbff0885625
Reviewed-on: https://git.clicks.codes/c/Minion/AdventOfCode/2023/+/162
diff --git a/day3/Cargo.lock b/day3/Cargo.lock
new file mode 100644
index 0000000..4104d70
--- /dev/null
+++ b/day3/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "day3"
+version = "0.1.0"
diff --git a/day3/Cargo.toml b/day3/Cargo.toml
new file mode 100644
index 0000000..898e70d
--- /dev/null
+++ b/day3/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "day3"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/day3/src/main.rs b/day3/src/main.rs
new file mode 100644
index 0000000..8f5bdd8
--- /dev/null
+++ b/day3/src/main.rs
@@ -0,0 +1,193 @@
+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);
+}