use std::io; use std::io::BufRead; use std::collections::HashMap; #[derive(Clone, PartialEq, Eq, Hash)] struct Pos { x: usize, y: usize } #[derive(Clone)] struct Dir { x: i32, y: i32 } impl Dir { fn rotright(&self) -> Dir { Dir { x: -self.y, y: self.x } } fn rotleft(&self) -> Dir { Dir { x: self.y, y: -self.x } } } enum Choice { Left, Forward, Right, } impl Choice { fn incr(&mut self) { match self { Choice::Left => *self = Choice::Forward, Choice::Forward => *self = Choice::Right, Choice::Right => *self = Choice::Left, } } fn apply(&self, dir: &Dir) -> Dir { match self { Choice::Left => dir.rotleft(), Choice::Forward => dir.clone(), Choice::Right => dir.rotright(), } } } struct Cart { pos: Pos, dir: Dir, choice: Choice, } impl Cart { fn nextpos(&self) -> Pos { Pos { x: (self.pos.x as i32 + self.dir.x) as usize, y: (self.pos.y as i32 + self.dir.y) as usize } } } fn newdir(dir: &Dir, c: u8) -> Dir { // println!(" newdir {},{} {}", dir.x, dir.y, std::char::from_u32(c as u32).unwrap()); if c == '|' as u8 || c == '-' as u8 { Dir { x: dir.x, y: dir.y } } else if c == '/' as u8 { if dir.y == 0 { dir.rotleft() } else { dir.rotright() } } else if c == '\\' as u8 { if dir.y == 0 { dir.rotright() } else { dir.rotleft() } } else { println!("<{}>", c); unreachable!(); } } #[allow(dead_code)] fn print_dir(dir: &Dir) -> char { match dir { Dir{x:1,y:0} => '>', Dir{x:-1,y:0} => '<', Dir{x:0,y:1} => 'v', Dir{x:0,y:-1} => '^', Dir{..} => '?', } } #[allow(dead_code)] fn print_grid(grid: &Vec>, carts: &Vec) { let mut hascart: HashMap = HashMap::new(); for (i, cart) in carts.iter().enumerate() { hascart.insert(cart.pos.clone(), i); } for y in 0..grid.len() { for x in 0..grid[y].len() { match hascart.get(&Pos { x, y }) { Some(&idx) => { print!("{}", print_dir(&carts[idx].dir)); } None => { print!("{}", std::char::from_u32(grid[y][x] as u32).unwrap()); } } } println!(""); } } pub fn main(reader: T) -> io::Result<(String, String)> { let mut grid: Vec> = reader.lines().map(|l| l.unwrap().as_bytes().to_vec()).collect(); let h = grid.len(); let w = grid[0].len(); let mut hascart = Vec::new(); for _ in 0..h { hascart.push(vec![false; w]); } let mut carts: Vec = Vec::new(); for y in 0..h { for x in 0..w { macro_rules! grid_process { ( $c1:expr, $c2:expr, $dx:expr, $dy:expr ) => { if grid[y][x] == $c1 as u8 { grid[y][x] = $c2 as u8; carts.push(Cart { pos: Pos { x, y }, dir: Dir { x: $dx, y: $dy }, choice: Choice::Left }); hascart[y][x] = true; } } } grid_process!('>', '-', 1, 0); grid_process!('<', '-', -1, 0); grid_process!('^', '|', 0, -1); grid_process!('v', '|', 0, 1); } } let mut first_crash = None; let part2; loop { let mut to_remove = Vec::new(); for (i, mut cart) in carts.iter_mut().enumerate() { if !hascart[cart.pos.y][cart.pos.x] { to_remove.push(i); continue; } let pos2 = cart.nextpos(); let c = grid[pos2.y][pos2.x]; // println!("cart at {},{} {},{}", cart.pos.x, cart.pos.y, cart.dir.x, cart.dir.y); let dir2; if c == '+' as u8 { dir2 = cart.choice.apply(&cart.dir); cart.choice.incr(); } else { dir2 = newdir(&cart.dir, c); } hascart[cart.pos.y][cart.pos.x] = false; if hascart[pos2.y][pos2.x] { if first_crash.is_none() { first_crash = Some(pos2.clone()); } to_remove.push(i); hascart[pos2.y][pos2.x] = false; } else { hascart[pos2.y][pos2.x] = true; cart.pos = pos2; cart.dir = dir2; } } for idx in to_remove { carts.swap_remove(idx); } if carts.len() == 1 { part2 = format!("{},{}", carts[0].pos.x, carts[0].pos.y); break; } carts.sort_by_key(|cart| (cart.pos.y, cart.pos.x)); // print_grid(&grid, &carts); } let part1 = match first_crash { Some(pos) => format!("{},{}", pos.x, pos.y), None => unreachable!() }; Ok((part1, part2)) }