1use super::*;
6
7#[derive(Clone, Copy, PartialEq, Eq, Debug)]
12pub(crate) enum Walker {
13 Crab,
14 Gull,
15}
16
17impl Board {
18 pub(super) fn resolve_walls_for(
21 &self,
22 tile: u16,
23 dir: &mut Direction,
24 handed: Handedness,
25 who: Walker,
26 ) {
27 let d = *dir;
28 if self.passable_for(tile, d, who) {
29 return;
30 }
31 let (first, second) = match handed {
32 Handedness::Left => (d.left(), d.right()),
33 Handedness::Right => (d.right(), d.left()),
34 };
35 if self.passable_for(tile, first, who) {
36 *dir = first;
37 } else if self.passable_for(tile, second, who) {
38 *dir = second;
39 } else {
40 *dir = d.reverse();
41 }
42 }
43
44 pub(super) fn resolve_walls(&self, crab: &mut Crab) {
47 self.resolve_walls_for(crab.tile, &mut crab.dir, crab.handed, Walker::Crab);
48 }
49
50 pub(super) fn turnstile_deflect(
56 &mut self,
57 tile: u16,
58 dir: &mut Direction,
59 handed: Handedness,
60 who: Walker,
61 ) -> bool {
62 let t = tile as usize;
63 let TileKind::Turnstile { next_right } = self.tiles[t] else {
64 return false;
65 };
66 *dir = if next_right { dir.right() } else { dir.left() };
67 self.tiles[t] = TileKind::Turnstile {
68 next_right: !next_right,
69 };
70 self.resolve_walls_for(tile, dir, handed, who);
71 true
72 }
73
74 pub(super) fn passable(&self, tile: u16, dir: Direction) -> bool {
77 self.passable_for(tile, dir, Walker::Crab)
78 }
79
80 fn wall_edge(&self, x: usize, y: usize, dir: Direction) -> (bool, usize) {
85 let w = self.width as usize;
86 match dir {
87 Direction::Up => (true, y * w + x),
88 Direction::Down => (true, (y + 1) * w + x),
89 Direction::Left => (false, y * (w + 1) + x),
90 Direction::Right => (false, y * (w + 1) + x + 1),
91 }
92 }
93
94 pub(super) fn edge_blocked(&self, x: usize, y: usize, dir: Direction) -> bool {
96 let (horizontal, i) = self.wall_edge(x, y, dir);
97 if horizontal {
98 self.h_walls[i]
99 } else {
100 self.v_walls[i]
101 }
102 }
103
104 pub(super) fn set_edge(&mut self, x: usize, y: usize, dir: Direction, present: bool) {
106 let (horizontal, i) = self.wall_edge(x, y, dir);
107 if horizontal {
108 self.h_walls[i] = present;
109 } else {
110 self.v_walls[i] = present;
111 }
112 }
113
114 pub(super) fn passable_for(&self, tile: u16, dir: Direction, who: Walker) -> bool {
117 let (x, y) = self.coords(tile);
118 if self.edge_blocked(x as usize, y as usize, dir) {
119 return false;
120 }
121 let (dx, dy) = dir.offset();
122 let (nx, ny) = (x + dx, y + dy);
123 let dest = if !self.in_bounds(nx, ny) {
124 if !self.wrap {
125 return false;
126 }
127 let (wx, wy) = self.wrap_coords(nx, ny);
128 self.tiles[self.index(wx, wy) as usize]
129 } else {
130 self.tiles[self.index(nx, ny) as usize]
131 };
132 match dest {
133 TileKind::Rock => false,
134 TileKind::Kelp => who == Walker::Crab,
135 TileKind::Empty
136 | TileKind::Castle(_)
137 | TileKind::Spawner(_)
138 | TileKind::Turnstile { .. }
139 | TileKind::Pool => true,
140 }
141 }
142
143 pub(super) fn ring_openings(
147 &self,
148 cx: i32,
149 cy: i32,
150 ring: &[(i32, i32)],
151 ) -> Vec<(i32, i32, i32, i32)> {
152 ring.iter()
153 .map(|&(ox, oy)| (cx + ox, cy + oy, ox, oy))
154 .filter(|&(nx, ny, _, _)| {
155 self.in_bounds(nx, ny) && self.tiles[self.index(nx, ny) as usize] == TileKind::Empty
156 })
157 .collect()
158 }
159
160 pub(super) fn wrap_coords(&self, x: i32, y: i32) -> (i32, i32) {
161 let w = i32::from(self.width);
162 let h = i32::from(self.height);
163 ((x % w + w) % w, (y % h + h) % h)
164 }
165
166 pub(super) fn neighbor(&self, tile: u16, dir: Direction) -> u16 {
167 let (x, y) = self.coords(tile);
168 let (dx, dy) = dir.offset();
169 if self.wrap {
170 let (wx, wy) = self.wrap_coords(x + dx, y + dy);
171 return self.index(wx, wy);
172 }
173 self.index(x + dx, y + dy)
174 }
175
176 pub fn step(&self, tile: u16, dir: Direction) -> Option<u16> {
181 let (x, y) = self.coords(tile);
182 let (dx, dy) = dir.offset();
183 if self.wrap {
184 let (wx, wy) = self.wrap_coords(x + dx, y + dy);
185 return Some(self.index(wx, wy));
186 }
187 self.in_bounds(x + dx, y + dy)
188 .then(|| self.index(x + dx, y + dy))
189 }
190
191 pub fn coords(&self, tile: u16) -> (i32, i32) {
196 (
197 i32::from(tile % u16::from(self.width)),
198 i32::from(tile / u16::from(self.width)),
199 )
200 }
201
202 pub fn index_of(&self, x: u8, y: u8) -> u16 {
204 self.index(i32::from(x), i32::from(y))
205 }
206
207 pub(super) fn index(&self, x: i32, y: i32) -> u16 {
208 debug_assert!(self.in_bounds(x, y));
209 (y * i32::from(self.width) + x) as u16
210 }
211
212 pub(super) fn in_bounds(&self, x: i32, y: i32) -> bool {
213 x >= 0 && y >= 0 && x < i32::from(self.width) && y < i32::from(self.height)
214 }
215}