use regex::Regex; use std::collections::{HashMap, HashSet}; static DAY: u8 = 14; fn main() { let input = advent::read_lines(DAY); println!("{DAY}a: {}", safety_factor(&input, Position { x: 101, y: 103 })); println!("{DAY}b: {}", find_tree(&input, Position { x: 101, y: 103 })); } #[derive(Eq, PartialEq, Hash, Clone, Copy)] struct Position { x: isize, y: isize, } struct Robot { pos: Position, velocity: Position, } impl Robot { fn new(input: &str) -> Robot { let re = Regex::new(r"p=([0-9]+),([0-9]+) v=([-0-9]+),([-0-9]+)").unwrap(); let caps = re.captures(input).unwrap(); let x = caps.get(1).unwrap().as_str().parse::().unwrap(); let y = caps.get(2).unwrap().as_str().parse::().unwrap(); let dx = caps.get(3).unwrap().as_str().parse::().unwrap(); let dy = caps.get(4).unwrap().as_str().parse::().unwrap(); Robot { pos: Position { x, y }, velocity: Position { x: dx, y: dy }, } } fn travel(&mut self, area: &Position) { self.pos.x = (self.pos.x + self.velocity.x + area.x) % area.x; self.pos.y = (self.pos.y + self.velocity.y + area.y) % area.y; } fn quadrant(&self, area: &Position) -> u8 { if self.pos.x < area.x / 2 && self.pos.y < area.y / 2 { 1 } else if self.pos.x < area.x / 2 && self.pos.y > area.y / 2 { 2 } else if self.pos.x > area.x / 2 && self.pos.y < area.y / 2 { 3 } else if self.pos.x > area.x / 2 && self.pos.y > area.y / 2 { 4 } else { 0 } } } struct RobotMap { robots: Vec, area: Position, } impl RobotMap { fn new(input: &[String], area: Position) -> RobotMap { let robots = input.iter() .map(|line| Robot::new(line)) .collect::>(); RobotMap { robots, area } } fn tick(&mut self) { for robot in self.robots.iter_mut() { robot.travel(&self.area); } } fn safety_factor(&self) -> u32 { let mut quadrants = HashMap::new(); for robot in &self.robots { let quadrant = robot.quadrant(&self.area); if quadrant != 0 { let entry = quadrants.entry(quadrant).or_insert(0); *entry += 1; } } quadrants.values().product() } fn to_text(&self) -> String { let mut map = String::new(); let positions = self.robots.iter() .map(|robot| robot.pos) .collect::>(); for y in 0 .. self.area.y { for x in 0 .. self.area.x { if positions.contains(&Position { x, y }) { map += "*"; } else { map += "."; } } map += "\n"; } map } fn draw(&self) { println!("{}", self.to_text()); } } fn safety_factor(input: &[String], area: Position) -> u32 { let mut robotmap = RobotMap::new(input, area); for _ in 0 .. 100 { robotmap.tick(); } robotmap.safety_factor() } fn find_tree(input: &[String], area: Position) -> u32 { let mut robotmap = RobotMap::new(input, area); let mut i = 0; loop { robotmap.tick(); i += 1; /* output likely contains a lot of robots closely together */ if robotmap.to_text().contains("*********") { robotmap.draw(); return i; } } } #[cfg(test)] mod tests { use super::*; #[test] fn test() { let input = [ "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", ].iter().map(|&x| String::from(x)).collect::>(); assert_eq!(safety_factor(&input, Position { x: 11, y: 7}), 12); } }