use crate::arrayvec::ArrayVec;
use crate::coretypes::{Cp, Move, MoveInfo, MAX_MOVES};
use crate::movelist::MoveInfoList;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub(crate) struct OrderStrategy {
is_tt_move: bool, promotion: Option<Cp>, mvv_lva: (bool, Cp), }
impl Default for OrderStrategy {
fn default() -> Self {
OrderStrategy {
is_tt_move: false,
promotion: None,
mvv_lva: (false, Cp(0)),
}
}
}
impl From<(MoveInfo, Option<Move>)> for OrderStrategy {
fn from((move_info, key_move): (MoveInfo, Option<Move>)) -> Self {
let is_tt_move = key_move == Some(move_info.move_());
let promotion = move_info.promotion.map(|pk| pk.centipawns());
let mvv_lva = if let Some(victim) = move_info.captured() {
let attacker = move_info.piece_kind.centipawns();
let victim = victim.centipawns();
(true, victim - attacker)
} else {
(false, Cp(0))
};
Self {
is_tt_move,
promotion,
mvv_lva,
}
}
}
pub fn order_all_moves(legal_moves: MoveInfoList, maybe_key_move: Option<Move>) -> MoveInfoList {
let mut ordering_vec: ArrayVec<(MoveInfo, OrderStrategy), MAX_MOVES> = legal_moves
.into_iter()
.map(|move_info| (move_info, OrderStrategy::from((move_info, maybe_key_move))))
.collect();
ordering_vec.sort_unstable_by_key(|pair| pair.1);
ordering_vec.into_iter().map(|pair| pair.0).collect()
}
pub fn pick_best_move(legal_moves: &mut MoveInfoList, key_move: Option<Move>) -> Option<MoveInfo> {
legal_moves
.iter()
.enumerate()
.max_by(|left, right| {
let left = OrderStrategy::from((*left.1, key_move));
let right = OrderStrategy::from((*right.1, key_move));
left.cmp(&right)
})
.map(|(index, _)| index)
.map(|index| legal_moves.swap_remove(index))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::coretypes::{Move, PieceKind, Square::*};
use crate::fen::Fen;
use crate::transposition::NodeKind;
use crate::Position;
#[test]
fn order_all_moves_one_capture() {
let pos = Position::parse_fen("rnb1k1nr/pppp1ppp/8/4p3/3P4/8/PPP1PPPP/RN2KBNR b - - 3 11")
.unwrap();
let capture = Move::new(E5, D4, None);
let num_moves = 24; let legal_moves = pos
.get_legal_moves()
.into_iter()
.map(|move_| pos.move_info(move_))
.collect();
let mut ordered_legal_moves = order_all_moves(legal_moves, None);
assert_eq!(ordered_legal_moves.len(), num_moves);
assert_eq!(ordered_legal_moves.pop().unwrap().move_(), capture);
}
#[test]
fn node_kind_ordering() {
assert!(NodeKind::Pv > NodeKind::Cut);
assert!(NodeKind::Cut > NodeKind::All);
}
#[test]
fn order_strategy_cmp() {
let os = OrderStrategy::default();
let mut gt_os = OrderStrategy::default();
gt_os.is_tt_move = true;
let mut lt_os = OrderStrategy::default();
lt_os.promotion = Some(PieceKind::Queen.centipawns());
assert!(gt_os > os);
assert!(gt_os > lt_os);
}
}