#[path = "../lib.rs"] mod lib; #[derive(PartialEq)] enum Tile { Free, Wall, Out, } fn get_input() -> (Vec>, Vec<(usize, bool)>) { let mut map = vec![]; let mut path = vec![]; let mut map_done = false; for line in lib::iter_input() { if line == "" { map_done = true; } else if !map_done { map.push(line.chars().map(|c| match c { '.' => Tile::Free, '#' => Tile::Wall, ' ' => Tile::Out, _ => unreachable!(), }).collect()); } else { let mut s = String::new(); for c in line.chars() { if c == 'R' || c == 'L' { path.push((s.parse::().unwrap(), c == 'R')); s = String::new(); } else { s += &c.to_string(); } } path.push((s.parse::().unwrap(), true)); path.push((0, false)); } } return (map, path); } fn part1(map: &Vec>, path: &Vec<(usize, bool)>) -> usize { let height = map.len(); let mut y = 0; let mut x = 0; let mut dir = 0; while map[y][x] != Tile::Free { x += 1; } for (steps, turn) in path.iter() { for _ in 0..*steps { let mut x2 = x; let mut y2 = y; match dir { 0 => { let width = map[y2].len(); x2 = (x2 + 1) % width; while map[y2][x2] == Tile::Out { x2 = (x2 + 1) % width; } }, 1 => { y2 = (y2 + 1) % height; while map[y2].len() <= x || map[y2][x2] == Tile::Out { y2 = (y2 + 1) % height; } }, 2 => { let width = map[y2].len(); x2 = (x2 + width - 1) % width; while map[y2][x2] == Tile::Out { x2 = (x2 + width - 1) % width; } }, 3 => { y2 = (y2 + height - 1) % height; while map[y2].len() <= x || map[y2][x2] == Tile::Out { y2 = (y2 + height - 1) % height; } }, _ => unreachable!(), } match map[y2][x2] { Tile::Free => { x = x2; y = y2; }, Tile::Wall => { break; }, Tile::Out => unreachable!(), } } if *turn { dir = (dir + 1) % 4; } else { dir = (dir + 3) % 4; } } return 1000 * (y + 1) + 4 * (x + 1) + dir; } fn wrap2(x: usize, y: usize, dir: usize) -> (usize, usize, usize) { // hardcoded cube topology let k = 50; let dx = x % k; let dy = y % k; return match (x / k, y / k, dir) { // 1 -> 2 (1, 0, 0) => ( x + 1, y, dir ), // 1 -> 3 (1, 0, 1) => ( x, y + 1, dir ), // 1 -> 4 (1, 0, 2) => ( 0 * k, 2 * k + (k - 1) - dy, 0, ), // 1 -> 6 (1, 0, 3) => ( 0 * k, 3 * k + dx, 0, ), // 2 -> 5 (2, 0, 0) => ( 1 * k + (k - 1), 2 * k + (k - 1) - dy, 2, ), // 2 -> 3 (2, 0, 1) => ( 1 * k + (k - 1), 1 * k + dx, 2, ), // 2 -> 1 (2, 0, 2) => ( x - 1, y, dir ), // 2 -> 6 (2, 0, 3) => ( 0 * k + dx, 3 * k + (k - 1), 3, ), // 3 -> 2 (1, 1, 0) => ( 2 * k + dy, 0 * k + (k - 1), 3, ), // 3 -> 5 (1, 1, 1) => ( x, y + 1, dir ), // 3 -> 4 (1, 1, 2) => ( 0 * k + dy, 2 * k, 1, ), // 3 -> 1 (1, 1, 3) => ( x, y - 1, dir, ), // 4 -> 5 (0, 2, 0) => ( x + 1, y, dir ), // 4 -> 6 (0, 2, 1) => ( x, y + 1, dir ), // 4 -> 1 (0, 2, 2) => ( 1 * k, 0 * k + (k - 1) - dy, 0, ), // 4 -> 3 (0, 2, 3) => ( 1 * k, 1 * k + dx, 0, ), // 5 -> 2 (1, 2, 0) => ( 2 * k + (k - 1), 0 * k + (k - 1) - dy, 2, ), // 5 -> 6 (1, 2, 1) => ( 0 * k + (k - 1), 3 * k + dx, 2, ), // 5 -> 4 (1, 2, 2) => ( x - 1, y, dir ), // 5 -> 3 (1, 2, 3) => ( x, y - 1, dir ), // 6 -> 5 (0, 3, 0) => ( 1 * k + dy, 2 * k + (k - 1), 3, ), // 6 -> 2 (0, 3, 1) => ( 2 * k + dx, 0 * k, 1, ), // 6 -> 1 (0, 3, 2) => ( 1 * k + dy, 0 * k, 1, ), // 6 -> 4 (0, 3, 3) => ( x, y - 1, dir ), _ => unreachable!(), }; } fn part2(map: &Vec>, path: &Vec<(usize, bool)>) -> usize { let mut y = 0; let mut x = 0; let mut dir = 0; while map[y][x] != Tile::Free { x += 1; } for (steps, turn) in path.iter() { for _ in 0..*steps { let (x2, y2, dir2) = match dir { 0 => if (x + 1) % 50 == 0 { wrap2(x, y, dir) } else { (x + 1, y, dir) }, 1 => if (y + 1) % 50 == 0 { wrap2(x, y, dir) } else { (x, y + 1, dir) }, 2 => if x % 50 == 0 { wrap2(x, y, dir) } else { (x - 1, y, dir) }, 3 => if y % 50 == 0 { wrap2(x, y, dir) } else { (x, y - 1, dir) }, _ => unreachable!(), }; match map[y2][x2] { Tile::Free => { x = x2; y = y2; dir = dir2; }, Tile::Wall => { break; }, Tile::Out => unreachable!(), } } if *turn { dir = (dir + 1) % 4; } else { dir = (dir + 3) % 4; } } return 1000 * (y + 1) + 4 * (x + 1) + dir; } fn test_wrap(x: usize, y: usize, dir: usize) { let (x2, y2, dir2) = wrap2(x, y, dir); let (x3, y3, dir3) = wrap2(x2, y2, (dir2 + 2) % 4); let dir4 = (dir3 + 2) % 4; assert_eq!((x, y, dir), (x3, y3, dir4)); } fn main() { let (map, path) = get_input(); for (y, row) in map.iter().enumerate() { for (x, tile) in row.iter().enumerate() { if *tile != Tile::Out { if x % 50 == 0 { test_wrap(x, y, 2); } if (x + 1) % 50 == 0 { test_wrap(x, y, 0); } if y % 50 == 0 { test_wrap(x, y, 3); } if (y + 1) % 50 == 0 { test_wrap(x, y, 1); } } } } println!("part1: {}", part1(&map, &path)); println!("part2: {}", part2(&map, &path)); }