|
| 1 | +use std::str::FromStr; |
| 2 | + |
| 3 | +use aoc_derive::aoc_main; |
| 4 | +use itertools::Itertools; |
| 5 | +use lazy_regex::regex_is_match; |
| 6 | +use math::Vec2D; |
| 7 | +use utils::*; |
| 8 | + |
| 9 | +#[derive(Debug, Clone)] |
| 10 | +struct Robot { |
| 11 | + pos: Vec2D, |
| 12 | + velocity: Vec2D, |
| 13 | +} |
| 14 | + |
| 15 | +impl Robot { |
| 16 | + fn move_(mut self, width: i64, height: i64) -> Self { |
| 17 | + self.pos += self.velocity + (width, height); |
| 18 | + |
| 19 | + self.pos.x %= width; |
| 20 | + self.pos.y %= height; |
| 21 | + |
| 22 | + self |
| 23 | + } |
| 24 | +} |
| 25 | + |
| 26 | +impl FromStr for Robot { |
| 27 | + type Err = (); |
| 28 | + |
| 29 | + fn from_str(s: &str) -> Result<Self, Self::Err> { |
| 30 | + let (x, y, vx, vy) = extract_numbers::<i64>(s).collect_tuple().unwrap(); |
| 31 | + Ok(Self { pos: (x, y).into(), velocity: (vx, vy).into() }) |
| 32 | + } |
| 33 | +} |
| 34 | + |
| 35 | +fn count_robots(robots: &[Robot], width: i64, height: i64) -> usize { |
| 36 | + robots.iter().filter(|r| r.pos.x < width / 2 && r.pos.y < height / 2).count() |
| 37 | + * robots.iter().filter(|r| r.pos.x < width / 2 && r.pos.y > height / 2).count() |
| 38 | + * robots.iter().filter(|r| r.pos.x > width / 2 && r.pos.y < height / 2).count() |
| 39 | + * robots.iter().filter(|r| r.pos.x > width / 2 && r.pos.y > height / 2).count() |
| 40 | +} |
| 41 | + |
| 42 | +fn part1(robots: impl Iterator<Item = Robot>, width: i64, height: i64) -> usize { |
| 43 | + let robots = robots |
| 44 | + .into_iter() |
| 45 | + .map(|robot| (0..100).fold(robot, |robot, _| robot.move_(width, height))) |
| 46 | + .collect_vec(); |
| 47 | + count_robots(&robots, width, height) |
| 48 | +} |
| 49 | + |
| 50 | +fn part2(mut robots: Vec<Robot>, width: usize, height: usize) -> usize { |
| 51 | + (1..) |
| 52 | + .find(|_| { |
| 53 | + robots = |
| 54 | + robots.clone().into_iter().map(|r| r.move_(width as i64, height as i64)).collect(); |
| 55 | + |
| 56 | + let lines = (0..height) |
| 57 | + .map(|y| { |
| 58 | + (0..width) |
| 59 | + .map(|x| if robots.iter().any(|r| r.pos == (x, y)) { 'X' } else { ' ' }) |
| 60 | + .collect::<String>() |
| 61 | + }) |
| 62 | + .collect_vec(); |
| 63 | + |
| 64 | + if lines.iter().any(|line| regex_is_match!(r"X{10,}", line)) { |
| 65 | + for line in lines { |
| 66 | + println!("{line}"); |
| 67 | + } |
| 68 | + println!(); |
| 69 | + true |
| 70 | + } else { |
| 71 | + false |
| 72 | + } |
| 73 | + }) |
| 74 | + .unwrap() |
| 75 | +} |
| 76 | + |
| 77 | +#[aoc_main] |
| 78 | +fn solve(input: Input) -> impl Into<Solution> { |
| 79 | + let robots = input.parse_lines::<Robot>(); |
| 80 | + |
| 81 | + (part1(robots.clone(), 101, 103), part2(robots.clone().collect(), 101, 103)) |
| 82 | +} |
| 83 | + |
| 84 | +#[cfg(test)] |
| 85 | +mod tests { |
| 86 | + use super::*; |
| 87 | + #[test] |
| 88 | + fn test_examples() { |
| 89 | + let example = " |
| 90 | +p=0,4 v=3,-3 |
| 91 | +p=6,3 v=-1,-3 |
| 92 | +p=10,3 v=-1,2 |
| 93 | +p=2,0 v=2,-1 |
| 94 | +p=0,0 v=1,3 |
| 95 | +p=3,0 v=-2,-2 |
| 96 | +p=7,6 v=-1,-3 |
| 97 | +p=3,0 v=-1,-2 |
| 98 | +p=9,3 v=2,3 |
| 99 | +p=7,3 v=-1,2 |
| 100 | +p=2,4 v=2,-3 |
| 101 | +p=9,5 v=-3,-3 |
| 102 | + "; |
| 103 | + assert_eq!(part1(Input::from(example).parse_lines(), 11, 7), 12); |
| 104 | + } |
| 105 | +} |
0 commit comments