use std::{ fs::File, io::{BufRead as _, BufReader}, }; fn get_lines(name: &str) -> impl Iterator { BufReader::new(File::open(name).unwrap()) .lines() .map(|line| line.unwrap()) } mod day1 { use std::collections::HashMap; use crate::get_lines; #[allow(dead_code)] pub fn task1() { let mut a = Vec::::new(); let mut b = Vec::::new(); for line in get_lines("input.txt") { let (a1, b1) = line.trim_end().split_once(" ").unwrap(); a.push(a1.parse().unwrap()); b.push(b1.parse().unwrap()); } a.sort_unstable(); b.sort_unstable(); let result: i32 = a.into_iter().zip(b).map(|(a, b)| (a - b).abs()).sum(); println!("{result}"); } #[allow(dead_code)] pub fn task2() { let mut a = Vec::::new(); let mut b = HashMap::::new(); for line in get_lines("input.txt") { let (a1, b1) = line.trim_end().split_once(" ").unwrap(); a.push(a1.parse().unwrap()); *b.entry(b1.parse().unwrap()).or_insert(0) += 1; } let result: u32 = a.into_iter().map(|a| a * *b.get(&a).unwrap_or(&0)).sum(); println!("{result}"); } } mod day2 { use itertools::Itertools; use crate::get_lines; fn safe(iter: impl Iterator) -> bool { let mut order = None; for (a, b) in iter.tuple_windows() { if !(1..4).contains(&(a - b).abs()) { return false; } if order.is_none() { order = Some(a.cmp(&b)); } else if order != Some(a.cmp(&b)) { return false; } } true } fn parse_line(line: &str) -> impl Iterator + '_ { line.trim() .split(' ') .map(|num| num.parse::().unwrap()) } #[allow(dead_code)] pub fn task1() { let result = get_lines("input.txt") .filter(|line| safe(parse_line(line))) .count(); println!("{result}"); } #[allow(dead_code)] pub fn task2() { let result = get_lines("input.txt") .filter(|line| { let numbers = parse_line(line).collect_vec(); if safe(numbers.iter().copied()) { return true; } let mut test = Vec::with_capacity(numbers.len() - 1); for i in 0..numbers.len() { test.extend_from_slice(&numbers[..i]); test.extend_from_slice(&numbers[i + 1..]); if safe(test.iter().copied()) { return true; } test.clear(); } false }) .count(); println!("{result}"); } } mod day3 { use std::fs::read_to_string; use regex::Regex; #[allow(dead_code)] pub fn task1() { let rx = Regex::new(r"mul\((\d{1,3}),(\d{1,3})\)").unwrap(); let mut result: i64 = 0; for c in rx.captures_iter(&read_to_string("input.txt").unwrap()) { let (_, [a, b]) = c.extract(); result += a.parse::().unwrap() * b.parse::().unwrap(); } println!("{result}"); } #[allow(dead_code)] pub fn task2() { let rx = Regex::new(r"(mul)\((\d{1,3}),(\d{1,3})\)|do()()()\(\)|do(n't)()()\(\)").unwrap(); let mut enabled = true; let mut result: i64 = 0; for c in rx.captures_iter(&read_to_string("input.txt").unwrap()) { let (_, [op, a, b]) = c.extract(); match op { "" => enabled = true, "n't" => enabled = false, "mul" if enabled => { result += a.parse::().unwrap() * b.parse::().unwrap(); } "mul" => {} _ => unreachable!("{op}"), } } println!("{result}"); } } mod day4 { use itertools::Itertools; #[allow(dead_code)] pub fn task1() { fn check(vals: &[u8]) -> usize { (vals == b"XMAS" || vals == b"SAMX").into() } let data = std::fs::read_to_string("input.txt").unwrap(); let mut lines = Vec::new(); let mut count = 0; for line in data.lines() { count += line.matches("XMAS").count(); count += line.matches("SAMX").count(); lines.push(line.as_bytes()); } for (i, &line) in lines[0..lines.len() - 3].iter().enumerate() { for j in 0..line.len() { count += check(&[ lines[i][j], lines[i + 1][j], lines[i + 2][j], lines[i + 3][j], ]); if j + 3 < line.len() { count += check(&[ lines[i][j], lines[i + 1][j + 1], lines[i + 2][j + 2], lines[i + 3][j + 3], ]); } if j > 2 { count += check(&[ lines[i][j], lines[i + 1][j - 1], lines[i + 2][j - 2], lines[i + 3][j - 3], ]); } } } println!("{count}"); } #[allow(dead_code)] pub fn task2() { fn check(vals: &[u8]) -> usize { (vals == b"MAS" || vals == b"SAM").into() } let data = std::fs::read_to_string("input.txt").unwrap(); let lines = data.lines().map(str::as_bytes).collect_vec(); let mut count: usize = 0; for i in 0..lines[..lines.len() - 2].len() { for j in 0..lines[i].len() - 2 { count += check(&[lines[i][j], lines[i + 1][j + 1], lines[i + 2][j + 2]]) * check(&[lines[i][j + 2], lines[i + 1][j + 1], lines[i + 2][j]]); } } println!("{count}"); } } mod day5 { use std::{ cmp::Ordering, collections::{HashMap, HashSet}, }; use itertools::Itertools; type Rules = HashMap>; fn parse_rules<'a>(lines: &mut impl Iterator) -> Rules { let mut rules: Rules = HashMap::new(); for rule in lines.by_ref().take_while(|s| !s.is_empty()) { let vals = rule.split_once('|').unwrap(); let (before, after) = (vals.0.parse().unwrap(), vals.1.parse().unwrap()); rules.entry(before).or_default().insert(after); } rules } fn parse_updates<'a>( lines: impl Iterator + 'a, ) -> impl Iterator> + 'a { lines.map(|line| { line.trim() .split(',') .map(|num| num.parse::().unwrap()) .collect_vec() }) } fn check_update(update: &[i32], rules: &Rules) -> bool { for (i, val) in update.iter().enumerate() { let Some(local_rules) = rules.get(val) else { continue; }; if update[..i].iter().any(|elem| local_rules.contains(elem)) { return false; } } true } #[allow(dead_code)] pub fn task1() { let data = std::fs::read_to_string("input.txt").unwrap(); let mut lines = data.lines(); let rules = parse_rules(&mut lines); let result: i32 = parse_updates(lines) .filter(|u| check_update(u, &rules)) .map(|u| u[u.len() / 2]) .sum(); println!("{result}"); } fn compare(a: i32, b: i32, rules: &Rules) -> Ordering { if let Some(rule) = rules.get(&a) { if rule.contains(&b) { return Ordering::Less; } } if let Some(rule) = rules.get(&b) { if rule.contains(&a) { return Ordering::Greater; } } Ordering::Equal } #[allow(dead_code)] pub fn task2() { let data = std::fs::read_to_string("input.txt").unwrap(); let mut lines = data.lines(); let rules = parse_rules(&mut lines); let result: i32 = parse_updates(lines) .filter(|u| !check_update(u, &rules)) .map(|mut u| { u.sort_unstable_by(|a, b| compare(*a, *b, &rules)); u[u.len() / 2] }) .sum(); println!("{result}"); } } fn main() { day5::task1(); day5::task2(); }