1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use crate::implementation::Board;
use myopic_core::*;

impl Board {
    pub fn passive_control_impl(&mut self) -> BitBoard {
        match &self.cache.passive_control {
            Some(x) => *x,
            None => {
                let result = self.compute_control(self.active.reflect());
                self.cache.passive_control = Some(result);
                result
            }
        }
    }

    /// Computes the total area of control on the board for a given side. Note
    /// that the passive king is treated as invisible so that if it is in
    /// check it cannot create it's own escape squares by blocking the
    /// control ray of an attacking slider. TODO Improve efficiency by
    /// treated all pawns as a block
    fn compute_control(&self, side: Side) -> BitBoard {
        let pieces = &self.pieces;
        let (whites, blacks) = match side {
            Side::White => (
                pieces.whites(),
                pieces.blacks() - pieces.king_location(Side::Black),
            ),
            Side::Black => (
                pieces.whites() - pieces.king_location(Side::White),
                pieces.blacks(),
            ),
        };
        let locs = |piece: Piece| pieces.locs(piece);
        let control = |piece: Piece, square: Square| piece.control(square, whites, blacks);
        Piece::of(side)
            .flat_map(|p| locs(p).into_iter().map(move |sq| control(p, sq)))
            .collect()
    }
}

#[cfg(test)]
mod test {
    use crate::implementation::test::TestBoard;
    use crate::implementation::Board;
    use myopic_core::{constants::*, BitBoard, CastleZoneSet, Side};

    struct TestCase {
        board: TestBoard,
        side: Side,
        expected_control: BitBoard,
    }

    fn execute_test(case: TestCase) {
        assert_eq!(
            case.expected_control,
            Board::from(case.board).compute_control(case.side)
        );
    }

    fn get_test_board() -> TestBoard {
        TestBoard {
            whites: vec![
                A2 | B3 | C2 | D2 | E4 | F2 | G2 | H2,
                F3,
                B2 | F1,
                A1,
                D1,
                E1,
            ],
            blacks: vec![
                A7 | B7 | C7 | D7 | E5 | F7 | G7 | H5,
                C6 | G8,
                C8,
                A8 | H8,
                F6,
                E8,
            ],
            castle_rights: CastleZoneSet::ALL,
            white_status: None,
            black_status: None,
            enpassant: None,
            active: Side::White,
            clock: 20,
            history_count: 20,
        }
    }

    #[test]
    fn test_white_control() {
        let expected_control: BitBoard = vec![
            A1, A2, A3, A4, A6, B1, B3, B5, C1, C2, C3, C4, D1, D2, D3, D4, D5, E1, E2, E3, E5, F1,
            F2, F3, F5, G1, G2, G3, G5, H2, H3, H4,
        ]
        .into_iter()
        .collect();

        execute_test(TestCase {
            board: get_test_board(),
            side: Side::White,
            expected_control,
        })
    }

    #[test]
    fn test_black_control() {
        let expected_control: BitBoard = vec![
            A7, A6, A5, B8, B7, B6, B4, C8, C6, D8, D7, D6, D4, E7, E6, E5, F8, F7, F6, F5, F4, F3,
            G8, G7, G6, G5, G4, H7, H6, H5, H4,
        ]
        .into_iter()
        .collect();

        execute_test(TestCase {
            board: get_test_board(),
            side: Side::Black,
            expected_control,
        })
    }
}