aboutsummaryrefslogtreecommitdiff
path: root/2022/24/common.rs
diff options
context:
space:
mode:
Diffstat (limited to '2022/24/common.rs')
-rw-r--r--2022/24/common.rs204
1 files changed, 204 insertions, 0 deletions
diff --git a/2022/24/common.rs b/2022/24/common.rs
new file mode 100644
index 0000000..0af4789
--- /dev/null
+++ b/2022/24/common.rs
@@ -0,0 +1,204 @@
1// This code is more complicated than it should be. The reason is that
2// for part 1 I decided to ignore the start and finish position, and not
3// to treat them as other cells. This way I could get away with not
4// considering wall cells '#' at all.
5// But then for part 2 it was more convenient to include them, so I
6// added an enum Position {} that treats start and finish as special
7// cases. This is quite messy.
8
9use std::collections::{VecDeque, HashSet};
10
11#[derive(PartialEq, Debug)]
12pub enum Direction { Up, Down, Left, Right, None }
13
14impl Direction {
15 pub fn from_byte(b: u8) -> Direction {
16 match b {
17 b'^' => Direction::Up,
18 b'v' => Direction::Down,
19 b'>' => Direction::Right,
20 b'<' => Direction::Left,
21 b'.' => Direction::None,
22 x => panic!("unexpected char '{}'", x as char)
23 }
24 }
25}
26
27#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
28pub enum Position {
29 Start,
30 Finish,
31 Other { i: usize, j: usize }
32}
33
34impl Position {
35 pub fn dist(&self, other: Position) -> usize {
36 match (*self, other) {
37 (Position::Start, Position::Start) |
38 (Position::Finish, Position::Finish) =>
39 0,
40 (Position::Start, Position::Finish) |
41 (Position::Finish, Position::Start) =>
42 usize::MAX, // Not technically correct, but it works
43 (Position::Start | Position::Finish, Position::Other { i, j }) |
44 (Position::Other { i, j }, Position::Start | Position::Finish) =>
45 i + j,
46 (Position::Other { i: i1, j: j1 },
47 Position::Other { i: i2, j: j2 }) => {
48 let i = (i1 as i64 - i2 as i64).abs() as usize;
49 let j = (j1 as i64 - j2 as i64).abs() as usize;
50 i + j
51 }
52 }
53 }
54}
55
56pub struct Map {
57 pub tiles: Vec<Vec<Direction>>,
58 pub imax: usize,
59 pub jmax: usize
60}
61
62#[derive(Hash, Eq, PartialEq, Copy, Clone, Debug)]
63struct Node {
64 p: Position,
65 t: usize
66}
67
68impl Map {
69 fn submod(i: usize, t: usize, m: usize) -> usize {
70 let maybeneg = ((i as i64) - (t as i64)) % m as i64;
71 (maybeneg as usize + m) % m
72 }
73
74 pub fn is_busy(&self, p: Position, t: usize) -> bool {
75 match p {
76 Position::Start | Position::Finish => false,
77 Position::Other { i, j } => {
78 let iup = Self::submod(i, t, self.imax);
79 let idown = (i + t) % self.imax;
80 let jleft = Map::submod(j, t, self.jmax);
81 let jright = (j + t) % self.jmax;
82
83 self.tiles[iup][j] == Direction::Down ||
84 self.tiles[idown][j] == Direction::Up ||
85 self.tiles[i][jleft] == Direction::Right ||
86 self.tiles[i][jright] == Direction::Left
87 }
88 }
89 }
90
91 #[allow(dead_code)]
92 pub fn print(&self, t: usize) {
93 print!("#.");
94 for _ in 0..self.jmax { print!("#"); }
95 println!();
96
97 for i in 0..self.imax {
98 print!("#");
99 for j in 0..self.jmax {
100 let p = Position::Other { i, j };
101 print!("{}", if self.is_busy(p, t) { '*' } else { '.' });
102 }
103 println!("#");
104 }
105
106 for _ in 0..self.jmax { print!("#"); }
107 print!(".#");
108 println!();
109 }
110
111 fn mv(&self, p: Position, d: Direction) -> Option<Position> {
112 if d == Direction::None { return Some(p); }
113 match p {
114 Position::Start => if d == Direction::Down {
115 Some(Position::Other { i: 0, j: 0 })
116 } else { None },
117 Position::Finish => if d == Direction::Up {
118 Some(Position::Other { i: self.imax-1, j: self.jmax-1 })
119 } else { None },
120 Position::Other { i, j } => match d {
121 Direction::Up => if i > 0 {
122 Some(Position::Other { i: i-1, j })
123 } else {
124 if i == 0 && j == 0 {
125 Some(Position::Start)
126 } else { None }
127 },
128 Direction::Down => if i < self.imax-1 {
129 Some(Position::Other { i: i+1, j })
130 } else {
131 if i == self.imax-1 && j == self.jmax-1 {
132 Some(Position::Finish)
133 } else { None }
134 },
135 Direction::Left => if j > 0 {
136 Some(Position::Other { i, j: j-1 })
137 } else { None },
138 Direction::Right => if j < self.jmax-1 {
139 Some(Position::Other { i, j: j+1 })
140 } else { None },
141 Direction::None => panic!("stupid rust")
142 }
143 }
144 }
145
146 fn maybe_push(&self, x: Node, d: Direction, v: &mut HashSet<Node>) {
147 if let Some(p) = self.mv(x.p, d) {
148 if !self.is_busy(p, x.t+1) {
149 v.insert(Node { p, t: x.t+1 });
150 }
151 }
152 }
153
154 pub fn shortest_path(
155 &self,
156 start: Position,
157 finish: Position,
158 t0: usize
159 ) -> usize {
160 let mut vnext = HashSet::<Node>::new();
161 vnext.insert(Node { p: start, t: t0 });
162 while !vnext.is_empty() {
163 let mut v = vnext.clone().into_iter().collect::<Vec<_>>();
164 // Heuristic: explore first the positions closer to the target
165 v.sort_by(|a, b| a.p.dist(finish).cmp(&(b.p.dist(finish))));
166 let mut v = VecDeque::<Node>::from(v);
167 vnext.clear();
168 while !v.is_empty() {
169 let x = v.pop_front().unwrap();
170 if x.p == finish { return x.t; }
171
172 self.maybe_push(x, Direction::Up, &mut vnext);
173 self.maybe_push(x, Direction::Down, &mut vnext);
174 self.maybe_push(x, Direction::Left, &mut vnext);
175 self.maybe_push(x, Direction::Right, &mut vnext);
176 if !self.is_busy(x.p, x.t+1) {
177 self.maybe_push(x, Direction::None, &mut vnext);
178 }
179 }
180 }
181 panic!("queue is empty");
182 }
183}
184
185pub fn read_map_from_stdin() -> Map {
186 let mut tiles = vec![];
187 let mut line = String::new();
188 while std::io::stdin().read_line(&mut line).unwrap() > 0 {
189 let bytes = line.as_bytes();
190 if bytes[2] == b'#' {
191 line.clear();
192 continue;
193 }
194 let i = tiles.len();
195 tiles.push(vec![]);
196 for j in 1..bytes.len()-2 {
197 tiles[i].push(Direction::from_byte(bytes[j]));
198 }
199 line.clear();
200 }
201 let imax = tiles.len();
202 let jmax = tiles[0].len();
203 Map { tiles, imax, jmax }
204}

Generated with cgit - Back to sebastiano.tronto.net