#[path = "../lib.rs"] mod lib; #[derive(Debug, Copy, Clone, PartialEq)] enum Tile { Wall, Floor, Box, DoubleBoxLeft, DoubleBoxRight, } #[derive(Debug, PartialEq, Copy, Clone)] enum Dir { Up, Right, Down, Left, } impl Dir { fn apply(self: &Self, x: usize, y: usize) -> (usize, usize) { return match self { Self::Up => (x, y - 1), Self::Right => (x + 1, y), Self::Down => (x, y + 1), Self::Left => (x - 1, y), }; } } fn parse_input() -> (usize, usize, Vec>, Vec) { let mut x0 = 0; let mut y0 = 0; let mut map = vec![]; let mut mov = vec![]; let mut map_done = false; for (y, line) in lib::iter_input().enumerate() { if line == "" { map_done = true; } else if map_done { for b in line.bytes() { mov.push(match b { b'^' => Dir::Up, b'>' => Dir::Right, b'v' => Dir::Down, b'<' => Dir::Left, v => { println!("{}", v); unreachable!(); } // _ => unreachable!(), }); } } else { map.push( line.bytes() .enumerate() .map(|(x, b)| match b { b'#' => Tile::Wall, b'.' => Tile::Floor, b'O' => Tile::Box, b'@' => { x0 = x; y0 = y; Tile::Floor } _ => unreachable!(), }) .collect(), ); } } return (x0, y0, map, mov); } fn stretch(x: usize, y: usize, map1: &Vec>) -> (usize, usize, Vec>) { let map2 = map1 .iter() .map(|row1| { let mut row2 = vec![]; for tile in row1.iter() { match tile { Tile::Box => { row2.push(Tile::DoubleBoxLeft); row2.push(Tile::DoubleBoxRight); } _ => { row2.push(*tile); row2.push(*tile); } }; } return row2; }) .collect(); return (x * 2, y, map2); } fn can_move(x: usize, y: usize, dir: &Dir, map: &mut Vec>) -> bool { // only used for up/down on double boxes // PERF: I expected that this explodes because of the recursion. // // For example, if two double boxes are directly on top of each other, // everything is checked twice (for each side). // // However, in practice this doesn't seem to be an issue. The tweaks I tried // had no noticable impact. Probably the chains are too short. let (x2, y2) = dir.apply(x, y); return match map[y2][x2] { Tile::Wall => false, Tile::Floor => true, Tile::Box => unreachable!(), Tile::DoubleBoxLeft => can_move(x2 + 1, y2, dir, map) && can_move(x2, y2, dir, map), Tile::DoubleBoxRight => can_move(x2 - 1, y2, dir, map) && can_move(x2, y2, dir, map), }; } fn do_move_double(x: usize, y: usize, dir: &Dir, map: &mut Vec>) { let (_, x2, y2) = do_move(x, y, dir, map); match dir { Dir::Up | Dir::Down => { do_move(x + 1, y, dir, map); } _ => {} } // important: if moving left/right, it is important to place the // box parts at the end so they do not get replaced by floors. map[y][x] = Tile::Floor; map[y][x + 1] = Tile::Floor; map[y2][x2] = Tile::DoubleBoxLeft; map[y2][x2 + 1] = Tile::DoubleBoxRight; } fn do_move(x: usize, y: usize, dir: &Dir, map: &mut Vec>) -> (bool, usize, usize) { let (x2, y2) = dir.apply(x, y); return match (map[y2][x2], dir) { (Tile::Wall, _) => (false, x, y), (Tile::Floor, _) => (true, x2, y2), (Tile::Box, _) | (Tile::DoubleBoxLeft | Tile::DoubleBoxRight, Dir::Left | Dir::Right) => { let (free, x3, y3) = do_move(x2, y2, dir, map); if free { map[y3][x3] = map[y2][x2]; map[y2][x2] = Tile::Floor; return (true, x2, y2); } else { return (false, x, y); } } (Tile::DoubleBoxLeft, Dir::Up | Dir::Down) => { if can_move(x, y, dir, map) { do_move_double(x2, y2, dir, map); return (true, x2, y2); } else { return (false, x, y); } } (Tile::DoubleBoxRight, Dir::Up | Dir::Down) => { if can_move(x, y, dir, map) { do_move_double(x2 - 1, y2, dir, map); return (true, x2, y2); } else { return (false, x, y); } } }; } fn score(map: &Vec>) -> usize { let mut score = 0; for (y, row) in map.iter().enumerate() { for (x, tile) in row.iter().enumerate() { match tile { Tile::Box | Tile::DoubleBoxLeft => { score += x + 100 * y; } _ => {} } } } return score; } fn print(px: usize, py: usize, map: &Vec>) { for (y, row) in map.iter().enumerate() { for (x, tile) in row.iter().enumerate() { match tile { Tile::Wall => print!("#"), Tile::Floor => print!("{}", if (x, y) == (px, py) { "@" } else { "." }), Tile::Box => print!("O"), Tile::DoubleBoxLeft => print!("["), Tile::DoubleBoxRight => print!("]"), } } println!(""); } println!(""); } fn main() { let (mut x1, mut y1, mut map1, mov) = parse_input(); let (mut x2, mut y2, mut map2) = stretch(x1, y1, &map1); for dir in mov.iter() { (_, x1, y1) = do_move(x1, y1, dir, &mut map1); (_, x2, y2) = do_move(x2, y2, dir, &mut map2); } print(x1, y1, &map1); print(x2, y2, &map2); println!("part1: {}", score(&map1)); println!("part2: {}", score(&map2)); }