main.rs (5042B)
1 use std::collections::{HashMap, HashSet}; 2 3 #[derive(Debug, Clone, Copy)] 4 enum Tile { 5 Empty, 6 Wall, 7 } 8 9 #[derive(PartialEq, Eq, Hash, Debug, Clone, Copy)] 10 enum Dir { 11 North, 12 East, 13 South, 14 West, 15 } 16 17 impl Dir { 18 fn left(&self) -> Self { 19 match self { 20 Self::North => Self::West, 21 Self::West => Self::South, 22 Self::South => Self::East, 23 Self::East => Self::North, 24 } 25 } 26 27 fn right(&self) -> Self { 28 match self { 29 Self::North => Self::East, 30 Self::East => Self::South, 31 Self::South => Self::West, 32 Self::West => Self::North, 33 } 34 } 35 36 fn opposite(&self) -> Self { 37 self.right().right() 38 } 39 40 fn go(&self, (x, y): &(i32, i32)) -> (i32, i32) { 41 let (dx, dy) = match self { 42 Self::North => (-1, 0), 43 Self::East => (0, 1), 44 Self::South => (1, 0), 45 Self::West => (0, -1), 46 }; 47 (x + dx, y + dy) 48 } 49 50 fn val(&self) -> i32 { 51 match self { 52 Self::North => 3, 53 Self::East => 0, 54 Self::South => 1, 55 Self::West => 2, 56 } 57 } 58 } 59 60 #[derive(Debug, Clone, Copy)] 61 enum Step { 62 Move(i32), 63 TurnR, 64 TurnL, 65 } 66 67 impl Step { 68 fn parse_path(s: &str) -> Vec<Step> { 69 let mut path = Vec::new(); 70 let mut buf = String::new(); 71 for c in s.trim().chars() { 72 match c { 73 'L' => { 74 if buf.len() > 0 { 75 path.push(Self::Move(buf.parse::<i32>().unwrap())); 76 buf.clear(); 77 } 78 path.push(Self::TurnL); 79 } 80 'R' => { 81 if buf.len() > 0 { 82 path.push(Self::Move(buf.parse::<i32>().unwrap())); 83 buf.clear(); 84 } 85 path.push(Self::TurnR); 86 } 87 c => { 88 buf.push(c); 89 } 90 } 91 } 92 if buf.len() > 0 { 93 path.push(Self::Move(buf.parse::<i32>().unwrap())); 94 buf.clear(); 95 } 96 path 97 } 98 } 99 100 fn main() { 101 let (map, path) = { 102 let tmp = std::fs::read_to_string("input.txt") 103 .unwrap() 104 .split("\n\n") 105 .map(|s| s.to_string()) 106 .collect::<Vec<String>>(); 107 let map_lines = tmp[0] 108 .split("\n") 109 .map(|s| s.to_string()) 110 .collect::<Vec<String>>(); 111 let mut map = HashMap::new(); 112 for (i, s) in map_lines.iter().enumerate() { 113 for (j, c) in s.chars().enumerate() { 114 match c { 115 '#' => { 116 map.insert((i as i32 + 1, j as i32 + 1), Tile::Wall); 117 } 118 '.' => { 119 map.insert((i as i32 + 1, j as i32 + 1), Tile::Empty); 120 } 121 _ => (), 122 } 123 } 124 } 125 (map, Step::parse_path(&tmp[1])) 126 }; 127 // 55244 128 println!("Part 1: {}", part_1(&map, &path)); 129 // 130 // println!("Part 2: {}", part_2(&map, &path)); 131 } 132 133 fn warp(map: &HashMap<(i32, i32), Tile>, pos: &(i32, i32), dir: Dir) -> (i32, i32) { 134 let mut prev = *pos; 135 let mut next = *pos; 136 let back_dir = dir.opposite(); 137 while map.contains_key(&next) { 138 prev = next; 139 next = back_dir.go(&prev); 140 } 141 prev 142 } 143 144 fn part_1(map: &HashMap<(i32, i32), Tile>, path: &Vec<Step>) -> i32 { 145 let start_x = *map.keys().map(|(x, _)| x).min().unwrap(); 146 let start_y = *map 147 .keys() 148 .filter_map(|(x, y)| if *x == start_x { Some(y) } else { None }) 149 .min() 150 .unwrap(); 151 let mut warp_dict = HashMap::new(); 152 let mut pos = (start_x, start_y); 153 let mut dir = Dir::East; 154 for step in path { 155 match step { 156 Step::Move(dist) => { 157 for _ in 0..*dist { 158 let mut next = dir.go(&pos); 159 if !map.contains_key(&next) { 160 if let Some(w) = warp_dict.get(&(pos, dir)) { 161 next = *w; 162 } else { 163 next = warp(map, &pos, dir); 164 warp_dict.insert((pos, dir), next); 165 warp_dict.insert((next, dir.opposite()), pos); 166 } 167 } 168 match map.get(&next).unwrap() { 169 Tile::Empty => { 170 pos = next; 171 } 172 Tile::Wall => { 173 break; 174 } 175 } 176 } 177 } 178 Step::TurnL => { 179 dir = dir.left(); 180 } 181 Step::TurnR => { 182 dir = dir.right(); 183 } 184 } 185 } 186 pos.0 * 1000 + pos.1 * 4 + dir.val() 187 }