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
use crate::eval::EvalBoard;
use crate::{eval, see};
use myopic_board::{Move, MoveComputeType, MutBoard, Termination};
use myopic_core::bitboard::BitBoard;
use myopic_core::reflectable::Reflectable;
use std::cmp;
const Q_DEPTH_CAP: i32 = -8;
const Q_CHECK_CAP: i32 = -2;
pub(super) fn search<B: EvalBoard>(state: &mut B, mut alpha: i32, beta: i32, depth: i32) -> i32 {
if depth == Q_DEPTH_CAP || state.termination_status().is_some() {
return match state.termination_status() {
Some(Termination::Loss) => eval::LOSS_VALUE,
Some(Termination::Draw) => eval::DRAW_VALUE,
None => state.static_eval(),
};
}
let mut result = if state.in_check() { -eval::INFTY } else { state.static_eval() };
if result >= beta {
return beta;
}
if alpha < result {
alpha = result;
}
for evolve in compute_quiescent_moves(state, depth) {
let discards = state.evolve(&evolve);
let next_result = -search(state, -beta, -alpha, depth - 1);
state.devolve(&evolve, discards);
result = cmp::max(result, next_result);
alpha = cmp::max(alpha, result);
if alpha > beta {
return beta;
}
}
return result;
}
fn compute_quiescent_moves<B: MutBoard>(state: &mut B, depth: i32) -> Vec<Move> {
let mut moves = if depth < Q_CHECK_CAP {
state.compute_moves(MoveComputeType::Attacks)
} else {
state.compute_moves(MoveComputeType::AttacksChecks)
};
let enemies = state.side(state.active().reflect());
let is_attack_filter = |mv: &Move| is_attack(mv, enemies);
let good_attack_threshold = if state.in_check() { -eval::INFTY } else { 0 };
let split_index = itertools::partition(&mut moves, is_attack_filter);
let mut attacks: Vec<_> = moves
.iter()
.take(split_index)
.map(|mv| (mv, score_attack(state, mv)))
.filter(|(_, score)| *score > good_attack_threshold)
.collect();
attacks.sort_by_key(|(_, score)| -*score);
moves
.iter()
.cloned()
.skip(split_index)
.chain(attacks.into_iter().map(|(mv, _)| mv.clone()))
.collect()
}
fn score_attack<B: MutBoard>(state: &mut B, attack: &Move) -> i32 {
match attack {
&Move::Enpassant(_, _) => 10000,
&Move::Promotion(_, _, _) => 20000,
&Move::Standard(_, source, target) => see::exchange_value(state, source, target),
_ => panic!(),
}
}
fn is_attack(query: &Move, enemies: BitBoard) -> bool {
match query {
&Move::Enpassant(_, _) => true,
&Move::Castle(_) => false,
&Move::Promotion(_, target, _) => enemies.contains(target),
&Move::Standard(_, _, target) => enemies.contains(target),
}
}