use super::piece_square_table::PieceSquareTable;
use bb::{BB, EMPTY, END_ROWS};
use castle::Castle;
use mv::{Move, MoveScore};
use mv_list::MoveList;
use piece::*;
use side::Side;
use square;
use square::*;
use std::collections::BinaryHeap;
use std::fmt;
pub struct SortedMoveList<'a> {
moves: &'a mut BinaryHeap<MoveScore>,
piece_square_table: &'a PieceSquareTable,
piece_grid: &'a [Piece; 64],
stm: Side,
}
impl<'a> fmt::Display for SortedMoveList<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl<'a> fmt::Debug for SortedMoveList<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl<'a> MoveList for SortedMoveList<'a> {
fn add_captures(&mut self, from: Square, targets: BB) {
if targets == EMPTY {
return;
}
let stm = self.stm;
let from_kind = unsafe { self.piece_grid.get_unchecked(from.to_usize()).kind() };
let from_score = self
.piece_square_table
.score(from_kind, from.from_side(stm));
for (to, _) in targets.iter() {
let to_score = self.piece_square_table.score(from_kind, to.from_side(stm));
let capture_kind = unsafe { self.piece_grid.get_unchecked(to.to_usize()).kind() };
let capture_score = self
.piece_square_table
.score(capture_kind, to.from_side(stm.flip()));
self.insert(
Move::new_capture(from, to),
-from_score + to_score + capture_score,
);
}
}
fn add_non_captures(&mut self, from: Square, targets: BB) {
if targets == EMPTY {
return;
}
let stm = self.stm;
let from_kind = unsafe { self.piece_grid.get_unchecked(from.to_usize()).kind() };
let from_score = self
.piece_square_table
.score(from_kind, from.from_side(stm));
for (to, _) in targets.iter() {
let to_score = self.piece_square_table.score(from_kind, to.from_side(stm));
self.insert(Move::new_push(from, to), -from_score + to_score);
}
}
fn add_castle(&mut self, castle: Castle) {
let score = self.piece_square_table.castle_score(castle);
self.insert(Move::new_castle(castle), score);
}
fn add_pawn_ep_capture(&mut self, from: Square, to: Square) {
let stm = self.stm;
let from_score = self.piece_square_table.score(PAWN, from.from_side(stm));
let to_score = self.piece_square_table.score(PAWN, to.from_side(stm));
let capture_sq = from.along_row_with_col(to);
let capture_score = self
.piece_square_table
.score(PAWN, capture_sq.from_side(stm.flip()));
let score = -from_score + to_score + capture_score;
self.insert(Move::new_ep_capture(from, to), score);
}
fn add_pawn_pushes(&mut self, shift: usize, targets: BB) {
let stm = self.stm;
let from_kind = PAWN;
for (to, _) in (targets & !END_ROWS).iter() {
let from = to.rotate_right(shift as square::Internal);
let from_score = self
.piece_square_table
.score(from_kind, from.from_side(stm));
let to_score = self.piece_square_table.score(from_kind, to.from_side(stm));
self.insert(Move::new_push(from, to), -from_score + to_score);
}
for (to, _) in (targets & END_ROWS).iter() {
let from = to.rotate_right(shift as square::Internal);
let from_score = self
.piece_square_table
.score(from_kind, from.from_side(stm));
for to_kind in &[QUEEN, ROOK, BISHOP, KNIGHT] {
let to_score = self.piece_square_table.score(*to_kind, to.from_side(stm));
self.insert(
Move::new_promotion(from, to, *to_kind),
-from_score + to_score,
);
}
}
}
fn add_pawn_captures(&mut self, shift: usize, targets: BB) {
let stm = self.stm;
let from_kind = PAWN;
for (to, _) in (targets & !END_ROWS).iter() {
let from = to.rotate_right(shift as square::Internal);
let from_score = self
.piece_square_table
.score(from_kind, from.from_side(stm));
let to_score = self.piece_square_table.score(from_kind, to.from_side(stm));
let capture_kind = unsafe { self.piece_grid.get_unchecked(to.to_usize()).kind() };
let capture_score = self
.piece_square_table
.score(capture_kind, to.from_side(stm.flip()));
self.insert(
Move::new_capture(from, to),
-from_score + to_score + capture_score,
);
}
for (to, _) in (targets & END_ROWS).iter() {
let from = to.rotate_right(shift as square::Internal);
let from_score = self
.piece_square_table
.score(from_kind, from.from_side(stm));
let capture_kind = unsafe { self.piece_grid.get_unchecked(to.to_usize()).kind() };
let capture_score = self
.piece_square_table
.score(capture_kind, to.from_side(stm.flip()));
for to_kind in &[QUEEN, ROOK, BISHOP, KNIGHT] {
let to_score = self.piece_square_table.score(*to_kind, to.from_side(stm));
self.insert(
Move::new_capture_promotion(from, to, *to_kind),
-from_score + to_score + capture_score,
);
}
}
}
}
impl<'a> SortedMoveList<'a> {
pub fn new(
piece_square_table: &'a PieceSquareTable,
piece_grid: &'a [Piece; 64],
stm: Side,
moves: &'a mut BinaryHeap<MoveScore>,
) -> SortedMoveList<'a> {
SortedMoveList {
moves: moves,
piece_square_table: piece_square_table,
piece_grid: piece_grid,
stm: stm,
}
}
pub fn best_move(&self) -> Option<&MoveScore> {
self.moves.peek()
}
#[allow(dead_code)]
pub fn len(&self) -> usize {
self.moves.len()
}
fn insert(&mut self, mv: Move, score: i16) {
self.moves.push(MoveScore::new(mv, score));
}
}
#[cfg(test)]
mod test {
extern crate rand;
use super::*;
use gen::*;
use position::*;
use rand::seq::SliceRandom;
use rand::Rng;
use side::WHITE;
#[test]
fn test_scored_move_vec() {
let position = &Position::from_fen(STARTING_POSITION_FEN).unwrap();
let piece_square_table = PieceSquareTable::new([[100i16; 64]; 6]);
let mut heap = BinaryHeap::<MoveScore>::new();
let mut list = SortedMoveList::new(
&piece_square_table,
position.grid(),
position.state().stm,
&mut heap,
);
legal_moves(&position, &mut list);
assert_eq!(heap.len(), 20);
}
#[test]
fn test_pawn_push_scoring() {
let position = &Position::from_fen(STARTING_POSITION_FEN).unwrap();
let mut piece_square_values = [[100i16; 64]; 6];
piece_square_values[PAWN.to_usize()][C2.to_usize()] = 150;
piece_square_values[PAWN.to_usize()][C4.to_usize()] = 165;
let piece_square_table = PieceSquareTable::new(piece_square_values);
let mut heap = BinaryHeap::<MoveScore>::new();
let mut list = SortedMoveList::new(
&piece_square_table,
position.grid(),
position.state().stm,
&mut heap,
);
legal_moves(&position, &mut list);
assert_list_includes_moves(&heap, &["c2c4 (15)"]);
}
#[test]
fn test_push_scoring() {
let position =
&Position::from_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b QqKk - 0 1")
.unwrap();
let mut piece_square_values = [[100i16; 64]; 6];
piece_square_values[KNIGHT.to_usize()][B1.to_usize()] = 300;
piece_square_values[KNIGHT.to_usize()][C3.to_usize()] = 333;
let piece_square_table = PieceSquareTable::new(piece_square_values);
let mut heap = BinaryHeap::<MoveScore>::new();
let mut list = SortedMoveList::new(
&piece_square_table,
position.grid(),
position.state().stm,
&mut heap,
);
legal_moves(&position, &mut list);
assert_list_includes_moves(&heap, &["b8c6 (33)"]);
}
#[test]
fn test_capture_scoring() {
let position =
&Position::from_fen("rnbqkbnr/pppppppp/2P5/8/8/8/PP1PPPPP/RNBQKBNR b QqKk - 0 1")
.unwrap();
let mut piece_square_values = [[100i16; 64]; 6];
piece_square_values[KNIGHT.to_usize()][B1.to_usize()] = 300;
piece_square_values[KNIGHT.to_usize()][C3.to_usize()] = 333;
piece_square_values[PAWN.to_usize()][C6.to_usize()] = 50;
let piece_square_table = PieceSquareTable::new(piece_square_values);
let mut heap = BinaryHeap::<MoveScore>::new();
let mut list = SortedMoveList::new(
&piece_square_table,
position.grid(),
position.state().stm,
&mut heap,
);
legal_moves(&position, &mut list);
assert_list_includes_moves(&heap, &["b8xc6 (83)"]);
}
#[test]
fn test_integrity() {
let position = &mut Position::from_fen(STARTING_POSITION_FEN).unwrap();
let piece_square_table = random_piece_square_table();
let initial_score = score_for_position(position, &piece_square_table);
let mut moves_scores = 0i16;
for _ in 0..30 {
let stm = position.state().stm;
let move_score = {
let mut heap = BinaryHeap::<MoveScore>::new();
let mut list = SortedMoveList::new(
&piece_square_table,
position.grid(),
position.state().stm,
&mut heap,
);
legal_moves(&position, &mut list);
let sorted_vec = heap.into_iter_sorted().collect::<Vec<MoveScore>>();
*sorted_vec.choose(&mut rand::thread_rng()).unwrap()
};
let score = move_score.score();
let mv = move_score.mv();
moves_scores += if stm == WHITE { score } else { -score };
position.make(mv);
}
let score_after_moves = score_for_position(position, &piece_square_table);
assert_eq!(initial_score + moves_scores, score_after_moves);
}
fn assert_list_includes_moves(heap: &BinaryHeap<MoveScore>, moves: &[&'static str]) {
for &m in moves.iter() {
assert!(heap
.iter()
.map(|pair: &MoveScore| format!("{} ({})", pair.mv(), pair.score()))
.any(|mv| mv == m));
}
}
fn random_piece_square_table() -> PieceSquareTable {
let mut piece_square_values = [[100i16; 64]; 6];
for pc in 0..6 {
for sq in 0..64 {
piece_square_values[pc][sq] = rand::thread_rng().gen_range(100, 200) as i16;
}
}
PieceSquareTable::new(piece_square_values)
}
fn score_for_position(pos: &Position, piece_square_table: &PieceSquareTable) -> i16 {
let mut score = 0i16;
for (idx, &pc) in pos.grid().iter().enumerate() {
if pc.is_some() {
let piece_square_value =
piece_square_table.score(pc.kind(), Square(idx).from_side(pc.side()));
score += if pos.state().stm == pc.side() {
piece_square_value
} else {
-piece_square_value
}
}
}
score
}
}