1use super::map_bit_board_to_squares;
2use super::{Direction, Path, Piece, PushPullDirection, Square};
3
4#[rustfmt::skip]
5pub const TRAP_MASK: u64 = 0b__00000000__00000000__00100100__00000000__00000000__00100100__00000000__00000000;
6#[rustfmt::skip]
7const P1_PLACEMENT_MASK: u64 = 0b__11111111__11111111__00000000__00000000__00000000__00000000__00000000__00000000;
8#[rustfmt::skip]
9const P2_PLACEMENT_MASK: u64 = 0b__00000000__00000000__00000000__00000000__00000000__00000000__11111111__11111111;
10
11#[rustfmt::skip]
12pub const LEFT_COLUMN_MASK: u64 = 0b__00000001__00000001__00000001__00000001__00000001__00000001__00000001__00000001;
13#[rustfmt::skip]
14pub const RIGHT_COLUMN_MASK: u64 = 0b__10000000__10000000__10000000__10000000__10000000__10000000__10000000__10000000;
15#[rustfmt::skip]
16pub const TOP_ROW_MASK: u64 = 0b__00000000__00000000__00000000__00000000__00000000__00000000__00000000__11111111;
17#[rustfmt::skip]
18pub const BOTTOM_ROW_MASK: u64 = 0b__11111111__00000000__00000000__00000000__00000000__00000000__00000000__00000000;
19
20#[derive(Clone, Debug)]
21
22pub struct PieceBoard {
23 pub p1_pieces: u64,
24 pub all_pieces: u64,
25 pub elephants: u64,
26 pub camels: u64,
27 pub horses: u64,
28 pub dogs: u64,
29 pub cats: u64,
30 pub rabbits: u64,
31}
32
33impl PieceBoard {
34 pub fn initial() -> Self {
35 PieceBoard {
36 p1_pieces: 0,
37 all_pieces: 0,
38 elephants: 0,
39 camels: 0,
40 horses: 0,
41 dogs: 0,
42 cats: 0,
43 rabbits: 0,
44 }
45 }
46
47 pub fn new(p1_pieces: u64, elephants: u64, camels: u64, horses: u64, dogs: u64, cats: u64, rabbits: u64) -> Self {
48 Self {
49 p1_pieces,
50 elephants,
51 camels,
52 horses,
53 dogs,
54 cats,
55 rabbits,
56 all_pieces: rabbits | cats | dogs | horses | camels | elephants,
57 }
58 }
59
60 pub fn get_bits_for_piece(&self, piece: Piece, p1_pieces: bool) -> u64 {
61 let player_piece_mask = if p1_pieces { self.p1_pieces } else { !self.p1_pieces & self.all_pieces };
62 self.get_bits_by_piece_type(piece) & player_piece_mask
63 }
64
65 pub fn get_player_piece_mask(&self, p1_pieces: bool) -> u64 {
66 if p1_pieces { self.p1_pieces } else { !self.p1_pieces & self.all_pieces }
67 }
68
69 pub fn get_bits_by_piece_type(&self, piece: Piece) -> u64 {
70 match piece {
71 Piece::Elephant => self.elephants,
72 Piece::Camel => self.camels,
73 Piece::Horse => self.horses,
74 Piece::Dog => self.dogs,
75 Piece::Cat => self.cats,
76 Piece::Rabbit => self.rabbits,
77 }
78 }
79
80 pub fn bits_by_piece_type_mut(&mut self, piece: Piece) -> &mut u64 {
81 match piece {
82 Piece::Elephant => &mut self.elephants,
83 Piece::Camel => &mut self.camels,
84 Piece::Horse => &mut self.horses,
85 Piece::Dog => &mut self.dogs,
86 Piece::Cat => &mut self.cats,
87 Piece::Rabbit => &mut self.rabbits,
88 }
89 }
90
91 pub fn get_trapped_piece_bits(&self) -> u64 {
92 let animal_is_on_trap = animal_is_on_trap(self);
93
94 if animal_is_on_trap {
95 let unsupported_piece_bits = get_both_player_unsupported_piece_bits(self);
96 unsupported_piece_bits & TRAP_MASK
97 } else {
98 0
99 }
100 }
101
102 pub fn get_piece_type_at_square(&self, square: &Square) -> Option<Piece> {
103 let square_bit = square.as_bit_board();
104 if square_bit & self.all_pieces != 0 { Some(get_piece_type_at_bit(square_bit, self)) } else { None }
105 }
106
107 pub fn get_curr_player_frozen_pieces(&self, p1_turn_to_move: bool) -> u64 {
108 let opp_piece_mask = self.get_opponent_piece_mask(p1_turn_to_move);
109 let curr_player_piece_mask = !opp_piece_mask & self.all_pieces;
110 let threatened_pieces = get_threatened_pieces(opp_piece_mask, curr_player_piece_mask, self);
111
112 curr_player_piece_mask & threatened_pieces & !get_supported_pieces(curr_player_piece_mask)
113 }
114
115 pub fn get_curr_player_non_frozen_pieces(&self, p1_turn_to_move: bool) -> u64 {
116 let opp_piece_mask = self.get_opponent_piece_mask(p1_turn_to_move);
117 let curr_player_piece_mask = !opp_piece_mask & self.all_pieces;
118 let threatened_pieces = get_threatened_pieces(opp_piece_mask, curr_player_piece_mask, self);
119
120 curr_player_piece_mask & (!threatened_pieces | get_supported_pieces(curr_player_piece_mask))
121 }
122
123 pub fn get_curr_player_piece_mask(&self, p1_turn_to_move: bool) -> u64 {
124 if p1_turn_to_move { self.p1_pieces } else { !self.p1_pieces & self.all_pieces }
125 }
126
127 pub fn get_opponent_piece_mask(&self, p1_turn_to_move: bool) -> u64 {
128 if p1_turn_to_move { !self.p1_pieces & self.all_pieces } else { self.p1_pieces }
129 }
130
131 pub fn push_pull_piece(&mut self, square: Square, direction: PushPullDirection) -> bool {
132 let mut piece_trapped = false;
133
134 for (square, direction) in direction.get_steps(square) {
135 piece_trapped |= self.move_piece(square, direction);
136 }
137
138 piece_trapped
139 }
140
141 pub fn move_piece_on_path(&mut self, square: Square, path: Path) -> bool {
142 let mut piece_trapped = false;
143 let mut square = square;
144
145 for direction in path.iter() {
146 piece_trapped |= self.move_piece(square, direction);
147 square = square.shift_in_direction(direction);
148 }
149
150 piece_trapped
151 }
152
153 pub fn move_piece_update_board(&mut self, square: Square, direction: Direction) {
154 let source_square_bit = square.as_bit_board();
155
156 self.elephants = shift_piece_in_direction(self.elephants, source_square_bit, direction);
157 self.camels = shift_piece_in_direction(self.camels, source_square_bit, direction);
158 self.horses = shift_piece_in_direction(self.horses, source_square_bit, direction);
159 self.dogs = shift_piece_in_direction(self.dogs, source_square_bit, direction);
160 self.cats = shift_piece_in_direction(self.cats, source_square_bit, direction);
161 self.rabbits = shift_piece_in_direction(self.rabbits, source_square_bit, direction);
162 self.p1_pieces = shift_piece_in_direction(self.p1_pieces, source_square_bit, direction);
163 self.all_pieces = shift_piece_in_direction(self.all_pieces, source_square_bit, direction);
164 }
165
166 pub fn move_piece(&mut self, square: Square, direction: Direction) -> bool {
167 self.move_piece_update_board(square, direction);
168 self.remove_trapped_pieces()
169 }
170
171 pub fn remove_trapped_pieces(&mut self) -> bool {
172 let trapped_animal_bits = self.get_trapped_piece_bits();
173 let piece_trapped = trapped_animal_bits != 0;
174
175 if piece_trapped {
176 let untrapped_animal_bits = !trapped_animal_bits;
177 self.elephants &= untrapped_animal_bits;
178 self.camels &= untrapped_animal_bits;
179 self.horses &= untrapped_animal_bits;
180 self.dogs &= untrapped_animal_bits;
181 self.cats &= untrapped_animal_bits;
182 self.rabbits &= untrapped_animal_bits;
183 self.p1_pieces &= untrapped_animal_bits;
184 self.all_pieces &= untrapped_animal_bits;
185 }
186
187 piece_trapped
188 }
189
190 pub fn get_vertical_symmetry(&self) -> Self {
191 Self::new(
192 get_bit_board_vertical_symmetry(self.p1_pieces),
193 get_bit_board_vertical_symmetry(self.elephants),
194 get_bit_board_vertical_symmetry(self.camels),
195 get_bit_board_vertical_symmetry(self.horses),
196 get_bit_board_vertical_symmetry(self.dogs),
197 get_bit_board_vertical_symmetry(self.cats),
198 get_bit_board_vertical_symmetry(self.rabbits),
199 )
200 }
201
202 pub fn place(&mut self, piece: Piece, square: Square, p1_piece: bool) {
203 let placement_bit = square.as_bit_board();
204
205 assert!(placement_bit & self.empty_placements(p1_piece) != 0, "Piece was placed on a non-empty or non placement square");
206
207 self.fill(piece, placement_bit, p1_piece);
208 }
209
210 pub fn fill(&mut self, piece: Piece, mask: u64, p1_piece: bool) {
211 *self.bits_by_piece_type_mut(piece) |= mask;
212
213 self.all_pieces |= mask;
214
215 if p1_piece {
216 self.p1_pieces |= mask;
217 }
218 }
219
220 pub fn empty_placements(&self, p1_placements: bool) -> u64 {
221 let placement_bits = if p1_placements { P1_PLACEMENT_MASK } else { P2_PLACEMENT_MASK };
222
223 !self.all_pieces & placement_bits
224 }
225}
226
227fn get_bit_board_vertical_symmetry(bit_board: u64) -> u64 {
228 map_bit_board_to_squares(bit_board).iter().map(|s| s.vertical_symmetry()).fold(0, |r, s| r | s.as_bit_board())
229}
230
231impl PartialEq for PieceBoard {
232 fn eq(&self, other: &Self) -> bool {
233 self.p1_pieces == other.p1_pieces
234 && self.elephants == other.elephants
235 && self.camels == other.camels
236 && self.horses == other.horses
237 && self.dogs == other.dogs
238 && self.cats == other.cats
239 && self.rabbits == other.rabbits
240 }
241}
242
243pub struct ExcludedSquaresWithDirections {
244 pub none: u64,
245 pub north: u64,
246 pub east: u64,
247 pub south: u64,
248 pub west: u64,
249}
250
251pub fn get_supported_square_exclusions(curr_player_piece_mask: u64) -> ExcludedSquaresWithDirections {
252 let supported_squares_n = shift_pieces_up!(curr_player_piece_mask);
253 let supported_squares_e = shift_pieces_right!(curr_player_piece_mask);
254 let supported_squares_s = shift_pieces_down!(curr_player_piece_mask);
255 let supported_squares_w = shift_pieces_left!(curr_player_piece_mask);
256 let north = supported_squares_e | supported_squares_s | supported_squares_w;
257 let east = supported_squares_n | supported_squares_s | supported_squares_w;
258 let south = supported_squares_n | supported_squares_e | supported_squares_w;
259 let west = supported_squares_n | supported_squares_e | supported_squares_s;
260 let none = north | south;
261
262 ExcludedSquaresWithDirections { north, east, south, west, none }
263}
264
265pub fn get_excluded_direction(i: usize, directions: &[Direction], exclusions: &ExcludedSquaresWithDirections) -> u64 {
266 if i == 1 { get_exclusion_for_direction(directions[0], exclusions) } else { exclusions.none }
270}
271
272pub fn get_exclusion_for_direction(direction: Direction, exclusions: &ExcludedSquaresWithDirections) -> u64 {
273 match direction {
274 Direction::Up => exclusions.north,
275 Direction::Right => exclusions.east,
276 Direction::Down => exclusions.south,
277 Direction::Left => exclusions.west,
278 }
279}
280
281pub fn get_threatened_pieces(predator_piece_mask: u64, prey_piece_mask: u64, piece_board: &PieceBoard) -> u64 {
282 let predator_elephant_influence = get_influenced_squares(piece_board.elephants & predator_piece_mask);
283 let predator_camel_influence = get_influenced_squares(piece_board.camels & predator_piece_mask);
284 let predator_horse_influence = get_influenced_squares(piece_board.horses & predator_piece_mask);
285 let predator_dog_influence = get_influenced_squares(piece_board.dogs & predator_piece_mask);
286 let predator_cat_influence = get_influenced_squares(piece_board.cats & predator_piece_mask);
287
288 let camel_threats = predator_elephant_influence;
289 let horse_threats = camel_threats | predator_camel_influence;
290 let dog_threats = horse_threats | predator_horse_influence;
291 let cat_threats = dog_threats | predator_dog_influence;
292 let rabbit_threats = cat_threats | predator_cat_influence;
293
294 let threatened_pieces =
295 (piece_board.camels & camel_threats) | (piece_board.horses & horse_threats) | (piece_board.dogs & dog_threats) | (piece_board.cats & cat_threats) | (piece_board.rabbits & rabbit_threats);
296
297 threatened_pieces & prey_piece_mask
298}
299
300pub fn get_threatened_pieces_for_dir(predator_piece_mask: u64, prey_piece_mask: u64, direction: Direction, piece_board: &PieceBoard) -> u64 {
301 let predator_elephant_influence = shift_pieces_in_direction(piece_board.elephants & predator_piece_mask, direction);
302 let predator_camel_influence = shift_pieces_in_direction(piece_board.camels & predator_piece_mask, direction);
303 let predator_horse_influence = shift_pieces_in_direction(piece_board.horses & predator_piece_mask, direction);
304 let predator_dog_influence = shift_pieces_in_direction(piece_board.dogs & predator_piece_mask, direction);
305 let predator_cat_influence = shift_pieces_in_direction(piece_board.cats & predator_piece_mask, direction);
306
307 let camel_threats = predator_elephant_influence;
308 let horse_threats = camel_threats | predator_camel_influence;
309 let dog_threats = horse_threats | predator_horse_influence;
310 let cat_threats = dog_threats | predator_dog_influence;
311 let rabbit_threats = cat_threats | predator_cat_influence;
312
313 let threatened_pieces =
314 (piece_board.camels & camel_threats) | (piece_board.horses & horse_threats) | (piece_board.dogs & dog_threats) | (piece_board.cats & cat_threats) | (piece_board.rabbits & rabbit_threats);
315
316 threatened_pieces & prey_piece_mask
317}
318
319pub fn get_threats_for_piece_type(predator_piece_mask: u64, piece_type: Piece, piece_board: &PieceBoard) -> u64 {
320 match piece_type {
321 Piece::Elephant => 0,
322 Piece::Camel => get_influenced_squares(piece_board.elephants & predator_piece_mask),
323 Piece::Horse => get_influenced_squares((piece_board.elephants | piece_board.camels) & predator_piece_mask),
324 Piece::Dog => get_influenced_squares((piece_board.elephants | piece_board.camels | piece_board.horses) & predator_piece_mask),
325 Piece::Cat => get_influenced_squares((piece_board.elephants | piece_board.camels | piece_board.horses | piece_board.dogs) & predator_piece_mask),
326 Piece::Rabbit => get_influenced_squares((piece_board.elephants | piece_board.camels | piece_board.horses | piece_board.dogs | piece_board.cats) & predator_piece_mask),
327 }
328}
329
330pub fn get_influenced_squares(piece_board: u64) -> u64 {
331 shift_pieces_up!(piece_board) | shift_pieces_right!(piece_board) | shift_pieces_down!(piece_board) | shift_pieces_left!(piece_board)
332}
333
334pub fn can_move_in_direction(direction: Direction, empty_squares: u64) -> u64 {
335 shift_pieces_in_opp_direction(direction, empty_squares)
336}
337
338fn shift_pieces_in_opp_direction(direction: Direction, bits: u64) -> u64 {
339 match direction {
340 Direction::Up => shift_pieces_down!(bits),
341 Direction::Right => shift_pieces_left!(bits),
342 Direction::Down => shift_pieces_up!(bits),
343 Direction::Left => shift_pieces_right!(bits),
344 }
345}
346
347pub fn shift_pieces_in_direction(bits: u64, direction: Direction) -> u64 {
348 match direction {
349 Direction::Up => shift_pieces_up!(bits),
350 Direction::Right => shift_pieces_right!(bits),
351 Direction::Down => shift_pieces_down!(bits),
352 Direction::Left => shift_pieces_left!(bits),
353 }
354}
355
356fn get_piece_type_at_bit(square_bit: u64, piece_board: &PieceBoard) -> Piece {
357 if piece_board.rabbits & square_bit != 0 {
358 Piece::Rabbit
359 } else if piece_board.elephants & square_bit != 0 {
360 Piece::Elephant
361 } else if piece_board.camels & square_bit != 0 {
362 Piece::Camel
363 } else if piece_board.horses & square_bit != 0 {
364 Piece::Horse
365 } else if piece_board.dogs & square_bit != 0 {
366 Piece::Dog
367 } else {
368 Piece::Cat
369 }
370}
371
372fn animal_is_on_trap(piece_board: &PieceBoard) -> bool {
373 (piece_board.all_pieces & TRAP_MASK) != 0
374}
375
376fn get_both_player_unsupported_piece_bits(piece_board: &PieceBoard) -> u64 {
377 piece_board.all_pieces & !get_both_player_supported_pieces(piece_board)
378}
379
380fn get_both_player_supported_pieces(piece_board: &PieceBoard) -> u64 {
381 let p1_pieces = piece_board.p1_pieces;
382 let p2_pieces = piece_board.all_pieces & !p1_pieces;
383
384 get_supported_pieces(p1_pieces) | get_supported_pieces(p2_pieces)
385}
386
387pub fn get_supported_pieces(piece_bits: u64) -> u64 {
388 let up_supported_pieces = piece_bits & shift_pieces_up!(piece_bits);
389 let right_supported_pieces = piece_bits & shift_pieces_right!(piece_bits);
390 let down_supported_pieces = piece_bits & shift_pieces_down!(piece_bits);
391 let left_supported_pieces = piece_bits & shift_pieces_left!(piece_bits);
392
393 up_supported_pieces | right_supported_pieces | down_supported_pieces | left_supported_pieces
394}
395
396fn shift_piece_in_direction(piece_board: u64, source_square_bit: u64, direction: Direction) -> u64 {
397 shift_in_direction(piece_board & source_square_bit, direction) | piece_board & !source_square_bit
398}
399
400fn shift_in_direction(bits: u64, direction: Direction) -> u64 {
401 match direction {
402 Direction::Up => shift_up!(bits),
403 Direction::Right => shift_right!(bits),
404 Direction::Down => shift_down!(bits),
405 Direction::Left => shift_left!(bits),
406 }
407}