pub fn day1_preprocess(input: &str) -> (Vec, Vec) { let pairs: Vec> = input .lines() .map(|line| { line.split_ascii_whitespace() .map(|location_id| location_id.parse().unwrap()) .collect() }) .collect(); let mut left = pairs.iter().map(|pair| pair[0]).collect::>(); let mut right = pairs.iter().map(|pair| pair[1]).collect::>(); left.sort(); right.sort(); (left, right) } pub fn day1_part1(input: &str) -> String { let (left, right) = day1_preprocess(input); let sum: u32 = left .iter() .zip(right) .map(|(left, right)| left.abs_diff(right)) .sum(); sum.to_string() } pub fn day1_part2(input: &str) -> String { let (left, right) = day1_preprocess(input); let similarity_score: u32 = left .into_iter() .map(|left_location| { left_location * right .iter() .filter(|&&right_location| right_location == left_location) .count() as u32 }) .sum(); similarity_score.to_string() } pub mod day2 { fn day2_preprocess(input: &str) -> Vec> { input .lines() .map(|report| { report .split_ascii_whitespace() .map(|level| level.parse().unwrap()) .collect() }) .collect() } pub fn day2_part1(input: &str) -> String { let reports = day2_preprocess(input); reports .into_iter() .filter(gradual) .filter(monotonic) .count() .to_string() } pub fn day2_part2(input: &str) -> String { let reports = day2_preprocess(input); reports .into_iter() .filter(should_filter_out_part2) .count() .to_string() } fn should_filter_out_part2(report: &Vec) -> bool { if gradual(report) && monotonic(report) { return true; } for index_to_skip in 0..report.len() { let skipped_report: Vec = report .iter() .enumerate() .filter(|(i, _)| *i != index_to_skip) .map(|(_, elem)| *elem) .collect(); if gradual(&skipped_report) && monotonic(&skipped_report) { return true; } } false } fn gradual(report: &Vec) -> bool { for i in 1..report.len() { let diff = report[i].abs_diff(report[i - 1]); if !(1..=3).contains(&diff) { return false; } } true } //fails if not gradual fn monotonic(report: &Vec) -> bool { let increasing = report[0] < report[1]; for i in 1..report.len() { if increasing { if report[i] < report[i - 1] { return false; } } else if report[i] > report[i - 1] { return false; } } true } } pub mod day3 { use regex::Regex; pub fn day3_part1(input: &str) -> String { let r = Regex::new(r"mul\((\d+),(\d+)\)").unwrap(); let multiplicands: Vec<(u32, u32)> = r .captures_iter(input) .map(|captures| { dbg!(&captures); (captures[1].parse().unwrap(), captures[2].parse().unwrap()) }) .collect(); let products: u32 = multiplicands.into_iter().map(|(l, r)| l * r).sum(); products.to_string() } pub fn day3_part2(input: &str) -> String { let r = Regex::new(r"(?:do\(\))|(?:don't\(\))|(?:mul\((\d+),(\d+)\))").unwrap(); let mut multiply = true; let mut products: Vec<(u32, u32)> = vec![]; for captures in r.captures_iter(input) { dbg!(&captures); if &captures[0] == "do()" { multiply = true; } else if &captures[0] == "don't()" { multiply = false; } else if multiply { products.push((captures[1].parse().unwrap(), captures[2].parse().unwrap())); } } let sum: u32 = products.into_iter().map(|(l, r)| l * r).sum(); sum.to_string() } } pub mod day4 { use regex::Regex; pub fn day4_part1(input: &str) -> String { let pattern = Regex::new(r"XMAS").unwrap(); let reverse_pattern = Regex::new(r"SAMX").unwrap(); let mut count = 0; count += pattern.find_iter(input).count(); count += reverse_pattern.find_iter(input).count(); dbg!(count); let transposed = dbg!(transpose_string(input)); count += pattern.find_iter(&transposed).count(); count += reverse_pattern.find_iter(&transposed).count(); dbg!(count); let diag1 = dbg!(rotate_string_45degrees_counterclockwise(input)); count += pattern.find_iter(&diag1).count(); count += reverse_pattern.find_iter(&diag1).count(); dbg!(count); let diag2 = dbg!(rotate_string_45degrees_clockwise(input)); count += pattern.find_iter(&diag2).count(); count += reverse_pattern.find_iter(&diag2).count(); dbg!(count); count.to_string() } pub fn day4_part2(input: &str) -> String{ let input = string_to_array(input); let mut count = 0; for row in 1..input.len()-1 { for col in 1..input.len()-1 { if is_x_mas(&input, row, col) { count += 1; } } } count.to_string() } /* S&M combinations for X-MAS MS MS SM SM MM SS SS MM */ //precondition: row and col are one away from the edge fn is_x_mas(input: &Vec>, row: usize, col: usize) -> bool { if input[row][col] != 'A' { return false; } if input[row-1][col-1] == 'M' { if input[row-1][col+1] == 'M' { input[row+1][col+1] == 'S' && input[row+1][col-1] == 'S' } else if input[row+1][col-1] == 'M' { input[row+1][col+1] == 'S' && input[row-1][col+1] == 'S' } else { false } } else if input[row-1][col-1] == 'S' { if input[row-1][col+1] == 'S' { input[row+1][col+1] == 'M' && input[row+1][col-1] == 'M' } else if input[row+1][col-1] == 'S' { input[row+1][col+1] == 'M' && input[row-1][col+1] == 'M' } else { false } } else { false } } //squares only fn rotate_string_45degrees_counterclockwise(input: &str) -> String { let array_input = string_to_array(input); assert_eq!(array_input.len(), array_input[0].len()); let mut array_output = vec![vec!['\0'; array_input.len()]; array_input.len() * 2 - 1]; // [(0, 5)] // [(0, 4), (1, 5)] // [(0, 3), (1, 4), (2, 5)] // [(0, 2), (1, 3), (2, 4), (3, 5)] // [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)] // [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)] // [(1, 0), (2, 1), (3, 2), (4, 3), (5, 4)] // [(2, 0), (3, 1), (4, 2), (5, 3)] // [(3, 0), (4, 1), (5, 2)] // [(4, 0), (5, 1)] // [(5, 0)] for input_row in 0..array_input.len() { for input_col in 0..array_input.len() { let output_row = (array_input.len() - 1) as isize + (input_row as isize - input_col as isize); array_output[output_row as usize].push(array_input[input_row][input_col]); } } let horistrings = array_output .into_iter() .map(|row| row.iter().filter(|&&c| c != '\0').collect::()) .collect::>(); horistrings.join("\n") } fn rotate_string_45degrees_clockwise(input: &str) -> String { let mut unstrung = string_to_array(input); flip_horizontal(&mut unstrung); let restrung = horizontal_strings(&unstrung); rotate_string_45degrees_counterclockwise(&restrung.join("\n")) } #[cfg(test)] #[test] fn string_rotation_tests() { let square_string = "AB\nCD"; let rotated = rotate_string_45degrees_counterclockwise(square_string); // B //A D // C assert_eq!(rotated, "B\nAD\nC"); /* ABCDE FGHIJ KLMNO PQRST UVWXY rotated counterclockwise: E D J C I O B H N T A G M S Y F L R X K Q W P V U */ let square_5x = "ABCDE\nFGHIJ\nKLMNO\nPQRST\nUVWXY"; let rotated = rotate_string_45degrees_counterclockwise(square_5x); day4_part1(square_5x); dbg!(rotated); // panic!() } fn transpose_string(input: &str) -> String { let unstrung = string_to_array(input); let transposed = transpose(&unstrung); let horistrings = horizontal_strings(&transposed); horistrings.join("\n") } fn string_to_array(input: &str) -> Vec> { input.lines().map(|line| line.chars().collect()).collect() } fn horizontal_strings(input: &Vec>) -> Vec { input.iter().map(|row| row.iter().collect()).collect() } fn transpose(input: &Vec>) -> Vec> { let mut output = vec![vec!['\0'; input.len()]; input[0].len()]; for col in 0..input[0].len() { for row in 0..input.len() { output[col][row] = input[row][col] } } output } fn flip_horizontal(input: &mut Vec>) { for row in 0..input.len() { input[row].reverse() } } } pub mod day5; pub mod day6; pub mod day7; pub mod day8; pub mod day9; pub mod day10; pub mod spatial; #[cfg(test)] mod tests { use super::*; #[test] fn day10_part1_test() { let test_result = day10::day10_part1(include_str!("../input/day10.test.txt")); assert_eq!(test_result, "36"); let result = day10::day10_part1(include_str!("../input/day10.txt")); assert_eq!(result, "820"); } #[test] fn day10_part2_test() { let test_result = day10::day10_part2(include_str!("../input/day10_simple2.test.txt")); assert_eq!(test_result, "3"); let test_result = day10::day10_part2(include_str!("../input/day10_simple.test.txt")); assert_eq!(test_result, "13"); let test_result = day10::day10_part2(include_str!("../input/day10.test.txt")); assert_eq!(test_result, "81"); let result = day10::day10_part2(include_str!("../input/day10.txt")); assert_eq!(result, "1786"); } #[test] fn day9_part1_test() { let test_result = day9::day9_part1(include_str!("../input/day9.test.txt")); assert_eq!(test_result, "1928"); let result = day9::day9_part1(include_str!("../input/day9.txt")); assert_eq!(result, "6448989155953"); } #[test] fn day9_part2_test() { let test_result = day9::day9_part2(include_str!("../input/day9.test.txt")); assert_eq!(test_result, "2858"); let result = day9::day9_part2(include_str!("../input/day9.txt")); assert_eq!(result, "6476642796832"); } #[test] fn day8_part1_test() { let simple_result = day8::day8_part1(include_str!("../input/day8.simple.test.txt")); assert_eq!(simple_result, "2"); let simple2_result = day8::day8_part1(include_str!("../input/day8.simple2.test.txt")); assert_eq!(simple2_result, "4"); let test_result = day8::day8_part1(include_str!("../input/day8.test.txt")); assert_eq!(test_result, "14"); let result = day8::day8_part1(include_str!("../input/day8.txt")); // assert!(result.parse::().unwrap() < 414); //too high assert_eq!(result, "369"); } #[test] fn day8_part2_test() { let test_result = day8::day8_part2(include_str!("../input/day8.test.txt")); assert_eq!(test_result, "34"); let result = day8::day8_part2(include_str!("../input/day8.txt")); assert_eq!(result, "1169"); } #[test] fn day7_part1_test() { let test_result = day7::day7_part1(include_str!("../input/day7.test.txt")); assert_eq!(test_result, "3749"); let result = day7::day7_part1(include_str!("../input/day7.txt")); assert_eq!(result, "3598800864292"); } #[ignore] #[test] fn day7_part2_test() { let simple_result = day7::day7_part2(include_str!("../input/day7_custom.test.txt")); assert_eq!(simple_result, (12345 + 12034050 + 15 + 120).to_string()); let test_result = day7::day7_part2(include_str!("../input/day7.test.txt")); assert_eq!(test_result, "11387"); let result = day7::day7_part2(include_str!("../input/day7.txt")); let parsed_result: u64 = result.parse::().unwrap(); assert!(parsed_result > 318_910_516_761_637, "{parsed_result} was too low"); assert_eq!(parsed_result, 340_362_529_351_427); } #[test] fn day6_part1_test() { let test_result = day6::day6_part1(include_str!("../input/day6.test.txt")); assert_eq!(test_result, "41"); let result = day6::day6_part1(include_str!("../input/day6.txt")); assert_eq!(result, "5131"); } #[ignore] #[test] fn day6_part2_test() { let test_result = day6::day6_part2(include_str!("../input/day6.test.txt")); assert_eq!(test_result, "6"); let result = day6::day6_part2(include_str!("../input/day6.txt")); assert_eq!(result, "1784"); } #[test] fn day5_part1_test() { let test_result = day5::day5_part1(include_str!("../input/day5.test.txt")); assert_eq!(test_result, "143"); let result = day5::day5_part1(include_str!("../input/day5.txt")); assert_eq!(result, "5452"); } #[test] fn day5_part2_test() { let test_result = day5::day5_part2(include_str!("../input/day5.test.txt")); assert_eq!(test_result, "123"); let result = day5::day5_part2(include_str!("../input/day5.txt")); assert_eq!(result, "4598"); } #[test] fn day4_part1_test() { let simplest = "..X...\n.SAMX.\n.A..A.\nXMAS.S\n.X....\n......"; let test_result = day4::day4_part1(simplest); assert_eq!(test_result, "4"); let test_result = day4::day4_part1(include_str!("../input/day4.test.txt")); assert_eq!(test_result, "18"); let result = day4::day4_part1(include_str!("../input/day4.txt")); assert_ne!(result, "2092"); //too low assert_eq!(result, "2493"); } #[test] fn day4_part2_test() { let test_result = day4::day4_part2(include_str!("../input/day4.test.txt")); assert_eq!(test_result, "9"); let result = day4::day4_part2(include_str!("../input/day4.txt")); assert_eq!(result, "1890"); } #[test] fn day3_part1_test() { let test_result = day3::day3_part1(include_str!("../input/day3.test.txt")); assert_eq!(test_result, "161"); let result = day3::day3_part1(include_str!("../input/day3.txt")); assert_eq!(result, "183788984"); } #[test] fn day3_part2_test() { let test_result = day3::day3_part2(include_str!("../input/day3_part2.test.txt")); assert_eq!(test_result, "48"); let result = day3::day3_part2(include_str!("../input/day3.txt")); assert_eq!(result, "62098619"); } #[test] fn day2_part1_test() { let test_result = day2::day2_part1(include_str!("../input/day2.test.txt")); assert_eq!(test_result, "2"); let result = day2::day2_part1(include_str!("../input/day2.txt")); assert_eq!(result, "213"); } #[test] fn day2_part2_test() { let test_result = day2::day2_part2(include_str!("../input/day2.test.txt")); assert_eq!(test_result, "4"); let result = day2::day2_part2(include_str!("../input/day2.txt")); assert_eq!(result, "285"); } #[test] fn day1_part1_test() { let test_result = day1_part1(include_str!("../input/day1_part1.test.txt")); assert_eq!(test_result, "11"); let result = day1_part1(include_str!("../input/day1.txt")); assert_eq!(result, "2742123"); } #[test] fn day1_part2_test() { let test_result = day1_part2(include_str!("../input/day1_part1.test.txt")); assert_eq!(test_result, "31"); let result = day1_part2(include_str!("../input/day1.txt")); assert_eq!(result, "21328497"); } }