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 }