From 4a2a3dfbd2ace1e00a64ec1234dd462f93e7ed1a Mon Sep 17 00:00:00 2001 From: Ian Liu Date: Sat, 14 Dec 2024 01:15:44 -0800 Subject: [PATCH] add: day 14 --- data/examples/14.txt | 12 ++++ src/bin/12.rs | 43 ++++++------ src/bin/13.rs | 1 - src/bin/14.rs | 161 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+), 23 deletions(-) create mode 100644 data/examples/14.txt create mode 100644 src/bin/14.rs diff --git a/data/examples/14.txt b/data/examples/14.txt new file mode 100644 index 0000000..72a324a --- /dev/null +++ b/data/examples/14.txt @@ -0,0 +1,12 @@ +p=0,4 v=3,-3 +p=6,3 v=-1,-3 +p=10,3 v=-1,2 +p=2,0 v=2,-1 +p=0,0 v=1,3 +p=3,0 v=-2,-2 +p=7,6 v=-1,-3 +p=3,0 v=-1,-2 +p=9,3 v=2,3 +p=7,3 v=-1,2 +p=2,4 v=2,-3 +p=9,5 v=-3,-3 \ No newline at end of file diff --git a/src/bin/12.rs b/src/bin/12.rs index 660c851..dfb0502 100644 --- a/src/bin/12.rs +++ b/src/bin/12.rs @@ -13,7 +13,7 @@ pub fn part_one(input: &str) -> Option { Some(get_fence_cost( &mut grid, &bounds, - compute_cost_and_update_grid, + compute_cost, )) } @@ -24,7 +24,7 @@ pub fn part_two(input: &str) -> Option { Some(get_fence_cost( &mut grid, &bounds, - compute_cost_and_update_grid2, + compute_cost2, )) } @@ -35,12 +35,12 @@ fn convert_input_to_grid(input: &str) -> Vec> { fn get_fence_cost( grid: &mut Vec>, bounds: &(usize, usize), - compute_cost_and_update_grid: F, + compute_cost: F, ) -> u32 where - F: Fn(&mut Vec>, &(usize, usize), HashSet<(usize, usize)>) -> u32, + F: Fn(&mut Vec>, &(usize, usize), &HashSet<(usize, usize)>) -> u32, { - let mut cost = 0; + let mut total_cost = 0; for pos in (0..bounds.0).cartesian_product(0..bounds.1) { if grid[pos.0][pos.1] == ' ' { @@ -50,10 +50,11 @@ where let mut visited: HashSet<(usize, usize)> = HashSet::new(); dfs(&pos, grid, bounds, &mut visited); - cost += compute_cost_and_update_grid(grid, bounds, visited); + total_cost += compute_cost(grid, bounds, &visited); + mark_visited_on_grid(grid, visited); } - cost + total_cost } fn dfs( @@ -62,7 +63,7 @@ fn dfs( bounds: &(usize, usize), visited: &mut HashSet<(usize, usize)>, ) { - if visited.contains(&pos) { + if visited.contains(pos) { return; } visited.insert(*pos); @@ -77,14 +78,20 @@ fn dfs( } } -fn compute_cost_and_update_grid( +fn mark_visited_on_grid(grid: &mut [Vec], visited: HashSet<(usize, usize)>) { + for pos in &visited { + grid[pos.0][pos.1] = ' '; + } +} + +fn compute_cost( grid: &mut Vec>, bounds: &(usize, usize), - visited: HashSet<(usize, usize)>, + visited: &HashSet<(usize, usize)>, ) -> u32 { let mut perimeter = 0; - for pos in &visited { + for pos in visited { for delta in DIRECTIONS { if let Some(next_pos) = is_valid_pos(pos, bounds, &delta) { if grid[next_pos.0][next_pos.1] != grid[pos.0][pos.1] { @@ -96,20 +103,16 @@ fn compute_cost_and_update_grid( } } - for pos in &visited { - grid[pos.0][pos.1] = ' '; - } - visited.len() as u32 * perimeter } -fn compute_cost_and_update_grid2( +fn compute_cost2( grid: &mut Vec>, bounds: &(usize, usize), - visited: HashSet<(usize, usize)>, + visited: &HashSet<(usize, usize)>, ) -> u32 { let mut borders: HashMap<(isize, isize), Vec<(usize, usize)>> = HashMap::new(); - for pos in &visited { + for pos in visited { for delta in DIRECTIONS { if let Some(next_pos) = is_valid_pos(pos, bounds, &delta) { if grid[next_pos.0][next_pos.1] != grid[pos.0][pos.1] { @@ -146,10 +149,6 @@ fn compute_cost_and_update_grid2( } } - for pos in &visited { - grid[pos.0][pos.1] = ' '; - } - sides * visited.len() as u32 } diff --git a/src/bin/13.rs b/src/bin/13.rs index af2a676..7ff7e8b 100644 --- a/src/bin/13.rs +++ b/src/bin/13.rs @@ -91,6 +91,5 @@ mod tests { #[test] fn test_part_two() { let result = part_two(&advent_of_code::template::read_file("examples", DAY)); - assert_eq!(result, None); } } diff --git a/src/bin/14.rs b/src/bin/14.rs new file mode 100644 index 0000000..58eab4d --- /dev/null +++ b/src/bin/14.rs @@ -0,0 +1,161 @@ +use std::collections::HashMap; + +use regex::Regex; + +advent_of_code::solution!(14); + +#[derive(Debug)] +struct Robot { + i: usize, + j: usize, + di: isize, + dj: isize, +} + +struct Grid { + m: usize, + n: usize, + robots: Vec, + mid_m: usize, + mid_n: usize, +} + +impl Grid { + fn new(m: usize, n: usize, robots: Vec) -> Self { + Grid { + m, + n, + robots, + mid_m: m / 2, + mid_n: n / 2, + } + } + + fn move_robots(&mut self) { + for robot in &mut self.robots { + robot.i = ((robot.i as isize + robot.di).rem_euclid(self.m as isize)) as usize; + robot.j = ((robot.j as isize + robot.dj).rem_euclid(self.n as isize)) as usize; + } + } + + fn find_safety_factor(&self) -> u32 { + let mut quadrant: HashMap<(bool, bool), u32> = HashMap::new(); + + for robot in &self.robots { + if robot.i == self.mid_m || robot.j == self.mid_n { + continue; + } + *quadrant + .entry((robot.i > self.mid_m, robot.j > self.mid_n)) + .or_default() += 1; + } + + quadrant.values().product() + } + + fn has_a_tree(&self) -> bool { + let mut rows: HashMap> = HashMap::new(); + let mut cols: HashMap> = HashMap::new(); + + for robot in &self.robots { + rows.entry(robot.i).or_default().push(robot.j); + cols.entry(robot.j).or_default().push(robot.i); + } + + let mut crowded_rows = 0; + for row in rows.values() { + if row.len() >= 30 { + crowded_rows += 1; + } + } + let mut crowded_cols = 0; + for col in cols.values() { + if col.len() >= 30 { + crowded_cols += 1; + } + } + + crowded_rows >= 2 && crowded_cols >= 2 // has a frame + } + + fn draw_grid(&self) { + let mut canvas = vec![vec!['.'; self.n]; self.m]; + for robot in &self.robots { + canvas[robot.i][robot.j] = '󱚣'; + } + for row in canvas { + println!("{}", row.iter().collect::()); + } + } + + #[allow(dead_code)] + fn dbg_robots(&self) { + for robot in &self.robots { + dbg!(robot); + } + } +} + +pub fn part_one(input: &str) -> Option { + let robots = parse_input(input); + let mut grid = Grid::new(103, 101, robots); + + for _ in 0..100 { + grid.move_robots(); + } + + Some(grid.find_safety_factor()) +} + +pub fn part_two(input: &str) -> Option { + let robots = parse_input(input); + let mut grid = Grid::new(103, 101, robots); + + for i in 1..=10000 { + grid.move_robots(); + if grid.has_a_tree() { + grid.draw_grid(); + return Some(i as u32); + } + } + + None +} + +fn parse_input(input: &str) -> Vec { + let line_re = Regex::new(r"p\=(.*),(.*)\sv\=(.*),(.*)").unwrap(); + let mut parsed_input = vec![]; + + input.lines().for_each(|line| { + let cap = line_re.captures(line).unwrap(); + parsed_input.push(Robot { + i: cap[2].parse().unwrap(), + j: cap[1].parse().unwrap(), + di: cap[4].parse().unwrap(), + dj: cap[3].parse().unwrap(), + }) + }); + + parsed_input +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_part_one() { + let robots = parse_input(&advent_of_code::template::read_file("examples", DAY)); + let mut grid = Grid::new(7, 11, robots); + for _ in 0..100 { + grid.move_robots(); + grid.draw_grid(); + } + assert_eq!(grid.find_safety_factor(), 12); + } + + #[test] + fn test_part_two() { + let result = part_two(&advent_of_code::template::read_file("examples", DAY)); + } +}