From b24a6f52c0e66e33e98a614c4776ee72b681a52c Mon Sep 17 00:00:00 2001 From: Sebastian Tobie Date: Sat, 14 Dec 2024 11:48:20 +0100 Subject: [PATCH] added incomplete solutions --- examples/2024/08.txt | 12 ++ examples/2024/11.txt | 1 + src/bin/2024/08.rs | 120 +++++++++++++++++ src/bin/2024/11.rs | 193 +++++++++++++++++++++++++++ src/bin/2024/12.rs | 307 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 633 insertions(+) create mode 100644 examples/2024/08.txt create mode 100644 examples/2024/11.txt create mode 100644 src/bin/2024/08.rs create mode 100644 src/bin/2024/11.rs create mode 100644 src/bin/2024/12.rs diff --git a/examples/2024/08.txt b/examples/2024/08.txt new file mode 100644 index 0000000..78a1e91 --- /dev/null +++ b/examples/2024/08.txt @@ -0,0 +1,12 @@ +............ +........0... +.....0...... +.......0.... +....0....... +......A..... +............ +............ +........A... +.........A.. +............ +............ diff --git a/examples/2024/11.txt b/examples/2024/11.txt new file mode 100644 index 0000000..9b26c84 --- /dev/null +++ b/examples/2024/11.txt @@ -0,0 +1 @@ +125 17 diff --git a/src/bin/2024/08.rs b/src/bin/2024/08.rs new file mode 100644 index 0000000..e4abf4b --- /dev/null +++ b/src/bin/2024/08.rs @@ -0,0 +1,120 @@ +use std::collections::HashMap; + +use advent_of_code::{ + strings::{convert_to_array, line_to_char}, + Kartesian, Table, +}; +#[allow(unused_imports)] +use advent_of_code_macros::{include_data, include_example}; +use log::*; + +include_data!(DATA 2024 08); + +const EMPTY: char = '.'; + +fn get_map(maximum: Kartesian) -> Vec> { + let mut resomap = Vec::new(); + let mut m = Vec::new(); + m.resize(maximum.y, EMPTY); + resomap.resize(maximum.x, m); + resomap +} + +fn find_antennas(map: Table) -> HashMap>> { + let mut positions = HashMap::new(); + let mut x = 0; + let mut y; + let len = map[0].len(); + for line in map.clone() { + y = 0; + for char in line { + if char != EMPTY { + if !positions.contains_key(&char) { + positions.insert(char, Vec::with_capacity(len)); + } + positions.get_mut(&char).unwrap().push(Kartesian::new(x, y)); + } + y += 1; + } + x += 1; + } + positions +} + +fn get_resonance(points: Vec>, maximum: Kartesian, harmonic: bool) -> Vec> { + let mut resonances = Vec::new(); + let mut diff; + let mut direction; + for outer in points.clone() { + for inner in points.clone() { + if outer == inner { + continue; + } + (diff, direction) = inner.diff(outer); + loop { + if let Some(reso) = outer.move_dir(diff, direction) { + if reso.x < maximum.x && reso.y < maximum.y { + debug!("Found Point: {}", reso); + resonances.push(reso); + if !harmonic { + break; + } + } else { + break; + } + } else { + break; + } + } + } + } + resonances +} + +fn main() { + let map = convert_to_array::<_, _, '\n'>(DATA, line_to_char); + let maximum = Kartesian::new(map.len(), map[0].len()); + let positions = find_antennas(map.clone()); + let mut tmp; + let mut resonating_points = Vec::new(); + for (group, points) in positions.clone() { + tmp = get_resonance(points.clone(), maximum, false); + println!("Found {} resonances for {} antennas in group {}", tmp.len(), points.len(), group,); + resonating_points.append(&mut tmp); + } + println!("Found {} points before dedup", resonating_points.len()); + resonating_points.dedup(); + println!("{} after simple dedup", resonating_points.len()); + resonating_points = resonating_points + .iter() + .filter(|x| { + for (group, positions) in positions.clone() { + if positions.contains(*x) { + println!("Discarded point {} because it is over an point of group {}", *x, group); + return false; + } + } + true + }) + .cloned() + .collect(); + let mut resomap = map.clone(); + println!("There are {} Resonating points on the map after dedup.", resonating_points.len()); + if resonating_points.len() != 305 { + println!("Wrong solution"); + } + for point in resonating_points { + resomap[point.x][point.y] = '~'; + } + for line in 0..resomap.len() { + print!("{:02}. ", line + 1); + for char in map[line].clone() { + print!("{}", char); + } + print!("|"); + for char in resomap[line].clone() { + print!("{}", char); + } + println!(); + } +} diff --git a/src/bin/2024/11.rs b/src/bin/2024/11.rs new file mode 100644 index 0000000..1e4fb4b --- /dev/null +++ b/src/bin/2024/11.rs @@ -0,0 +1,193 @@ +#![allow(unused)] +use std::collections::HashMap; + +use advent_of_code::{ + numberlength, + strings::{convert_to_array, parsenumber}, + ExtendedOption, +}; +#[allow(unused_imports)] +use advent_of_code_macros::{include_data, include_example}; +use num::Integer; + +include_data!(DATA 2024 11); + +const BILLIONS: u64 = 1000_000_000; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct Stone { + number: u64, + left: usize, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct MapSkip { + newnumber_a: u64, + newnumber_b: u64, + skipped_steps: usize, +} + +#[derive(Debug, Clone)] +struct StoneCounter { + cache: Vec, + map: HashMap>, + stones: u64, + start: usize, + stones_billions: u64, +} + +impl StoneCounter { + fn new(start_values: Vec, rounds: usize) -> StoneCounter { + let mut cache = Vec::with_capacity(start_values.len()); + for v in start_values.iter() { + cache.push(Stone { number: *v, left: rounds }); + } + StoneCounter { + cache: cache, + map: HashMap::with_capacity(10_000), + stones: 0, + start: rounds, + stones_billions: 0, + } + } + + fn blink(&mut self) -> u64 { + let mut stone: Stone; + let mut copy: Stone; + let (mut next, mut second); + while self.cache.len() > 0 { + stone = self.cache.pop().unwrap(); + copy = stone.clone(); + while stone.left > 0 { + if self.map.contains_key(&stone.number) { + match self.map.get(&stone.number).unwrap() { + ExtendedOption::None => { + self.stones += 1; + break; + }, + ExtendedOption::Some(skip) => { + if stone.left < skip.skipped_steps { + stone.left = 0 + } else { + stone.left -= skip.skipped_steps; + } + stone.number = skip.newnumber_a; + self.cache.push(Stone { number: skip.newnumber_b, left: stone.left }); + continue; + }, + ExtendedOption::Unset => {}, + } + } + if stone.left == 1 { + self.map = self + .map + .iter() + .filter_map(|x| { + Some(if *x.1 == ExtendedOption::Unset { + (*x.0, ExtendedOption::None) + } else { + (*x.0, *x.1) + }) + }) + .collect(); + break; + } else { + self.map.insert(stone.number, ExtendedOption::Unset); + } + stone.left -= 1; + (next, second) = blink(stone.number); + match second { + None => { + self.map.insert(stone.number, ExtendedOption::Unset); + stone.number = next; + }, + Some(sec) => { + self.map.insert( + stone.number, + ExtendedOption::Some(MapSkip { + newnumber_a: next, + newnumber_b: sec, + skipped_steps: copy.left - stone.left, + }), + ); + }, + } + } + } + + self.stones_billions * BILLIONS + self.stones + } +} + +fn combine_number(p: &[u64]) -> u64 { + let mut o = 0; + for i in p { + o = o * 10 + i; + } + o +} + +fn splitnumber(i: u64) -> (u64, u64) { + let len = numberlength(i) as usize; + let mut array = Vec::with_capacity(len); + let (mut quot, mut remainder); + quot = i; + loop { + (quot, remainder) = quot.div_rem(&10); + array.insert(0, remainder); + if quot == 0 { + break; + } + } + let (a, b) = array.split_at(len / 2); + (combine_number(a), combine_number(b)) +} + +fn blink(stone: u64) -> (u64, Option) { + if stone == 0 { + (1, None) + } else if numberlength(stone) % 2 == 0 { + let (stone, t) = splitnumber(stone); + (stone, Some(t)) + } else { + (stone * 2024, None) + } +} + +fn main() { + let stoneline = convert_to_array::<_, _, ' '>(DATA, parsenumber); + let mut counter = StoneCounter::new(stoneline.clone(), 25); + let stones = counter.blink(); + debug_assert!(stones == 235850, "{} != {}", stones, 235850); + println!("{:?} Stones after 25 blinks", stones); + counter = StoneCounter::new(stoneline, 75); + println!("{:?} Stones after 75 blinks", counter.blink()); +} + +#[cfg(test)] +mod test { + + use super::*; + + #[test] + fn test_blink() { + let mut res = blink(8); + assert_eq!(res, (16192, None)); + res = blink(res.0); + assert_eq!(res, (32772608, None)); + res = blink(res.0); + assert_eq!(res, (3277, Some(2608))); + } + + #[test] + fn test_stones() { + let testvalues = vec![ + 125, 17, + ]; + let mut counter = StoneCounter::new(testvalues.clone(), 6); + assert_eq!(counter.blink(), 22); + + counter = StoneCounter::new(testvalues.clone(), 25); + assert_eq!(counter.blink(), 55312); + } +} diff --git a/src/bin/2024/12.rs b/src/bin/2024/12.rs new file mode 100644 index 0000000..f46d70f --- /dev/null +++ b/src/bin/2024/12.rs @@ -0,0 +1,307 @@ +#![allow(unused)] +use std::ops::{Mul, Not}; + +use advent_of_code::{strings::convert_to_array, ExtendedOption, Kartesian, KartesianDirection, KartesianIterator, MaximumFromMap, Table, KD}; +#[allow(unused_imports)] +use advent_of_code_macros::{include_data, include_example}; +use log::*; +use num::{iter, Integer}; + +include_data!(DATA 2024 12); + +type Minimap = Vec>; + +#[inline] +fn mul(v: (T, T)) -> T { + v.0 * v.1 +} + +fn calc_perimeter_and_area(minimap: &Minimap) -> (u32, u32) { + //print_minimap(minimap); + let mut rectangle = true; + let length = minimap[0].len(); + for line in minimap.clone() { + rectangle = rectangle && line.len() == length && line.iter().all(|x| *x); + } + if rectangle { + let height = minimap.len() as u32; + (height * length as u32, 2 * (length as u32 + height)) + } else { + let (mut area, mut perimeter) = (0, 0); + for (x, line) in minimap.iter().enumerate() { + debug!("Row: {}", x + 1); + for (y, cell) in line.iter().cloned().enumerate() { + if cell { + area += 1; + } + if cell { + if x == 0 { + debug!(" Top Border in column {}", y + 1); + perimeter += 1; + } + if x == minimap.len() - 1 { + debug!(" Bottom Border"); + perimeter += 1; + } + if y == 0 { + debug!(" Left border on line {}", x + 1); + perimeter += 1 + } + if y == line.len() - 1 { + debug!(" Right border on line {}", x + 1); + perimeter += 1; + } + if x != 0 { + if minimap[x - 1].len() <= y || minimap[x - 1].len() > y && !minimap[x - 1][y] { + debug!(" Top Border on line {} and column {}", x + 1, y + 1); + perimeter += 1; + } + } + if x != minimap.len() - 1 { + if minimap[x + 1].len() <= y || minimap[x + 1].len() > y && !minimap[x + 1][y] { + debug!(" Bottom Border on line {} and column {}", x + 1, y + 1); + perimeter += 1; + } + } + if y != 0 && !minimap[x][y - 1] { + debug!(" Left Border on line {} and column {}", x + 1, y + 1); + perimeter += 1; + } + if y != line.len() - 1 && !minimap[x][y + 1] { + debug!(" Right Border on line {} and column {}", x + 1, y + 1); + perimeter += 1; + } + } + } + } + debug!("Area is {} and perimeter {}", area, perimeter); + (area, perimeter) + } +} + +fn check_side(minimap: &Minimap, position: Kartesian, dir: KD) -> Option> { + match position.move_dir(dir.vector(), dir) { + None => None, + Some(new) => { + if minimap.len() == new.x || minimap[new.x].len() < new.y { + return Some(new); + } + None + }, + } +} +fn calc_sides_and_area(minimap: &Minimap) -> (u32, u32) { + print_minimap(&minimap); + let maximum: Kartesian = Kartesian::maximum(&minimap); + let area = minimap.iter().map(|l| l.iter().filter(|x| **x == true).count()).sum::() as u32; + let mut sides = 0; + let mut lines = Vec::with_capacity(minimap.len().max(minimap[0].len())); + let mut position = Kartesian::default(); + let mut innerdir; + let mut innerposition; + let mut emptys; + let mut map; + for dir in KD::iter(false, false, true) { + map = minimap.clone(); + innerdir = dir.clockwise(false); + loop { + emptys = 0; + innerposition = position; + loop { + if innerposition.get_value(&map) == Some(true) { + break; + } + emptys += 1; + innerposition = match innerposition.move_dir_max(innerdir.vector(), innerdir, maximum) { + None => break, + Some(new) => new, + } + } + lines.push(emptys); + if let Some(newpos) = position.move_dir_max(dir.vector(), dir, maximum) { + position = newpos; + } else { + break; + } + } + let mut iter = lines.iter().cloned(); + let mut last = iter.next().unwrap(); + for i in iter { + if last != i { + sides += 1; + } + last = i; + } + sides += 1; + lines.clear(); + } + println!("Prossesing done, found {} sides with and area of {}", sides, area); + (area, sides) +} + +fn print_minimap(map: &Minimap) { + for row in map { + for column in row { + print!( + "{}", + match column { + false => ".", + true => "X", + } + ) + } + println!() + } +} + +#[inline] +fn move_content_left(minimap: &mut Minimap, columns: usize) { + for line in minimap.iter_mut() { + for _ in 0..columns { + if line.len() == 0 { + line.push(false); + } else { + line.insert(0, false); + } + } + } +} + +fn ensure_size(minimap: &mut Minimap, x: usize, y: usize) { + if minimap.len() <= x + 1 { + let mut minimal_v = Vec::new(); + minimal_v.resize(y + 1, false); + minimap.resize(x + 1, minimal_v); + } + if minimap[x].len() <= y + 1 { + minimap[x].resize(y + 1, false); + } +} + +fn find_connected_i(position: Kartesian, minimap: &mut Minimap, visited: &mut Vec>, max: Kartesian, map: &Table, plant: char, startpos: &mut Kartesian) { + for direction in KD::iter(false, false, true) { + match position.move_dir_max(direction.vector(), direction, max) { + None => continue, + Some(new) => { + if map[new.x as usize][new.y as usize] != plant { + continue; + } + if !visited.contains(&new) { + visited.push(new); + } + let (mut diff, dir) = startpos.diff(new); + let mut movedir = KD::None; + match dir { + KD::Left | KD::BottomLeft => { + debug!("Moving {} from {}, because {} is {}", dir, startpos, new, dir); + *startpos = startpos.move_dir(KD::Left.vector(), KD::Left).unwrap(); + move_content_left(minimap, diff.y as usize); + (diff, _) = startpos.diff(new); + }, + KD::TopLeft => { + debug!("Moving {} from {}, because {} is {}", dir, startpos, new, dir); + *startpos = startpos.move_dir(KD::TopLeft.vector(), KD::TopLeft).unwrap(); + minimap.insert(0, Vec::new()); + move_content_left(minimap, diff.y as usize); + (diff, _) = startpos.diff(new); + }, + KD::Top => { + debug!("Moving {} from {}, because {} is {}", dir, startpos, new, dir); + *startpos = startpos.move_dir(KD::Top.vector(), KD::Top).unwrap(); + minimap.insert(0, Vec::new()); + (diff, _) = startpos.diff(new); + }, + _ => {}, + } + ensure_size(minimap, diff.x as usize, diff.y as usize); + if minimap[diff.x as usize][diff.y as usize] { + continue; + } + minimap[diff.x as usize][diff.y as usize] = true; + find_connected_i(new, minimap, visited, max, map, plant, startpos); + }, + } + } +} + +fn find_connected (u32, u32) + Copy>(position: Kartesian, map: &Table, visited: &mut Vec>, func: F) -> u32 { + if visited.contains(&position) { + return 0; + } + let max = Kartesian::maximum(map); + let plant = map[position.x as usize][position.y as usize]; + let mut fences = 2; + let mut newposition = position; + let mut startposition = position; + let mut minimap = vec![vec![true]]; + find_connected_i(position, &mut minimap, visited, max, map, plant, &mut startposition); + let max_length = minimap.iter().map(|line| line.len()).max().unwrap_or(0); + for line in minimap.iter_mut() { + if line.len() < max_length { + line.resize(max_length, false); + } + } + let price = mul(func(&minimap)); + debug!("price for region {}: {}", plant, price); + price +} + +fn calculate_fence_price (u32, u32) + Copy>(map: Table, func: F) -> u32 { + let mut visited = Vec::with_capacity(map.len() * map[0].len()); + let mut fences = 0; + let mut position = Kartesian::new(0, 0); + for (x, row) in map.iter().enumerate() { + for (y, column) in row.iter().enumerate() { + position = Kartesian::new(x as u32, y as u32); + if !visited.contains(&position) { + fences += find_connected(position, &map, &mut visited, func); + } + } + } + fences +} +fn main() { + let map: Table = convert_to_array::<_, _, '\n'>(DATA, |l| l.chars().collect()); + println!("The Price for the fences is {}", calculate_fence_price(map.clone(), calc_perimeter_and_area)); + println!("The Price with bulk discount is {}", calculate_fence_price(map, calc_sides_and_area)); +} + +#[cfg(test)] +mod test { + use super::*; + include_example!(MAP_A 2024 12a); + include_example!(MAP_B 2024 12b); + include_example!(MAP_C 2024 12c); + include_example!(MAP_D 2024 12d); + include_example!(MAP_E 2024 12f); + + #[test] + fn test_fence_a_perimeter() { + assert_eq!(calculate_fence_price(convert_to_array::<_, _, '\n'>(MAP_A, |l| l.chars().collect()), calc_perimeter_and_area), 140); + } + + #[test] + fn test_fence_b_perimeter() { + assert_eq!(calculate_fence_price(convert_to_array::<_, _, '\n'>(MAP_B, |l| l.chars().collect()), calc_perimeter_and_area), 772); + } + + #[test] + fn test_fence_c_perimeter() { + assert_eq!(calculate_fence_price(convert_to_array::<_, _, '\n'>(MAP_C, |l| l.chars().collect()), calc_perimeter_and_area), 1930); + } + + #[test] + fn test_fence_a_sides() { + assert_eq!(calculate_fence_price(convert_to_array::<_, _, '\n'>(MAP_A, |l| l.chars().collect()), calc_sides_and_area), 80); + } + + #[test] + fn test_fence_d_sides() { + assert_eq!(calculate_fence_price(convert_to_array::<_, _, '\n'>(MAP_D, |l| l.chars().collect()), calc_sides_and_area), 236); + } + + #[test] + fn test_fence_e_sides() { + assert_eq!(calculate_fence_price(convert_to_array::<_, _, '\n'>(MAP_E, |l| l.chars().collect()), calc_sides_and_area), 368); + } +}