rustenginelib/
square.rs

1use crate::bitboard::*;
2use crate::constants::*;
3
4use once_cell::sync::Lazy;
5
6extern crate rand;
7
8use rand::Rng;
9
10use std::io::{BufWriter, Write};
11
12/// Rank type represents the rank of a square as an unsigned int
13pub type Rank = usize;
14/// Rank type represents the file of a square as an unsigned int
15pub type File = usize;
16/// Square type represents a square of a chess board as an unsigned int
17pub type Square = usize;
18
19/// PawnInfo struct records the possible move and ep related squares of pawns
20#[derive(Clone)]
21pub struct PawnInfo {
22    pub pushes: Vec<Square>,
23    pub captures: Vec<Square>,
24}
25
26/// Move type represents a chess move
27pub type Move = u32;
28
29/// MoveTrait adds methods to Move
30pub trait MoveTrait {
31    /// returns a move from from square and to square
32    fn ft(from_sq: Square, to_sq: Square) -> Move;
33    /// returns from square of move
34    fn from_sq(self) -> Square;
35    /// returns to square of move
36    fn to_sq(self) -> Square;
37    /// return uci representation of move
38    fn uci(self) -> String;
39}
40
41impl MoveTrait for Move {
42    /// returns a move from from square and to square
43    fn ft(from_sq: Square, to_sq: Square) -> Move {
44        ((from_sq << FROM_SQ_SHIFT) + (to_sq << TO_SQ_SHIFT)) as u32
45    }
46    /// returns from square of move
47    fn from_sq(self) -> Square {
48        ((self >> FROM_SQ_SHIFT) & SQUARE_MASK) as Square
49    }
50    /// returns to square of move
51    fn to_sq(self) -> Square {
52        ((self >> TO_SQ_SHIFT) & SQUARE_MASK) as Square
53    }
54    /// return uci representation of move
55    fn uci(self) -> String {
56        format!("{}{}", self.from_sq().uci(), self.to_sq().uci())
57    }
58}
59
60/// Delta enum lists the possible deltas of chess pieces
61#[derive(Copy, Clone)]
62pub enum Delta {
63    N,
64    NE,
65    NNE,
66    NEE,
67    E,
68    SE,
69    SEE,
70    SSE,
71    S,
72    SW,
73    SSW,
74    SWW,
75    W,
76    NW,
77    NWW,
78    NNW,
79}
80
81/// DeltaBuffer trait adds methods to various size buffers that holds deltas
82pub trait DeltaBuffer {
83    /// len tells the length of the delta buffer
84    fn len(&self) -> usize;
85    /// get gets a delta by index from the delta buffer
86    fn get(&self, i: usize) -> &Delta;
87}
88
89// DeltaBuffer trait adds methods to 1 buffer that holds deltas
90impl DeltaBuffer for [Delta; 1] {
91    /// len tells the length of the delta buffer
92    fn len(&self) -> usize {
93        1
94    }
95    /// get gets a delta by index from the delta buffer
96    fn get(&self, i: usize) -> &Delta {
97        return &self[i];
98    }
99}
100
101// DeltaBuffer trait adds methods to 4 buffer that holds deltas
102impl DeltaBuffer for [Delta; 4] {
103    /// len tells the length of the delta buffer
104    fn len(&self) -> usize {
105        4
106    }
107    /// get gets a delta by index from the delta buffer
108    fn get(&self, i: usize) -> &Delta {
109        return &self[i];
110    }
111}
112
113// DeltaBuffer trait adds methods to 8 buffer that holds deltas
114impl DeltaBuffer for [Delta; 8] {
115    /// len tells the length of the delta buffer
116    fn len(&self) -> usize {
117        8
118    }
119    /// get gets a delta by index from the delta buffer
120    fn get(&self, i: usize) -> &Delta {
121        return &self[i];
122    }
123}
124
125/// constructs a Square from rank and file
126pub fn rank_file(rank: Rank, file: File) -> Square {
127    rank * NUM_FILES + file
128}
129
130/// Square trait adds methods to a Square
131pub trait SquareTrait {
132    /// creates Square from uci
133    fn from_uci(uci: String) -> (Square, bool);
134    /// returns the rank of the square
135    fn rank(self) -> Rank;
136    /// returns the file of the square
137    fn file(self) -> File;
138    /// returns the UCI representation of the square
139    fn uci(self) -> String;
140    /// returns an otherwise empty bitboard with the bit for this square set
141    fn bitboard(self) -> Bitboard;
142    /// adds a delta to a square and returns the resulting square
143    /// together with a bool indicating whether adding was a success
144    fn add_delta(self, delta: &Delta) -> (Square, bool);
145    /// same as add_delta, but also fails if the resulting square is occupied
146    fn add_delta_occup(self, delta: &Delta, occup: Bitboard) -> (Square, bool);
147}
148
149/// SquareTrait adds methods to a Square
150impl SquareTrait for Square {
151    /// creates Square from uci    
152    fn from_uci(uci: String) -> (Square, bool) {
153        if uci.len() != 2 {
154            return (SQUARE_A1, false);
155        }
156        let file = match &uci[0..1] {
157            "a" => 0,
158            "b" => 1,
159            "c" => 2,
160            "d" => 3,
161            "e" => 4,
162            "f" => 5,
163            "g" => 6,
164            "h" => 7,
165            _ => panic!("invalid file {:?}", &uci[0..1]),
166        };
167        let rank = match &uci[1..2] {
168            "1" => 0,
169            "2" => 1,
170            "3" => 2,
171            "4" => 3,
172            "5" => 4,
173            "6" => 5,
174            "7" => 6,
175            "8" => 7,
176            _ => panic!("invalid rank {:?}", &uci[1..2]),
177        };
178        (rank * NUM_FILES + file, true)
179    }
180    /// returns the rank of the square
181    fn rank(self) -> Rank {
182        self >> RANK_SHIFT
183    }
184
185    /// returns the file of the square
186    fn file(self) -> File {
187        self & FILE_MASK
188    }
189
190    /// returns the UCI representation of the square
191    fn uci(self) -> String {
192        format!("{}{}", FILE_NAMES[self.file()], self.rank() + 1)
193    }
194
195    /// returns an otherwise empty bitboard with the bit for this square set
196    fn bitboard(self) -> Bitboard {
197        1 << (LAST_FILE - self.file()) + self.rank() * NUM_FILES
198    }
199
200    /// adds a delta to a square and returns the resulting square
201    /// together with a bool indicating whether adding was a success
202    fn add_delta(self, delta: &Delta) -> (Square, bool) {
203        let file = self.file();
204        let rank = self.rank();
205        match delta {
206            Delta::N => {
207                return if rank > ONE_BEFORE_LAST_RANK {
208                    (0, false)
209                } else {
210                    (rank_file(rank + 1, file), true)
211                }
212            }
213            Delta::NE => {
214                return if rank > ONE_BEFORE_LAST_RANK || file > ONE_BEFORE_LAST_FILE {
215                    (0, false)
216                } else {
217                    (rank_file(rank + 1, file + 1), true)
218                }
219            }
220            Delta::NNE => {
221                return if rank > TWO_BEFORE_LAST_RANK || file > ONE_BEFORE_LAST_FILE {
222                    (0, false)
223                } else {
224                    (rank_file(rank + 2, file + 1), true)
225                }
226            }
227            Delta::NEE => {
228                return if rank > ONE_BEFORE_LAST_RANK || file > TWO_BEFORE_LAST_FILE {
229                    (0, false)
230                } else {
231                    (rank_file(rank + 1, file + 2), true)
232                }
233            }
234            Delta::E => {
235                return if file > ONE_BEFORE_LAST_FILE {
236                    (0, false)
237                } else {
238                    (rank_file(rank, file + 1), true)
239                }
240            }
241            Delta::SE => {
242                return if rank < 1 || file > ONE_BEFORE_LAST_FILE {
243                    (0, false)
244                } else {
245                    (rank_file(rank - 1, file + 1), true)
246                }
247            }
248            Delta::SEE => {
249                return if rank < 1 || file > TWO_BEFORE_LAST_FILE {
250                    (0, false)
251                } else {
252                    (rank_file(rank - 1, file + 2), true)
253                }
254            }
255            Delta::SSE => {
256                return if rank < 2 || file > ONE_BEFORE_LAST_FILE {
257                    (0, false)
258                } else {
259                    (rank_file(rank - 2, file + 1), true)
260                }
261            }
262            Delta::S => {
263                return if rank < 1 {
264                    (0, false)
265                } else {
266                    (rank_file(rank - 1, file), true)
267                }
268            }
269            Delta::SW => {
270                return if rank < 1 || file < 1 {
271                    (0, false)
272                } else {
273                    (rank_file(rank - 1, file - 1), true)
274                }
275            }
276            Delta::SSW => {
277                return if rank < 2 || file < 1 {
278                    (0, false)
279                } else {
280                    (rank_file(rank - 2, file - 1), true)
281                }
282            }
283            Delta::SWW => {
284                return if rank < 1 || file < 2 {
285                    (0, false)
286                } else {
287                    (rank_file(rank - 1, file - 2), true)
288                }
289            }
290            Delta::W => {
291                return if file < 1 {
292                    (0, false)
293                } else {
294                    (rank_file(rank, file - 1), true)
295                }
296            }
297            Delta::NW => {
298                return if rank > ONE_BEFORE_LAST_RANK || file < 1 {
299                    (0, false)
300                } else {
301                    (rank_file(rank + 1, file - 1), true)
302                }
303            }
304            Delta::NWW => {
305                return if rank > ONE_BEFORE_LAST_RANK || file < 2 {
306                    (0, false)
307                } else {
308                    (rank_file(rank + 1, file - 2), true)
309                }
310            }
311            Delta::NNW => {
312                return if rank > TWO_BEFORE_LAST_RANK || file < 1 {
313                    (0, false)
314                } else {
315                    (rank_file(rank + 2, file - 1), true)
316                }
317            }
318        }
319    }
320
321    /// same as add_delta, but also fails if the resulting square is occupied
322    fn add_delta_occup(self, delta: &Delta, occup: Bitboard) -> (Square, bool) {
323        let (sq, ok) = self.add_delta(delta);
324        if !ok {
325            return (0, false);
326        }
327        if sq.bitboard() & occup != 0 {
328            return (0, false);
329        }
330        return (sq, true);
331    }
332}
333
334/// returns jump attack bitboard from deltas
335pub fn jump_attack<T: DeltaBuffer>(sq: Square, deltas: &T, occup: Bitboard) -> Bitboard {
336    let mut bb: Bitboard = 0;
337    for i in 0..deltas.len() {
338        let (test_sq, ok) = sq.add_delta_occup(deltas.get(i), occup);
339        if ok {
340            bb |= test_sq.bitboard();
341        }
342    }
343    bb
344}
345
346/// returns sliding attack bitboard from deltas
347pub fn sliding_attack<T: DeltaBuffer>(sq: Square, deltas: &T, occup: Bitboard) -> Bitboard {
348    let mut bb: Bitboard = 0;
349    for i in 0..deltas.len() {
350        let mut test_sq = sq;
351        loop {
352            let (new_test_sq, ok) = test_sq.add_delta(deltas.get(i));
353            if ok {
354                test_sq = new_test_sq;
355                bb |= test_sq.bitboard();
356                if (test_sq.bitboard()) & occup != 0 {
357                    break;
358                }
359            } else {
360                break;
361            }
362        }
363    }
364    bb
365}
366
367/// AttackTable type records an attack bitboard for every square of a chess board
368pub type AttackTable = [Bitboard; BOARD_AREA];
369
370/// PAWN_INFOS records PawnInfo for color and square
371pub static PAWN_INFOS: Lazy<Vec<Vec<PawnInfo>>> = Lazy::new(|| {
372    let mut cpis: Vec<Vec<PawnInfo>> = Vec::new();
373    for col in BLACK..WHITE + 1 {
374        let mut spis: Vec<PawnInfo> = Vec::new();
375        for sq in 0..BOARD_AREA {
376            let mut pi: PawnInfo = PawnInfo {
377                pushes: Vec::new(),
378                captures: Vec::new(),
379            };
380            let push_delta: &Delta = if col == WHITE { &Delta::N } else { &Delta::S };
381            let (push_one_sq, ok) = sq.add_delta(push_delta);
382            if ok {
383                pi.pushes.push(push_one_sq);
384                let (capt_left_sq, ok) = push_one_sq.add_delta(&Delta::W);
385                if ok {
386                    pi.captures.push(capt_left_sq);
387                }
388                let (capt_right_sq, ok) = push_one_sq.add_delta(&Delta::E);
389                if ok {
390                    pi.captures.push(capt_right_sq);
391                }
392                if sq.rank() == PAWN_START_RANKS[col] {
393                    let (push_two_sq, ok) = push_one_sq.add_delta(push_delta);
394                    if ok {
395                        pi.pushes.push(push_two_sq);
396                    } else {
397                        panic!("illegal pawn start rank")
398                    }
399                }
400            }
401            spis.push(pi);
402        }
403        cpis.push(spis);
404    }
405    cpis
406});
407
408/// KNIGHT_ATTACK is the attack table of knight
409pub static KNIGHT_ATTACK: Lazy<AttackTable> = Lazy::new(|| {
410    let mut at = EMPTY_ATTACK_TABLE;
411    for sq in 0..BOARD_AREA {
412        at[sq] = jump_attack(sq, &KNIGHT_DELTAS, 0);
413    }
414    at
415});
416/// BISHOP_ATTACK is the attack table of bishop
417pub static BISHOP_ATTACK: Lazy<AttackTable> = Lazy::new(|| {
418    let mut at = EMPTY_ATTACK_TABLE;
419    for sq in 0..BOARD_AREA {
420        at[sq] = sliding_attack(sq, &BISHOP_DELTAS, 0);
421    }
422    at
423});
424/// BISHOP_MAGIC_ATTACK is the magic attack table of bishop
425pub static BISHOP_MAGIC_ATTACK: Lazy<AttackTable> = Lazy::new(|| {
426    let mut at = EMPTY_ATTACK_TABLE;
427    for sq in 0..BOARD_AREA {
428        at[sq] = magic_attack(sq, sliding_attack(sq, &BISHOP_DELTAS, 0));
429    }
430    at
431});
432/// ROOK_ATTACK is the attack table of rook
433pub static ROOK_ATTACK: Lazy<AttackTable> = Lazy::new(|| {
434    let mut at = EMPTY_ATTACK_TABLE;
435    for sq in 0..BOARD_AREA {
436        at[sq] = sliding_attack(sq, &ROOK_DELTAS, 0);
437    }
438    at
439});
440/// ROOK_MAGIC_ATTACK is the magic attack table of rook
441pub static ROOK_MAGIC_ATTACK: Lazy<AttackTable> = Lazy::new(|| {
442    let mut at = EMPTY_ATTACK_TABLE;
443    for sq in 0..BOARD_AREA {
444        at[sq] = magic_attack(sq, sliding_attack(sq, &ROOK_DELTAS, 0));
445    }
446    at
447});
448/// QUEEN_ATTACK is the attack table of queen
449pub static QUEEN_ATTACK: Lazy<AttackTable> = Lazy::new(|| {
450    let mut at = EMPTY_ATTACK_TABLE;
451    for sq in 0..BOARD_AREA {
452        at[sq] = sliding_attack(sq, &QUEEN_DELTAS, 0);
453    }
454    at
455});
456/// LANCER_ATTACKS is are attack tables of lancers
457pub static LANCER_ATTACKS: Lazy<[AttackTable; NUM_LANCERS]> = Lazy::new(|| {
458    let mut ats: [AttackTable; NUM_LANCERS] = [
459        EMPTY_ATTACK_TABLE,
460        EMPTY_ATTACK_TABLE,
461        EMPTY_ATTACK_TABLE,
462        EMPTY_ATTACK_TABLE,
463        EMPTY_ATTACK_TABLE,
464        EMPTY_ATTACK_TABLE,
465        EMPTY_ATTACK_TABLE,
466        EMPTY_ATTACK_TABLE,
467    ];
468    for ld in 0..NUM_LANCERS {
469        for sq in 0..BOARD_AREA {
470            ats[ld][sq] = sliding_attack(sq, &[LANCER_DELTAS[ld]], 0);
471        }
472    }
473    ats
474});
475/// KING_ATTACK is the attack table of king
476pub static KING_ATTACK: Lazy<AttackTable> = Lazy::new(|| {
477    let mut at = EMPTY_ATTACK_TABLE;
478    for sq in 0..BOARD_AREA {
479        at[sq] = jump_attack(sq, &QUEEN_DELTAS, 0);
480    }
481    at
482});
483/// KING_AREA is the attack table of king plus king square
484pub static KING_AREA: Lazy<AttackTable> = Lazy::new(|| {
485    let mut at = EMPTY_ATTACK_TABLE;
486    for sq in 0..BOARD_AREA {
487        at[sq] = jump_attack(sq, &QUEEN_DELTAS, 0) | sq.bitboard();
488    }
489    at
490});
491
492/// translates an occupancy mask to partial occupancy of a mobility
493pub fn translate_mask_to_occupancy(mask: usize, mobility: Bitboard) -> Bitboard {
494    let mut occup: Bitboard = 0;
495    let mut mob = mobility;
496    let mut m = mask;
497    loop {
498        let (bb, ok) = mob.pop_bitboard();
499        if ok {
500            if m & 1 != 0 {
501                occup |= bb;
502            }
503            m >>= 1;
504        } else {
505            return occup;
506        }
507    }
508}
509
510/// returns a random magic
511pub fn new_magic() -> u64 {
512    rand::thread_rng().gen::<u64>()
513}
514
515/// returns index of mobility in mobility table for a magic and shift
516pub fn mobility_index(mobility: Bitboard, magic: u64, shift: usize) -> usize {
517    let (result, _) = mobility.overflowing_mul(magic);
518    (result >> (64 - shift)) as usize
519}
520
521/// detects collision of a magic applied to a mobility, returns true if there is a collision, false otherwise
522pub fn detect_collision(magic: u64, shift: usize, mobility: Bitboard) -> bool {
523    let variations = mobility.variation_count();
524    let mut mapped = vec![(false, 0); 1 << shift];
525
526    let mut mask: usize = 0;
527
528    loop {
529        if mask < variations {
530            let occup = translate_mask_to_occupancy(mask, mobility);
531            let index = mobility_index(occup, magic, shift);
532            let (used, bb) = mapped[index];
533            if used {
534                if bb != occup {
535                    return true;
536                }
537            } else {
538                mapped[index] = (true, occup);
539            }
540            mask += 1;
541        } else {
542            return false;
543        }
544    }
545}
546
547/// tries to find magic for shift in certain number of tries for a mobility, returns a tuple of the found magic and a bool indicating success
548pub fn find_magic_for_shift(shift: usize, mobility: Bitboard, max_tries: usize) -> (u64, bool) {
549    for _ in 0..max_tries {
550        let magic = new_magic();
551        if !detect_collision(magic, shift, mobility) {
552            return (magic, true);
553        }
554    }
555    return (0, false);
556}
557
558/// tries to find magic and shift in certain number of tries per shift for a mobility, starting from a maximum shift, going to minimum shift, returns a tuple of the found magic and a bool indicating success
559pub fn find_magic_and_shift(
560    mobility: Bitboard,
561    max_shift: usize,
562    min_shift: usize,
563    max_tries: usize,
564) -> (u64, usize, bool) {
565    let mut last_good_shift: usize = 0;
566    let mut last_good_magic: u64 = 0;
567    let mut has_magic = false;
568    for i in 0..(max_shift - min_shift + 1) {
569        let shift = max_shift - i;
570        let (magic, ok) = find_magic_for_shift(shift, mobility, max_tries);
571        if ok {
572            last_good_shift = shift;
573            last_good_magic = magic;
574            has_magic = true;
575        }
576    }
577    if has_magic {
578        return (last_good_magic, last_good_shift, true);
579    }
580    return (0, 0, false);
581}
582
583/// return magic attack for an attack
584pub fn magic_attack(sq: Square, attack: Bitboard) -> Bitboard {
585    let mut mask = BITBOARD_MIDDLE;
586
587    if sq.rank() == 0 {
588        mask |= BITBOARD_RANK_1_MIDDLE;
589    }
590    if sq.rank() == LAST_RANK {
591        mask |= BITBOARD_RANK_8_MIDDLE;
592    }
593    if sq.file() == 0 {
594        mask |= BITBOARD_FILE_A_MIDDLE;
595    }
596    if sq.file() == LAST_FILE {
597        mask |= BITBOARD_FILE_H_MIDDLE;
598    }
599
600    attack & mask
601}
602
603/// find and log magic and shift
604pub fn log_find_magic_and_shift(
605    bw: &mut BufWriter<std::fs::File>,
606    sq: Square,
607    attack: Bitboard,
608    name: &str,
609) {
610    let data = format!(
611        "{}\nmobility for {} at square {} , count {}\n\n",
612        attack.pretty_print_string(),
613        name,
614        sq.uci(),
615        attack.count_ones()
616    );
617
618    (*bw)
619        .write_all(data.as_bytes())
620        .expect("Unable to write data.");
621
622    println!("{}", data);
623
624    let result = find_magic_and_shift(attack, 14, if name == "bishop" { 5 } else { 10 }, 5000);
625
626    if !result.2 {
627        panic!("shift too large")
628    }
629
630    let data = format!(
631        "magic kind {} square {} magic {:016X} shift {}\n\n",
632        name,
633        sq.uci(),
634        result.0,
635        result.1
636    );
637
638    (*bw)
639        .write_all(data.as_bytes())
640        .expect("Unable to write data.");
641
642    println!("{}", data);
643}
644
645/// find and log all magics
646pub fn find_and_log_magics() {
647    let file = std::fs::File::create("magics.txt").expect("Error: Unable to create magics file.");
648
649    let mut bw = BufWriter::new(file);
650
651    for sq in 0..BOARD_AREA {
652        log_find_magic_and_shift(&mut bw, sq, magic_attack(sq, ROOK_ATTACK[sq]), "rook");
653
654        log_find_magic_and_shift(&mut bw, sq, magic_attack(sq, BISHOP_ATTACK[sq]), "bishop");
655    }
656}
657
658/// MagicInfo records the magic and shift for a square
659pub struct MagicInfo {
660    pub sq: Square,
661    pub magic: u64,
662    pub shift: usize,
663}
664
665/// returns the total number of magic look up table items
666pub fn total_magic_space(magics: [MagicInfo; BOARD_AREA]) -> usize {
667    let mut total = 0;
668    for i in 0..BOARD_AREA {
669        let mi = &magics[i];
670        total += 1 << mi.shift;
671    }
672    total
673}
674
675/// create magic lookup table
676pub fn create_magic_lookup_table<T: DeltaBuffer>(
677    mis: &[MagicInfo; BOARD_AREA],
678    at: &AttackTable,
679    deltas: &T,
680) -> Vec<Vec<Bitboard>> {
681    let mut table = vec![vec![0; 0]; BOARD_AREA];
682    for sq in 0..BOARD_AREA {
683        let mobility = at[sq];
684        let variations = mobility.variation_count();
685
686        let mut mask: usize = 0;
687
688        let sq = mis[sq].sq; // just to use redundant sq for something
689
690        let magic = mis[sq].magic;
691        let shift = mis[sq].shift;
692
693        table[sq] = vec![0; 1 << shift];
694
695        loop {
696            if mask < variations {
697                let occup = translate_mask_to_occupancy(mask, mobility);
698                let index = mobility_index(occup, magic, shift);
699
700                table[sq][index] = sliding_attack(sq, deltas, occup);
701
702                mask += 1;
703            } else {
704                break;
705            }
706        }
707    }
708    table
709}
710
711/// MAGIC_LOOKUP_BISHOP is the magic lookup table for bishop
712pub static MAGIC_LOOKUP_BISHOP: Lazy<Vec<Vec<Bitboard>>> =
713    Lazy::new(|| create_magic_lookup_table(&BISHOP_MAGICS, &BISHOP_MAGIC_ATTACK, &BISHOP_DELTAS));
714
715/// MAGIC_LOOKUP_ROOK is the magic lookup table for rook
716pub static MAGIC_LOOKUP_ROOK: Lazy<Vec<Vec<Bitboard>>> =
717    Lazy::new(|| create_magic_lookup_table(&ROOK_MAGICS, &ROOK_MAGIC_ATTACK, &ROOK_DELTAS));
718
719/// MoveGenNode lists the move generation modes
720#[derive(Copy, Clone)]
721pub enum MoveGenMode {
722    Violent,
723    Quiet,
724    All,
725}
726
727/// returns sliding mobility
728pub fn get_sliding_mobility(
729    gen_mode: MoveGenMode,
730    sq: Square,
731    occup_us: Bitboard,
732    occup_them: Bitboard,
733    mis: &[MagicInfo; BOARD_AREA],
734    at: &AttackTable,
735    lookup_table: &Vec<Vec<Bitboard>>,
736) -> Bitboard {
737    let magic = mis[sq].magic;
738    let shift = mis[sq].shift;
739    let magic_occup = (occup_us | occup_them) & at[sq];
740    let index = mobility_index(magic_occup, magic, shift);
741    match gen_mode {
742        MoveGenMode::All => lookup_table[sq][index] & !occup_us,
743        MoveGenMode::Violent => lookup_table[sq][index] & occup_them,
744        MoveGenMode::Quiet => lookup_table[sq][index] & !(occup_us | occup_them),
745    }
746}
747
748/// returns bishop mobility
749pub fn bishop_mobility(
750    sq: Square,
751    gen_mode: MoveGenMode,
752    occup_us: Bitboard,
753    occup_them: Bitboard,
754) -> Bitboard {
755    get_sliding_mobility(
756        gen_mode,
757        sq,
758        occup_us,
759        occup_them,
760        &BISHOP_MAGICS,
761        &BISHOP_MAGIC_ATTACK,
762        &MAGIC_LOOKUP_BISHOP,
763    )
764}
765
766/// returns rook mobility
767pub fn rook_mobility(
768    sq: Square,
769    gen_mode: MoveGenMode,
770    occup_us: Bitboard,
771    occup_them: Bitboard,
772) -> Bitboard {
773    get_sliding_mobility(
774        gen_mode,
775        sq,
776        occup_us,
777        occup_them,
778        &ROOK_MAGICS,
779        &ROOK_MAGIC_ATTACK,
780        &MAGIC_LOOKUP_ROOK,
781    )
782}
783
784/// returns jailer mobility
785pub fn jailer_mobility(
786    sq: Square,
787    gen_mode: MoveGenMode,
788    occup_us: Bitboard,
789    occup_them: Bitboard,
790) -> Bitboard {
791    rook_mobility(sq, gen_mode, occup_us, occup_them) & (!occup_them)
792}
793
794/// returns queen mobility
795pub fn queen_mobility(
796    sq: Square,
797    gen_mode: MoveGenMode,
798    occup_us: Bitboard,
799    occup_them: Bitboard,
800) -> Bitboard {
801    bishop_mobility(sq, gen_mode, occup_us, occup_them)
802        | rook_mobility(sq, gen_mode, occup_us, occup_them)
803}
804
805/// returns lancer mobility
806pub fn lancer_mobility(
807    sq: Square,
808    gen_mode: MoveGenMode,
809    occup_us: Bitboard,
810    occup_them: Bitboard,
811    lancer_mask: Bitboard,
812) -> Bitboard {
813    queen_mobility(sq, gen_mode, 0, occup_them) & lancer_mask & (!occup_us)
814}
815
816/// returns jump mobility
817pub fn get_jump_mobility(
818    gen_mode: MoveGenMode,
819    occup_us: Bitboard,
820    occup_them: Bitboard,
821    attack: Bitboard,
822) -> Bitboard {
823    match gen_mode {
824        MoveGenMode::All => attack & !occup_us,
825        MoveGenMode::Violent => attack & occup_them,
826        MoveGenMode::Quiet => attack & !(occup_us | occup_them),
827    }
828}
829
830/// returns knight mobility
831pub fn knight_mobility(
832    sq: Square,
833    gen_mode: MoveGenMode,
834    occup_us: Bitboard,
835    occup_them: Bitboard,
836) -> Bitboard {
837    get_jump_mobility(gen_mode, occup_us, occup_them, KNIGHT_ATTACK[sq])
838}
839
840/// returns king mobility
841pub fn king_mobility(
842    sq: Square,
843    gen_mode: MoveGenMode,
844    occup_us: Bitboard,
845    occup_them: Bitboard,
846) -> Bitboard {
847    get_jump_mobility(gen_mode, occup_us, occup_them, KING_ATTACK[sq])
848}