myopic_brain/
quiescent.rs1use crate::eval::EvalChessBoard;
2use crate::{eval, see};
3use anyhow::Result;
4use myopic_board::{BitBoard, Move, MoveComputeType, Reflectable, Termination};
5use std::cmp;
6
7const Q_DEPTH_CAP: i32 = -8;
8const Q_CHECK_CAP: i32 = -2;
9
10pub fn search<B: EvalChessBoard>(
15 state: &mut B,
16 mut alpha: i32,
17 beta: i32,
18 depth: i32,
19) -> Result<i32> {
20 if depth == Q_DEPTH_CAP || state.termination_status().is_some() {
21 return Ok(match state.termination_status() {
22 Some(Termination::Loss) => eval::LOSS_VALUE,
23 Some(Termination::Draw) => eval::DRAW_VALUE,
24 None => state.static_eval(),
25 });
26 }
27 let mut result = if state.in_check() {
34 -eval::INFTY
35 } else {
36 state.static_eval()
37 };
38
39 if result >= beta {
41 return Ok(beta);
42 }
43 if alpha < result {
44 alpha = result;
45 }
46
47 for evolve in compute_quiescent_moves(state, depth) {
48 state.make(evolve)?;
49 let next_result = -search(state, -beta, -alpha, depth - 1)?;
50 state.unmake()?;
51 result = cmp::max(result, next_result);
52 alpha = cmp::max(alpha, result);
53 if alpha > beta {
54 return Ok(beta);
55 }
56 }
57 return Ok(result);
58}
59
60fn compute_quiescent_moves<B: EvalChessBoard>(state: &mut B, depth: i32) -> Vec<Move> {
61 let mut moves = if depth < Q_CHECK_CAP {
62 state.compute_moves(MoveComputeType::Attacks)
63 } else {
64 state.compute_moves(MoveComputeType::AttacksChecks)
65 };
66 let enemies = state.side(state.active().reflect());
67 let is_attack_filter = |mv: &Move| is_attack(mv, enemies);
68 let good_attack_threshold = if state.in_check() { -eval::INFTY } else { 0 };
70 let split_index = itertools::partition(&mut moves, is_attack_filter);
71 let mut attacks: Vec<_> = moves
74 .iter()
75 .take(split_index)
76 .map(|mv| (mv, score_attack(state, mv)))
77 .filter(|(_, score)| *score > good_attack_threshold)
78 .collect();
79 attacks.sort_by_key(|(_, score)| -*score);
80
81 moves
82 .iter()
83 .cloned()
84 .skip(split_index)
85 .chain(attacks.into_iter().map(|(mv, _)| mv.clone()))
86 .collect()
87}
88
89fn score_attack<B: EvalChessBoard>(state: &mut B, attack: &Move) -> i32 {
90 match attack {
91 &Move::Enpassant { .. } => 10000,
92 &Move::Promotion { .. } => 20000,
93 &Move::Standard { from, dest, .. } => {
94 see::exchange_value(state, from, dest, state.piece_values())
95 }
96 _ => 0,
98 }
99}
100
101fn is_attack(query: &Move, enemies: BitBoard) -> bool {
102 match query {
103 &Move::Enpassant { .. } => true,
104 &Move::Castle { .. } => false,
105 &Move::Promotion { dest, .. } => enemies.contains(dest),
106 &Move::Standard { dest, .. } => enemies.contains(dest),
107 }
108}