diff options
Diffstat (limited to '2024_rust/src/bin')
-rw-r--r-- | 2024_rust/src/bin/day1.rs | 49 | ||||
-rw-r--r-- | 2024_rust/src/bin/day2.rs | 69 | ||||
-rw-r--r-- | 2024_rust/src/bin/day3.rs | 37 | ||||
-rw-r--r-- | 2024_rust/src/bin/day4.rs | 141 | ||||
-rw-r--r-- | 2024_rust/src/bin/day5.rs | 224 | ||||
-rw-r--r-- | 2024_rust/src/bin/dayN.rs | 15 |
6 files changed, 535 insertions, 0 deletions
diff --git a/2024_rust/src/bin/day1.rs b/2024_rust/src/bin/day1.rs new file mode 100644 index 0000000..2e0f01d --- /dev/null +++ b/2024_rust/src/bin/day1.rs @@ -0,0 +1,49 @@ +fn p1(input: &str) -> String { + let mut left: Vec<u32> = vec![]; + let mut right: Vec<u32> = vec![]; + for line in input.lines() { + let mut fields = line.split_whitespace(); + let e = "Wrong file format"; + left.push(fields.next().expect(e).parse().expect(e)); + right.push(fields.next().expect(e).parse().expect(e)); + } + left.sort_unstable(); + right.sort_unstable(); + + let mut distance: u32 = 0; + for i in 0..left.len() { + let l = left[i]; + let r = right[i]; + distance += if l >= r { l - r } else { r - l }; + } + + distance.to_string() +} + +use std::collections::HashMap; + +fn p2(input: &str) -> String { + let mut left: Vec<u32> = vec![]; + let mut right: HashMap<u32, u32> = HashMap::new(); + for line in input.lines() { + let mut fields = line.split_whitespace(); + let e = "Wrong file format"; + left.push(fields.next().expect(e).parse().expect(e)); + let r: u32 = fields.next().expect(e).parse().expect(e); + match right.get(&r) { + None => right.insert(r, 1), + Some(v) => right.insert(r, v + 1), + }; + } + + let mut distance: u32 = 0; + for l in left { + distance += l * right.get(&l).unwrap_or(&0); + } + + distance.to_string() +} + +fn main() { + aoc2024::run_day("1", p1, p2); +} diff --git a/2024_rust/src/bin/day2.rs b/2024_rust/src/bin/day2.rs new file mode 100644 index 0000000..92d288f --- /dev/null +++ b/2024_rust/src/bin/day2.rs @@ -0,0 +1,69 @@ +#[derive(PartialEq, Clone, Copy)] +enum Direction { + Up, + Down, + Unknown, +} +use Direction::*; + +fn is_safe(levels: &[u32]) -> bool { + let mut direction = Unknown; + for i in 0..levels.len() - 1 { + let [x, y] = levels[i..=i + 1] else { + unreachable!() + }; + let (diff, d) = if x > y { (x - y, Down) } else { (y - x, Up) }; + if direction == Unknown { + direction = d; + } + if diff == 0 || diff > 3 || direction != d { + return false; + } + } + return true; +} + +fn p1(input: &str) -> String { + let mut total = 0; + for report in input.lines() { + let levels: Vec<u32> = report + .split_whitespace() + .map(|l| l.parse().unwrap()) + .collect(); + if is_safe(&levels) { + total += 1; + } + } + + total.to_string() +} + +fn is_safe_with_dampener(levels: &[u32]) -> bool { + for i in 0..levels.len() { + let mut levels_without_i: Vec<u32> = levels.to_vec(); + levels_without_i.remove(i); + if is_safe(&levels_without_i) { + return true; + } + } + return false; +} + +fn p2(input: &str) -> String { + let mut total = 0; + for report in input.lines() { + let levels: Vec<u32> = report + .split_whitespace() + .map(|l| l.parse().unwrap()) + .collect(); + if is_safe(&levels) || is_safe_with_dampener(&levels) { + total += 1; + } + } + + total.to_string() +} + +fn main() { + aoc2024::run_day("2", p1, p2); +} diff --git a/2024_rust/src/bin/day3.rs b/2024_rust/src/bin/day3.rs new file mode 100644 index 0000000..1eedefe --- /dev/null +++ b/2024_rust/src/bin/day3.rs @@ -0,0 +1,37 @@ +use regex::Regex; + +fn p1(input: &str) -> String { + let re = Regex::new(r"mul\(([0-9]{1,3}),([0-9]{1,3})\)").unwrap(); + + let mut result: u32 = 0; + for (_, [x, y]) in re.captures_iter(input).map(|cs| cs.extract()) { + result += x.parse::<u32>().unwrap() * y.parse::<u32>().unwrap(); + } + + result.to_string() +} + +fn p2(input: &str) -> String { + let re = Regex::new(r"do\(\)|don't\(\)|mul\(([0-9]{1,3}),([0-9]{1,3})\)").unwrap(); + + let mut doing = true; + let mut result: u32 = 0; + for cs in re.captures_iter(input) { + let mut it = cs.iter().flatten().map(|m| m.as_str()); + match it.next().unwrap() { + "do()" => doing = true, + "don't()" => doing = false, + mul if doing && mul.starts_with("mul") => { + let mut next = || it.next().unwrap().parse::<u32>().unwrap(); + result += next() * next(); + } + _ => (), + } + } + + result.to_string() +} + +fn main() { + aoc2024::run_day("3", p1, p2); +} diff --git a/2024_rust/src/bin/day4.rs b/2024_rust/src/bin/day4.rs new file mode 100644 index 0000000..7eb7b7a --- /dev/null +++ b/2024_rust/src/bin/day4.rs @@ -0,0 +1,141 @@ +const XMAS: &str = "XMAS"; +const MAS: &str = "MAS"; + +type Position = (usize, usize); +type Row = Vec<Position>; +type Rows = Vec<Row>; +type Move = (Position, (usize, isize)); + +struct Matrix { + chars: Vec<Vec<char>>, + limit: Position, +} + +impl Matrix { + fn new(text: &str) -> Self { + let chars: Vec<Vec<char>> = text.lines().map(|row| row.chars().collect()).collect(); + let limit = (chars.len(), chars[0].len()); + Self { chars, limit } + } + + fn check_iter<'a, I>(&self, it: I, needle: &str) -> bool + where + I: Iterator<Item = &'a Position>, + { + for (c, (i, j)) in needle.chars().zip(it) { + if self.chars[*i][*j] != c { + return false; + } + } + + true + } + + fn check_row(&self, row: &Row, needle: &str) -> bool { + self.check_iter(row.iter(), needle) || self.check_iter(row.iter().rev(), needle) + } +} + +struct Square<'a> { + pos: Position, + size: usize, + matrix: &'a Matrix, +} + +impl Square<'_> { + fn rows<M>(&self, moves: M) -> Rows + where + M: Fn(&Square) -> Vec<Move>, + { + let mut rs: Rows = vec![]; + + // Horizontal / vertical / diagonal + for (p0, (di, dj)) in moves(self) { + // Bounds check + if p0.0 + di * self.size > self.matrix.limit.0 + || (dj > 0 && offplus(p0.1, dj * self.size as isize) > self.matrix.limit.1) + || p0.1 >= self.matrix.limit.1 + { + continue; + } + + let mut p_acc = p0; + let mut row: Row = vec![]; + for _idx in 0..self.size { + row.push(p_acc); + p_acc = (p_acc.0 + di, offplus(p_acc.1, dj)); + } + rs.push(row); + } + + rs + } +} + +fn offplus(x: usize, y: isize) -> usize { + (x as isize + y) as usize +} + +fn moves_xmas(Square { pos, size, .. }: &Square) -> Vec<Move> { + let v = vec![ + (*pos, (0, 1)), // diag from pos, top-down and left-to-right + (*pos, (1, 0)), // diag from pos, top-down and left-to-right + (*pos, (1, 1)), // diag from pos, top-down and left-to-right + ((pos.0, pos.1 + size - 1), (1, -1)), // the other diag, top-down and right-to-left + ]; + v +} + +fn p1(input: &str) -> String { + let matrix: Matrix = Matrix::new(input); + let mut result = 0; + + for i in 0..matrix.limit.0 { + for j in 0..matrix.limit.1 { + let sq = Square { + pos: (i, j), + size: XMAS.len(), + matrix: &matrix, + }; + for row in sq.rows(moves_xmas) { + if matrix.check_row(&row, XMAS) { + result += 1; + } + } + } + } + + result.to_string() +} + +fn moves_mas(Square { pos, size, .. }: &Square) -> Vec<Move> { + let v = vec![ + (*pos, (1, 1)), // diag from pos, top-down and left-to-right + ((pos.0, pos.1 + size - 1), (1, -1)), // the other diag, top-down and right-to-left + ]; + v +} + +fn p2(input: &str) -> String { + let matrix: Matrix = Matrix::new(input); + let mut result = 0; + + for i in 0..=matrix.limit.0 - MAS.len() { + for j in 0..=matrix.limit.1 - MAS.len() { + let sq = Square { + pos: (i, j), + size: MAS.len(), + matrix: &matrix, + }; + if sq.rows(moves_mas).iter().all(|r| matrix.check_row(r, MAS)) { + result += 1; + } + } + } + + result.to_string() +} + +fn main() { + aoc2024::run_day("4", p1, p2); +} diff --git a/2024_rust/src/bin/day5.rs b/2024_rust/src/bin/day5.rs new file mode 100644 index 0000000..fe22295 --- /dev/null +++ b/2024_rust/src/bin/day5.rs @@ -0,0 +1,224 @@ +use regex::Regex; +use std::collections::{HashMap, HashSet}; +use std::str::FromStr; + +type Page = u32; + +#[derive(Debug, Clone)] +struct Rule { + before: Page, + after: Page, +} + +#[derive(Debug)] +enum ParseError { + ParseError, +} + +impl FromStr for Rule { + type Err = ParseError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let Some(cs) = Regex::new("^([0-9]+)\\|([0-9]+)$").unwrap().captures(s) else { + return Err(ParseError::ParseError); + }; + let (_full, [bf, af]) = cs.extract(); + Ok(Rule { + before: bf.parse().unwrap(), + after: af.parse().unwrap(), + }) + } +} + +#[derive(Debug)] +struct RuleStatus { + previous: HashMap<Page, HashSet<Page>>, + invalid: HashSet<Page>, +} + +impl RuleStatus { + fn new(rules: &Vec<Rule>) -> Self { + let mut previous = HashMap::new(); + for &Rule { before, after } in rules.iter() { + match previous.get_mut(&after) { + None => { + let mut set = HashSet::new(); + set.insert(before); + previous.insert(after, set); + } + Some(set) => { + set.insert(before); + } + } + } + RuleStatus { + previous, + invalid: HashSet::new(), + } + } + + fn step(&mut self, page: &Page) -> bool { + let RuleStatus { previous, invalid } = self; + if invalid.contains(page) { + return false; + } + if let Some(prev) = previous.get(page) { + for p in prev.iter() { + invalid.insert(*p); + } + } + true + } +} + +type Update = Vec<Page>; + +fn update_from_str(s: &str) -> Update { + Regex::new("([0-9]+)") + .unwrap() + .find_iter(s) + .map(|s| s.as_str().parse().unwrap()) + .collect() +} + +fn is_update_valid(update: &Update, rules: &Vec<Rule>) -> bool { + let mut status = RuleStatus::new(&rules); + for page in update.iter() { + if !status.step(page) { + return false; + } + } + true +} + +fn middle_page(update: &Update) -> Page { + update[update.len() / 2] +} + +#[derive(Debug)] +struct Input { + rules: Vec<Rule>, + updates: Vec<Update>, +} + +impl FromStr for Input { + type Err = ParseError; + fn from_str(s: &str) -> Result<Self, Self::Err> { + let mut lines = s.lines(); + + let mut input = Input { + rules: vec![], + updates: vec![], + }; + + // Read rules + while let Some(l) = lines.next() { + if l == "" { + break; + } + input.rules.push(Rule::from_str(l).unwrap()); + } + + // Read updates + while let Some(l) = lines.next() { + input.updates.push(update_from_str(l)); + } + + Ok(input) + } +} + +fn sum_valid_updates(Input { rules, updates }: &Input) -> u32 { + updates + .iter() + .filter(|u| is_update_valid(u, &rules)) + .map(|u| middle_page(u)) + .sum() +} + +fn p1(input: &str) -> String { + let input = Input::from_str(input).unwrap(); + sum_valid_updates(&input).to_string() +} + +// Remove useless rules for this particular update +fn filter_rules(rules: &Vec<Rule>, update: &Update) -> Vec<Rule> { + let updated: HashSet<&Page> = update.iter().collect(); + rules + .iter() + .cloned() + .filter(|r| updated.contains(&r.before)) + .collect() +} + +fn sort_rules(rules: &Vec<Rule>) -> Vec<Rule> { + let mut sorted: Vec<Rule> = vec![]; + let mut afters: HashMap<Page, u8> = HashMap::new(); + for a in rules.iter().map(|r| r.after) { + *afters.entry(a).or_default() += 1; + } + + // Allocate new "droppable" rules by wrapping the originals into Options + let mut rules: Vec<Option<&Rule>> = rules.iter().map(|r| Some(r)).collect(); + while rules.iter().any(|x| x.is_some()) { + for rule in rules.iter_mut() { + let Some(r) = *rule else { continue }; + if let None = afters.get(&r.before) { + sorted.push(r.clone()); + match afters.get_mut(&r.after) { + None => { + panic!("This should not happen"); + } + Some(1) => { + afters.remove(&r.after); + } + Some(x) => { + *x -= 1; + } + }; + *rule = None; + } + } + } + + sorted +} + +fn update_reorder(update: &Update, rules: &Vec<Rule>) -> Option<Update> { + let filtered_rules = filter_rules(rules, update); + let sorted_rules = sort_rules(&filtered_rules); + let mut result: Update = vec![]; + let mut pages: HashSet<Page> = update.iter().cloned().collect(); + for Rule { before, .. } in sorted_rules.iter() { + if pages.contains(before) { + result.push(*before); + pages.remove(before); + } + } + // Add unrestricted pages at the end + for &p in pages.iter() { + result.push(p); + } + if is_update_valid(&result, &sorted_rules) { + Some(result) + } else { + None + } +} + +fn sum_reordered_updates(Input { rules, updates }: &Input) -> u32 { + updates + .iter() + .filter(|u| !is_update_valid(u, &rules)) // drop valid updates + .filter_map(|u| update_reorder(u, &rules)) // reorder updates + .map(|u| middle_page(&u)) + .sum() +} + +fn p2(input: &str) -> String { + let input = Input::from_str(input).unwrap(); + sum_reordered_updates(&input).to_string() +} + +fn main() { + aoc2024::run_day("5", p1, p2); +} diff --git a/2024_rust/src/bin/dayN.rs b/2024_rust/src/bin/dayN.rs new file mode 100644 index 0000000..cdaa061 --- /dev/null +++ b/2024_rust/src/bin/dayN.rs @@ -0,0 +1,15 @@ +fn p1(_input: &str) -> String { + let result = "TODO"; + + result.to_string() +} + +fn p2(_input: &str) -> String { + let result = "TODO"; + + result.to_string() +} + +fn main() { + aoc2024::run_day("N", p1, p2); +} |