check_buddy/
algorithm.rs

1#![allow(unused_must_use)]
2use crate::piece_color::PieceColor;
3use crate::position_move::PositionMove;
4use crate::BoardMap;
5use rand::Rng;
6
7#[derive(Default, Clone)]
8pub struct ChessEngine {}
9
10impl ChessEngine {
11    const MIN: f32 = -1000.;
12    const MAX: f32 = 1000.;
13
14    pub fn find_best_move_minimax_ab(&mut self, board: BoardMap, depth: usize) -> PositionMove {
15        let mut best_value = Self::MIN;
16        let mut best_moves = vec![];
17        for from in board.get_active_pieces() {
18            let moves = board.gen_legal_positions(from);
19            for to in moves {
20                let piece_move = PositionMove::new(from, to);
21                let piece_value = board.get_piece(from).0;
22                let mut temp_board = board;
23
24                temp_board.single_move_turn(piece_move);
25                let move_value =
26                    self.ab_max(depth, temp_board, ChessEngine::MIN, ChessEngine::MAX, true);
27                temp_board.undo_move(piece_move, piece_value);
28
29                if move_value == best_value {
30                    best_moves.push(piece_move);
31                } else if move_value > best_value {
32                    best_value = move_value;
33                    best_moves.clear();
34                    best_moves.push(piece_move);
35                }
36            }
37        }
38
39        println!("best move value is {}", best_value);
40        println!(
41            "possible moves are {:?}",
42            best_moves
43                .iter()
44                .map(|x| format!("{:?} to {:?}", x.from, x.to))
45                .collect::<Vec<_>>()
46        );
47
48        let mut rand = rand::thread_rng();
49        let index = rand.gen_range(0..best_moves.len());
50        best_moves[index]
51    }
52
53    pub fn find_best_move_negamax(&mut self, board: BoardMap, depth: usize) -> PositionMove {
54        let mut best_value = Self::MIN;
55        let mut best_moves = vec![];
56        for from in board.get_active_pieces() {
57            let moves = board.gen_legal_positions(from);
58            for to in moves {
59                let piece_move = PositionMove::new(from, to);
60                let piece_value = board.get_piece(from).0;
61
62                let mut temp_board = board;
63                temp_board.single_move_turn(piece_move);
64                let move_value = self.nega_max(temp_board, depth);
65                temp_board.undo_move(piece_move, piece_value);
66
67                if move_value == best_value {
68                    best_moves.push(piece_move);
69                } else if move_value > best_value {
70                    best_value = move_value;
71                    best_moves.clear();
72                    best_moves.push(piece_move);
73                }
74            }
75        }
76
77        println!("best move value is {}", best_value);
78        println!(
79            "possible moves are {:?}",
80            best_moves
81                .iter()
82                .take(10)
83                .map(|x| format!("{:?} to {:?}", x.from, x.to))
84                .collect::<Vec<_>>()
85        );
86
87        let mut rand = rand::thread_rng();
88        let index = rand.gen_range(0..best_moves.len());
89        best_moves[index]
90    }
91
92    fn ab_max(
93        &self,
94        depth: usize,
95        board: BoardMap,
96        mut alpha: f32,
97        mut beta: f32,
98        is_maximizing_player: bool,
99    ) -> f32 {
100        if is_maximizing_player {
101            // AB MAX
102            if depth == 0 {
103                return self.evaluate(board);
104            }
105            let moves = board.gen_all_legal_moves();
106
107            for piece_move in moves.iter() {
108                let mut temp_board = board;
109
110                let piece_value = board.get_piece(piece_move.from).0;
111
112                temp_board.single_move_turn(*piece_move);
113                let score = self.ab_max(depth - 1, temp_board, alpha, beta, !is_maximizing_player);
114                temp_board.undo_move(*piece_move, piece_value);
115
116                if score >= beta {
117                    return beta;
118                }
119                if score > alpha {
120                    alpha = score;
121                }
122            }
123            alpha
124        } else {
125            // AB MIN
126            if depth == 0 {
127                return -self.evaluate(board);
128            }
129            let moves = board.gen_all_legal_moves();
130
131            for piece_move in moves.iter() {
132                let mut temp_board = board;
133
134                let piece_value = board.get_piece(piece_move.from).0;
135
136                temp_board.single_move_turn(*piece_move);
137                let score = self.ab_max(depth - 1, temp_board, alpha, beta, !is_maximizing_player);
138                temp_board.undo_move(*piece_move, piece_value);
139
140                if score <= alpha {
141                    return alpha;
142                }
143                if score < beta {
144                    beta = score;
145                }
146            }
147            beta
148        }
149    }
150
151    fn nega_max(&mut self, board: BoardMap, depth: usize) -> f32 {
152        if depth == 0 {
153            return self.evaluate(board);
154        }
155        let mut max = Self::MIN;
156        let moves = board.gen_all_legal_moves();
157
158        for piece_move in moves.iter() {
159            let mut temp_board = board;
160            temp_board.single_move_turn(*piece_move);
161
162            let value = -self.nega_max(board, depth - 1);
163            if value > max {
164                max = value;
165            }
166        }
167        max
168    }
169
170    /// score = materialWeight * (numWhitePieces - numBlackPieces) * who2move
171    //TODO https://chessfox.com/example-of-the-complete-evaluation-process-of-chess-a-chess-position/
172    // https://www.chessprogramming.org/Evaluation
173    //
174    fn evaluate(&self, board: BoardMap) -> f32 {
175        let material_weight = board.get_material_weight() as f32;
176        let num_white_pieces = board.get_num_white_pieces() as f32;
177        let num_black_pieces = board.get_num_black_pieces() as f32;
178        let who2move = if *board.get_active_color() == PieceColor::Black {
179            1.
180        } else {
181            -1.
182        };
183        // println!("{material_weight} * ({num_white_pieces} - {num_black_pieces}) * {who2move}");
184        material_weight * (num_white_pieces - num_black_pieces) * who2move
185    }
186}