blob: 8f5bdd8e2ccc98fe3a5dbef57e1dacae0b22600f [file] [log] [blame]
Skyler Greye69b3512023-12-04 00:07:06 +00001use std::collections::HashMap;
2
3fn is_symbol(character: &char) -> bool {
4 return !character.is_digit(10) && *character != '.';
5}
6
7#[derive(Debug, Clone)]
8struct SchematicNumber {
9 number: u16,
10 surroundings: Vec<char>
11}
12
13impl SchematicNumber {
14 fn is_part_number(&self) -> bool {
15 self.surroundings.iter().any(is_symbol)
16 }
17}
18
19#[derive(Debug)]
20struct Gear {
21 surroundings: Vec<SchematicNumber>
22}
23
24impl Gear {
25 fn sum(&self) -> u64 {
26 let mut part_number_count = 0;
27 let mut part_number_mul: u64 = 1;
28
29 for schematic_number in self.surroundings.iter() {
30 if schematic_number.is_part_number() {
31 part_number_count += 1;
32 part_number_mul *= Into::<u64>::into(schematic_number.number);
33 }
34 }
35
36 if part_number_count != 2 {
37 return 0;
38 }
39
40 return part_number_mul;
41 }
42 fn add_number(&mut self, number: SchematicNumber) {
43 self.surroundings.push(number);
44 }
45}
46
47fn process_gear(number: &SchematicNumber, gears: &mut HashMap<(usize, usize), Gear>, row: usize, col: usize) {
48 let position = gears.get_mut(&(row, col));
49
50 if let Some(gear) = position {
51 gear.add_number(number.clone())
52 } else {
53 gears.insert((row, col), Gear { surroundings: vec![ number.clone() ] });
54 }
55}
56
57fn process_number(gears: &mut HashMap<(usize, usize), Gear>, input: &str, digits: String, row: usize, start_col: usize, end_col: usize) -> SchematicNumber {
58 let mut surroundings: Vec<char> = vec![];
59
60 let input_lines: Vec<&str> = input.split('\n').collect();
61 let prev_line: Vec<char> = (if row > 0 { input_lines[row - 1] } else { "" }).chars().collect();
62 let current_line: Vec<char> = input_lines[row].chars().collect();
63 let next_line: Vec<char> = (if row < input_lines.len() - 1 { input_lines[row + 1] } else { "" }).chars().collect();
64
65 let mut gear_positions: Vec<(usize, usize)> = vec![];
66
67 if start_col > 0 {
68 surroundings.push(current_line[start_col - 1]);
69 if current_line[start_col - 1] == '*' {
70 gear_positions.push((row, start_col - 1));
71 }
72 }
73
74 if end_col < current_line.len() - 1 {
75 surroundings.push(current_line[end_col + 1]);
76 if current_line[end_col + 1] == '*' {
77 gear_positions.push((row, end_col + 1));
78 }
79 }
80
81 if !prev_line.is_empty() {
82 for col in start_col..end_col+1 {
83 surroundings.push(prev_line[col]);
84 if prev_line[col] == '*' {
85 gear_positions.push((row - 1, col));
86 }
87 }
88 if start_col > 0 {
89 surroundings.push(prev_line[start_col - 1]);
90 if prev_line[start_col - 1] == '*' {
91 gear_positions.push((row - 1, start_col - 1));
92 }
93 }
94 if end_col < prev_line.len() - 1 {
95 surroundings.push(prev_line[end_col + 1]);
96 if prev_line[end_col + 1] == '*' {
97 gear_positions.push((row - 1, end_col + 1));
98 }
99 }
100 }
101
102 if !next_line.is_empty() {
103 for col in start_col..end_col+1{
104 surroundings.push(next_line[col]);
105 if next_line[col] == '*' {
106 gear_positions.push((row + 1, col));
107 }
108 }
109 if start_col > 0 {
110 surroundings.push(next_line[start_col - 1]);
111 if next_line[start_col - 1] == '*' {
112 gear_positions.push((row + 1, start_col - 1));
113 }
114 }
115 if end_col < next_line.len() - 1 {
116 surroundings.push(next_line[end_col + 1]);
117 if next_line[end_col + 1] == '*' {
118 gear_positions.push((row + 1, end_col + 1));
119 }
120 }
121 }
122
123 println!("Surroundings: {:?}, digits: {}", surroundings, digits);
124
125 let schematic_number = SchematicNumber {
126 number: digits.parse().unwrap(),
127 surroundings
128 };
129
130 println!("{:#?}: is_part_number: {}", schematic_number, schematic_number.is_part_number());
131
132 for pos in gear_positions {
133 process_gear(&schematic_number, gears, pos.0, pos.1);
134 }
135
136 schematic_number
137}
138
139fn main() {
140 let input = include_str!("../input.txt");
141
142 let mut numbers: Vec<SchematicNumber> = vec![];
143 let mut gears: HashMap<(usize, usize), Gear> = HashMap::new();
144
145 let mut number_start: Option<usize> = None;
146 let mut number_digits = "".to_owned();
147
148 // Find numbers and their surroundings
149 for (row, line) in input.split('\n').enumerate() {
150 for (col, character) in line.chars().enumerate() {
151 match (number_start, character.is_digit(10)) {
152 (Some(_), true) => {
153 number_digits.push(character);
154 println!("{} is still a digit. number_digits is now {}", character, number_digits);
155 },
156 (Some(start_col), false) => {
157 numbers.push(process_number(&mut gears, input, number_digits, row, start_col, col - 1));
158 number_digits = "".to_owned();
159 number_start = None;
160 },
161 (None, true) => {
162 number_digits.push(character);
163 number_start = Some(col);
164 println!("{} is a digit. Starting a new number at postition {} with number_digits {}", character, number_start.unwrap(), number_digits);
165 },
166 (None, false) => {}
167 }
168 }
169 if let Some(start_col) = number_start {
170 numbers.push(process_number(&mut gears, input, number_digits, row, start_col, line.len() - 1));
171 number_digits = "".to_owned();
172 number_start = None;
173 }
174 }
175
176 let mut numbers_total: u64 = 0;
177 let mut gears_total: u64 = 0;
178
179 for number in numbers {
180 println!("{:?}, part_no: {}", number, number.is_part_number());
181 if number.is_part_number() {
182 numbers_total += Into::<u64>::into(number.number);
183 }
184 }
185
186 for gear in gears.values() {
187 println!("{:?}, sum: {}", gear, gear.sum());
188 gears_total += gear.sum();
189 }
190
191 println!("{}", numbers_total);
192 println!("{}", gears_total);
193}