use std::{collections::HashMap, error::Error, fs}; /// Solves the problem for day 04. /// /// # Errors /// /// This function will return an error if the file cannot be read or if the input is invalid. #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] pub fn solve_day05(path: &str) -> Result<(i32, i32), Box<dyn Error>> { let content = fs::read_to_string(path)?; let (rules, updates) = parse_input(&content); let ordered_updates = updates .iter() .map(|update| order_updates(update, &rules)) .collect::<Vec<_>>(); let result_one = updates .iter() .zip(ordered_updates.iter()) .filter(|(a, b)| a == b) .map(|(a, _)| a.clone()) .collect::<Vec<_>>(); let result_two = updates .iter() .zip(ordered_updates.iter()) .filter(|(a, b)| a != b) .map(|(_, b)| b.clone()) .collect::<Vec<_>>(); let part_one = sum_middle_value(&result_one); let part_two = sum_middle_value(&result_two); Ok((part_one, part_two)) } fn parse_input(input: &str) -> (HashMap<i32, Vec<i32>>, Vec<Vec<i32>>) { let parts: Vec<_> = input.split("\n\n").collect(); let mut map: HashMap<i32, Vec<i32>> = HashMap::new(); parts[0].split('\n').for_each(|r| { let values = r .split('|') .map(|i| i.parse().unwrap()) .collect::<Vec<i32>>(); let index = values[0]; map.entry(index) .or_default() .append(values[1..].to_vec().as_mut()); }); // .collect(); let updates: Vec<Vec<i32>> = parts[1] .split('\n') .map(|r| r.split(',').map(|i| i.parse().unwrap()).collect()) .collect(); (map, updates) } fn order_updates(updates: &[i32], rules: &HashMap<i32, Vec<i32>>) -> Vec<i32> { if !updates.iter().any(|u| rules.contains_key(u)) { return updates.to_vec(); } let mut ordered_updates: Vec<i32> = updates.to_vec(); ordered_updates.sort_by(|a, b| { for (key, values) in rules { if a == key && values.contains(b) { return std::cmp::Ordering::Less; // } else if b == key && values.contains(a) { // return std::cmp::Ordering::Greater; // } else if a < b { // return std::cmp::Ordering::Less; // } else if a > b { // return std::cmp::Ordering::Greater; } } std::cmp::Ordering::Equal }); ordered_updates } fn sum_middle_value(input: &[Vec<i32>]) -> i32 { input .iter() .filter_map(|row| { if row.is_empty() || row.len() % 2 == 0 { None } else { Some(row[row.len() / 2]) } }) .sum() } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_input() { let input = "47|53 97|13 97|61 97|47 75|29 61|13 75|53 29|13 97|29 53|29 61|53 97|53 61|29 47|13 75|47 97|75 47|61 75|61 47|29 75|13 53|13 75,47,61,53,29 97,61,53,29,13 29,13,75 75,97,47,61,53 61,13,29 97,13,75,29,47"; let (rules, updates) = parse_input(input); assert_eq!(rules.len(), 6); assert_eq!(rules[&47], vec![53, 13, 61, 29]); assert_eq!(rules[&53], vec![29, 13]); assert_eq!(updates.len(), 6); assert_eq!(updates[0], vec![75, 47, 61, 53, 29]); let sum = sum_middle_value(&updates); assert_eq!(sum, 61 + 53 + 13 + 47 + 13 + 75); let want = vec![75, 29, 13]; let got = order_updates(&updates[2], &rules); dbg!(&got, &updates[2]); assert_eq!(got, want); let want = vec![97, 75, 47, 61, 53]; let got = order_updates(&updates[3], &rules); dbg!(&got, &updates[3]); assert_eq!(got, want); let want = vec![61, 29, 13]; let got = order_updates(&updates[4], &rules); dbg!(&got, &updates[4]); assert_eq!(got, want); let want = vec![97, 75, 47, 29, 13]; let got = order_updates(&updates[5], &rules); dbg!(&got, &want, &updates[5]); assert_eq!(got, want); let want = 61; let got = sum_middle_value(&[order_updates(&updates[0], &rules)]); assert_eq!(got, want); let want = 143; let got = sum_middle_value( &updates .iter() .take(3) .map(|u| order_updates(u, &rules)) .collect::<Vec<_>>(), ); assert_eq!(got, want); } }