1pub fn day1_preprocess(input: &str) -> (Vec<u32>, Vec<u32>) {
2 let pairs: Vec<Vec<u32>> = input
3 .lines()
4 .map(|line| {
5 line.split_ascii_whitespace()
6 .map(|location_id| location_id.parse().unwrap())
7 .collect()
8 })
9 .collect();
10
11 let mut left = pairs.iter().map(|pair| pair[0]).collect::<Vec<_>>();
12 let mut right = pairs.iter().map(|pair| pair[1]).collect::<Vec<_>>();
13 left.sort();
14 right.sort();
15 (left, right)
16}
17pub fn day1_part1(input: &str) -> String {
18 let (left, right) = day1_preprocess(input);
19 let sum: u32 = left
20 .iter()
21 .zip(right)
22 .map(|(left, right)| left.abs_diff(right))
23 .sum();
24 sum.to_string()
25}
26
27pub fn day1_part2(input: &str) -> String {
28 let (left, right) = day1_preprocess(input);
29 let similarity_score: u32 = left
30 .into_iter()
31 .map(|left_location| {
32 left_location
33 * right
34 .iter()
35 .filter(|&&right_location| right_location == left_location)
36 .count() as u32
37 })
38 .sum();
39 similarity_score.to_string()
40}
41
42pub mod day2 {
43 fn day2_preprocess(input: &str) -> Vec<Vec<u32>> {
44 input
45 .lines()
46 .map(|report| {
47 report
48 .split_ascii_whitespace()
49 .map(|level| level.parse().unwrap())
50 .collect()
51 })
52 .collect()
53 }
54 pub fn day2_part1(input: &str) -> String {
55 let reports = day2_preprocess(input);
56 reports
57 .into_iter()
58 .filter(gradual)
59 .filter(monotonic)
60 .count()
61 .to_string()
62 }
63 pub fn day2_part2(input: &str) -> String {
64 let reports = day2_preprocess(input);
65 reports
66 .into_iter()
67 .filter(should_filter_out_part2)
68 .count()
69 .to_string()
70 }
71 fn should_filter_out_part2(report: &Vec<u32>) -> bool {
72 if gradual(report) && monotonic(report) {
73 return true;
74 }
75 for index_to_skip in 0..report.len() {
76 let skipped_report: Vec<u32> = report
77 .iter()
78 .enumerate()
79 .filter(|(i, _)| *i != index_to_skip)
80 .map(|(_, elem)| *elem)
81 .collect();
82 if gradual(&skipped_report) && monotonic(&skipped_report) {
83 return true;
84 }
85 }
86 false
87 }
88
89 fn gradual(report: &Vec<u32>) -> bool {
90 for i in 1..report.len() {
91 let diff = report[i].abs_diff(report[i - 1]);
92 if !(1..=3).contains(&diff) {
93 return false;
94 }
95 }
96 true
97 }
98
99 //fails if not gradual
100 fn monotonic(report: &Vec<u32>) -> bool {
101 let increasing = report[0] < report[1];
102 for i in 1..report.len() {
103 if increasing {
104 if report[i] < report[i - 1] {
105 return false;
106 }
107 } else if report[i] > report[i - 1] {
108 return false;
109 }
110 }
111 true
112 }
113}
114
115pub mod day3 {
116 use regex::Regex;
117
118 pub fn day3_part1(input: &str) -> String {
119 let r = Regex::new(r"mul\((\d+),(\d+)\)").unwrap();
120 let multiplicands: Vec<(u32, u32)> = r
121 .captures_iter(input)
122 .map(|captures| {
123 dbg!(&captures);
124 (captures[1].parse().unwrap(), captures[2].parse().unwrap())
125 })
126 .collect();
127 let products: u32 = multiplicands.into_iter().map(|(l, r)| l * r).sum();
128 products.to_string()
129 }
130
131 pub fn day3_part2(input: &str) -> String {
132 let r = Regex::new(r"(?:do\(\))|(?:don't\(\))|(?:mul\((\d+),(\d+)\))").unwrap();
133 let mut multiply = true;
134 let mut products: Vec<(u32, u32)> = vec![];
135 for captures in r.captures_iter(input) {
136 dbg!(&captures);
137 if &captures[0] == "do()" {
138 multiply = true;
139 } else if &captures[0] == "don't()" {
140 multiply = false;
141 } else if multiply {
142 products.push((captures[1].parse().unwrap(), captures[2].parse().unwrap()));
143 }
144 }
145 let sum: u32 = products.into_iter().map(|(l, r)| l * r).sum();
146 sum.to_string()
147 }
148}
149
150pub mod day4 {
151 use regex::Regex;
152
153 pub fn day4_part1(input: &str) -> String {
154 let pattern = Regex::new(r"XMAS").unwrap();
155 let reverse_pattern = Regex::new(r"SAMX").unwrap();
156 let mut count = 0;
157
158 count += pattern.find_iter(input).count();
159 count += reverse_pattern.find_iter(input).count();
160 dbg!(count);
161
162 let transposed = dbg!(transpose_string(input));
163
164 count += pattern.find_iter(&transposed).count();
165 count += reverse_pattern.find_iter(&transposed).count();
166
167 dbg!(count);
168 let diag1 = dbg!(rotate_string_45degrees_counterclockwise(input));
169 count += pattern.find_iter(&diag1).count();
170 count += reverse_pattern.find_iter(&diag1).count();
171
172 dbg!(count);
173 let diag2 = dbg!(rotate_string_45degrees_clockwise(input));
174 count += pattern.find_iter(&diag2).count();
175 count += reverse_pattern.find_iter(&diag2).count();
176
177 dbg!(count);
178 count.to_string()
179 }
180
181 pub fn day4_part2(input: &str) -> String{
182 let input = string_to_array(input);
183 let mut count = 0;
184 for row in 1..input.len()-1 {
185 for col in 1..input.len()-1 {
186 if is_x_mas(&input, row, col) {
187 count += 1;
188 }
189 }
190 }
191 count.to_string()
192 }
193
194 /* S&M combinations for X-MAS
195 MS
196 MS
197
198 SM
199 SM
200
201 MM
202 SS
203
204 SS
205 MM */
206 //precondition: row and col are one away from the edge
207 fn is_x_mas(input: &Vec<Vec<char>>, row: usize, col: usize) -> bool {
208 if input[row][col] != 'A' {
209 return false;
210 }
211 if input[row-1][col-1] == 'M' {
212 if input[row-1][col+1] == 'M' {
213 input[row+1][col+1] == 'S' && input[row+1][col-1] == 'S'
214 } else if input[row+1][col-1] == 'M' {
215 input[row+1][col+1] == 'S' && input[row-1][col+1] == 'S'
216 } else {
217 false
218 }
219 } else if input[row-1][col-1] == 'S' {
220 if input[row-1][col+1] == 'S' {
221 input[row+1][col+1] == 'M' && input[row+1][col-1] == 'M'
222 } else if input[row+1][col-1] == 'S' {
223 input[row+1][col+1] == 'M' && input[row-1][col+1] == 'M'
224 } else {
225 false
226 }
227 } else {
228 false
229 }
230
231 }
232
233 //squares only
234 fn rotate_string_45degrees_counterclockwise(input: &str) -> String {
235 let array_input = string_to_array(input);
236 assert_eq!(array_input.len(), array_input[0].len());
237 let mut array_output = vec![vec!['\0'; array_input.len()]; array_input.len() * 2 - 1];
238
239 // [(0, 5)]
240 // [(0, 4), (1, 5)]
241 // [(0, 3), (1, 4), (2, 5)]
242 // [(0, 2), (1, 3), (2, 4), (3, 5)]
243 // [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
244 // [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
245 // [(1, 0), (2, 1), (3, 2), (4, 3), (5, 4)]
246 // [(2, 0), (3, 1), (4, 2), (5, 3)]
247 // [(3, 0), (4, 1), (5, 2)]
248 // [(4, 0), (5, 1)]
249 // [(5, 0)]
250
251 for input_row in 0..array_input.len() {
252 for input_col in 0..array_input.len() {
253 let output_row = (array_input.len() - 1) as isize + (input_row as isize - input_col as isize);
254 array_output[output_row as usize].push(array_input[input_row][input_col]);
255 }
256 }
257
258 let horistrings = array_output
259 .into_iter()
260 .map(|row| row.iter().filter(|&&c| c != '\0').collect::<String>())
261 .collect::<Vec<_>>();
262 horistrings.join("\n")
263 }
264
265 fn rotate_string_45degrees_clockwise(input: &str) -> String {
266 let mut unstrung = string_to_array(input);
267 flip_horizontal(&mut unstrung);
268 let restrung = horizontal_strings(&unstrung);
269 rotate_string_45degrees_counterclockwise(&restrung.join("\n"))
270 }
271
272 #[cfg(test)]
273 #[test]
274 fn string_rotation_tests() {
275 let square_string = "AB\nCD";
276 let rotated = rotate_string_45degrees_counterclockwise(square_string);
277 // B
278 //A D
279 // C
280 assert_eq!(rotated, "B\nAD\nC");
281
282 /*
283 ABCDE
284 FGHIJ
285 KLMNO
286 PQRST
287 UVWXY
288
289 rotated counterclockwise:
290
291 E
292 D J
293 C I O
294 B H N T
295 A G M S Y
296 F L R X
297 K Q W
298 P V
299 U
300
301 */
302
303 let square_5x = "ABCDE\nFGHIJ\nKLMNO\nPQRST\nUVWXY";
304 let rotated = rotate_string_45degrees_counterclockwise(square_5x);
305 day4_part1(square_5x);
306 dbg!(rotated);
307 // panic!()
308 }
309
310 fn transpose_string(input: &str) -> String {
311 let unstrung = string_to_array(input);
312 let transposed = transpose(&unstrung);
313 let horistrings = horizontal_strings(&transposed);
314 horistrings.join("\n")
315 }
316
317 fn string_to_array(input: &str) -> Vec<Vec<char>> {
318 input.lines().map(|line| line.chars().collect()).collect()
319 }
320
321 fn horizontal_strings(input: &Vec<Vec<char>>) -> Vec<String> {
322 input.iter().map(|row| row.iter().collect()).collect()
323 }
324
325 fn transpose(input: &Vec<Vec<char>>) -> Vec<Vec<char>> {
326 let mut output = vec![vec!['\0'; input.len()]; input[0].len()];
327
328 for col in 0..input[0].len() {
329 for row in 0..input.len() {
330 output[col][row] = input[row][col]
331 }
332 }
333 output
334 }
335
336 fn flip_horizontal(input: &mut Vec<Vec<char>>) {
337 for row in 0..input.len() {
338 input[row].reverse()
339 }
340 }
341}
342
343pub mod day5;
344pub mod day6;
345pub mod day7;
346pub mod day8;
347pub mod day9;
348pub mod day10;
349pub mod spatial;
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[test]
356 fn day10_part1_test() {
357 let test_result = day10::day10_part1(include_str!("../input/day10.test.txt"));
358 assert_eq!(test_result, "36");
359 let result = day10::day10_part1(include_str!("../input/day10.txt"));
360 assert_eq!(result, "820");
361 }
362 #[test]
363 fn day10_part2_test() {
364 let test_result = day10::day10_part2(include_str!("../input/day10_simple2.test.txt"));
365 assert_eq!(test_result, "3");
366 let test_result = day10::day10_part2(include_str!("../input/day10_simple.test.txt"));
367 assert_eq!(test_result, "13");
368 let test_result = day10::day10_part2(include_str!("../input/day10.test.txt"));
369 assert_eq!(test_result, "81");
370 let result = day10::day10_part2(include_str!("../input/day10.txt"));
371 assert_eq!(result, "1786");
372 }
373
374 #[test]
375 fn day9_part1_test() {
376 let test_result = day9::day9_part1(include_str!("../input/day9.test.txt"));
377 assert_eq!(test_result, "1928");
378 let result = day9::day9_part1(include_str!("../input/day9.txt"));
379 assert_eq!(result, "6448989155953");
380 }
381 #[test]
382 fn day9_part2_test() {
383 let test_result = day9::day9_part2(include_str!("../input/day9.test.txt"));
384 assert_eq!(test_result, "2858");
385 let result = day9::day9_part2(include_str!("../input/day9.txt"));
386 assert_eq!(result, "6476642796832");
387 }
388
389 #[test]
390 fn day8_part1_test() {
391 let simple_result = day8::day8_part1(include_str!("../input/day8.simple.test.txt"));
392 assert_eq!(simple_result, "2");
393 let simple2_result = day8::day8_part1(include_str!("../input/day8.simple2.test.txt"));
394 assert_eq!(simple2_result, "4");
395 let test_result = day8::day8_part1(include_str!("../input/day8.test.txt"));
396 assert_eq!(test_result, "14");
397 let result = day8::day8_part1(include_str!("../input/day8.txt"));
398 // assert!(result.parse::<u64>().unwrap() < 414); //too high
399 assert_eq!(result, "369");
400 }
401 #[test]
402 fn day8_part2_test() {
403 let test_result = day8::day8_part2(include_str!("../input/day8.test.txt"));
404 assert_eq!(test_result, "34");
405 let result = day8::day8_part2(include_str!("../input/day8.txt"));
406 assert_eq!(result, "1169");
407 }
408
409 #[test]
410 fn day7_part1_test() {
411 let test_result = day7::day7_part1(include_str!("../input/day7.test.txt"));
412 assert_eq!(test_result, "3749");
413 let result = day7::day7_part1(include_str!("../input/day7.txt"));
414 assert_eq!(result, "3598800864292");
415 }
416
417 #[ignore]
418 #[test]
419 fn day7_part2_test() {
420 let simple_result = day7::day7_part2(include_str!("../input/day7_custom.test.txt"));
421 assert_eq!(simple_result, (12345 + 12034050 + 15 + 120).to_string());
422
423 let test_result = day7::day7_part2(include_str!("../input/day7.test.txt"));
424 assert_eq!(test_result, "11387");
425 let result = day7::day7_part2(include_str!("../input/day7.txt"));
426 let parsed_result: u64 = result.parse::<u64>().unwrap();
427 assert!(parsed_result > 318_910_516_761_637, "{parsed_result} was too low");
428 assert_eq!(parsed_result, 340_362_529_351_427);
429 }
430
431 #[test]
432 fn day6_part1_test() {
433 let test_result = day6::day6_part1(include_str!("../input/day6.test.txt"));
434 assert_eq!(test_result, "41");
435 let result = day6::day6_part1(include_str!("../input/day6.txt"));
436 assert_eq!(result, "5131");
437 }
438
439 #[ignore]
440 #[test]
441 fn day6_part2_test() {
442 let test_result = day6::day6_part2(include_str!("../input/day6.test.txt"));
443 assert_eq!(test_result, "6");
444 let result = day6::day6_part2(include_str!("../input/day6.txt"));
445 assert_eq!(result, "1784");
446 }
447
448 #[test]
449 fn day5_part1_test() {
450 let test_result = day5::day5_part1(include_str!("../input/day5.test.txt"));
451 assert_eq!(test_result, "143");
452 let result = day5::day5_part1(include_str!("../input/day5.txt"));
453 assert_eq!(result, "5452");
454 }
455
456 #[test]
457 fn day5_part2_test() {
458 let test_result = day5::day5_part2(include_str!("../input/day5.test.txt"));
459 assert_eq!(test_result, "123");
460 let result = day5::day5_part2(include_str!("../input/day5.txt"));
461 assert_eq!(result, "4598");
462 }
463
464 #[test]
465 fn day4_part1_test() {
466 let simplest = "..X...\n.SAMX.\n.A..A.\nXMAS.S\n.X....\n......";
467 let test_result = day4::day4_part1(simplest);
468 assert_eq!(test_result, "4");
469
470 let test_result = day4::day4_part1(include_str!("../input/day4.test.txt"));
471 assert_eq!(test_result, "18");
472 let result = day4::day4_part1(include_str!("../input/day4.txt"));
473 assert_ne!(result, "2092"); //too low
474 assert_eq!(result, "2493");
475 }
476
477 #[test]
478 fn day4_part2_test() {
479 let test_result = day4::day4_part2(include_str!("../input/day4.test.txt"));
480 assert_eq!(test_result, "9");
481 let result = day4::day4_part2(include_str!("../input/day4.txt"));
482 assert_eq!(result, "1890");
483 }
484
485 #[test]
486 fn day3_part1_test() {
487 let test_result = day3::day3_part1(include_str!("../input/day3.test.txt"));
488 assert_eq!(test_result, "161");
489 let result = day3::day3_part1(include_str!("../input/day3.txt"));
490 assert_eq!(result, "183788984");
491 }
492
493 #[test]
494 fn day3_part2_test() {
495 let test_result = day3::day3_part2(include_str!("../input/day3_part2.test.txt"));
496 assert_eq!(test_result, "48");
497 let result = day3::day3_part2(include_str!("../input/day3.txt"));
498 assert_eq!(result, "62098619");
499 }
500
501 #[test]
502 fn day2_part1_test() {
503 let test_result = day2::day2_part1(include_str!("../input/day2.test.txt"));
504 assert_eq!(test_result, "2");
505 let result = day2::day2_part1(include_str!("../input/day2.txt"));
506 assert_eq!(result, "213");
507 }
508 #[test]
509 fn day2_part2_test() {
510 let test_result = day2::day2_part2(include_str!("../input/day2.test.txt"));
511 assert_eq!(test_result, "4");
512 let result = day2::day2_part2(include_str!("../input/day2.txt"));
513 assert_eq!(result, "285");
514 }
515
516 #[test]
517 fn day1_part1_test() {
518 let test_result = day1_part1(include_str!("../input/day1_part1.test.txt"));
519 assert_eq!(test_result, "11");
520 let result = day1_part1(include_str!("../input/day1.txt"));
521 assert_eq!(result, "2742123");
522 }
523
524 #[test]
525 fn day1_part2_test() {
526 let test_result = day1_part2(include_str!("../input/day1_part1.test.txt"));
527 assert_eq!(test_result, "31");
528 let result = day1_part2(include_str!("../input/day1.txt"));
529 assert_eq!(result, "21328497");
530 }
531}