use moves::{Move, MoveDigest, AddMove};
pub struct MoveStack {
moves: Vec<Move>,
savepoints: Vec<usize>,
first_move_index: usize,
}
impl AddMove for MoveStack {
#[inline]
fn add_move(&mut self, m: Move) {
self.push(m);
}
}
impl MoveStack {
pub fn new() -> MoveStack {
MoveStack {
moves: Vec::with_capacity(32 * 64),
savepoints: Vec::with_capacity(32),
first_move_index: 0,
}
}
#[inline]
pub fn save(&mut self) {
self.savepoints.push(self.first_move_index);
self.first_move_index = self.moves.len();
}
#[inline]
pub fn restore(&mut self) {
self.moves.truncate(self.first_move_index);
self.first_move_index = self.savepoints.pop().unwrap();
}
#[inline]
pub fn ply(&self) -> usize {
self.savepoints.len()
}
#[inline]
pub fn clear(&mut self) {
self.moves.truncate(self.first_move_index);
}
#[inline]
pub fn clear_all(&mut self) {
self.moves.clear();
self.savepoints.clear();
self.first_move_index = 0;
}
#[inline]
pub fn push(&mut self, m: Move) {
debug_assert!(self.moves.len() >= self.first_move_index);
self.moves.push(m);
}
#[inline]
pub fn pop(&mut self) -> Option<Move> {
debug_assert!(self.moves.len() >= self.first_move_index);
if self.moves.len() > self.first_move_index {
self.moves.pop()
} else {
None
}
}
#[inline]
pub fn pull(&mut self, index: usize) -> Move {
let last_move = *self.moves.last().unwrap();
let m;
{
let requested_slot = &mut self.moves[self.first_move_index + index];
m = *requested_slot;
*requested_slot = last_move;
}
self.moves.pop();
m
}
#[inline]
pub fn pull_move(&mut self, move_digest: MoveDigest) -> Option<Move> {
debug_assert!(self.moves.len() >= self.first_move_index);
let last_move = if let Some(last) = self.moves.last() {
*last
} else {
return None;
};
let m;
'moves: loop {
for curr in self.list_mut().iter_mut() {
if curr.digest() == move_digest {
m = *curr;
*curr = last_move;
break 'moves;
}
}
return None;
}
debug_assert!(!self.moves.is_empty());
self.moves.pop();
Some(m)
}
#[inline]
pub fn pull_best(&mut self) -> Option<Move> {
debug_assert!(self.moves.len() >= self.first_move_index);
let moves = &mut self.moves;
let n = moves.len();
if n > self.first_move_index {
let last = n - 1;
unsafe {
let mut best_move = *moves.get_unchecked(last);
let mut best = last;
let mut i = last;
while i > self.first_move_index {
i -= 1;
let m = *moves.get_unchecked(i);
if m > best_move {
best_move = m;
best = i;
}
}
*moves.get_unchecked_mut(best) = *moves.get_unchecked(last);
moves.pop();
return Some(best_move);
}
}
None
}
#[inline]
pub fn list(&self) -> &[Move] {
debug_assert!(self.moves.len() >= self.first_move_index);
&self.moves[self.first_move_index..]
}
#[inline]
pub fn list_mut(&mut self) -> &mut [Move] {
debug_assert!(self.moves.len() >= self.first_move_index);
&mut self.moves[self.first_move_index..]
}
}
#[cfg(test)]
mod tests {
use super::*;
use board::*;
use squares::*;
use moves::*;
#[should_panic]
#[test]
fn move_stack_pull_panic() {
let mut s = MoveStack::new();
s.pull(0);
}
#[test]
fn move_stack() {
let cr = CastlingRights::new(0);
let m = Move::new(MOVE_NORMAL, E2, E4, 0, PIECE_NONE, PAWN, cr, 8, 0);
let mut s = MoveStack::new();
assert_eq!(s.ply(), 0);
assert!(s.pull_best().is_none());
s.save();
assert_eq!(s.ply(), 1);
s.push(m);
assert_eq!(s.pull(0), m);
assert!(s.pull_best().is_none());
s.restore();
assert!(s.pull_best().is_none());
assert_eq!(s.list().len(), 0);
assert!(s.pop().is_none());
assert!(s.pull_move(m.digest()).is_none());
s.push(m);
s.push(m);
assert_eq!(s.list().len(), 2);
assert_eq!(s.pop().unwrap(), m);
assert_eq!(s.list().len(), 1);
s.push(m);
assert_eq!(s.pull_move(m.digest()).unwrap(), m);
assert_eq!(s.list().len(), 1);
s.push(m);
assert_eq!(s.list().iter().count(), 2);
s.save();
s.push(m);
s.restore();
assert_eq!(s.pull_best().unwrap(), m);
assert_eq!(s.pull_best().unwrap(), m);
assert!(s.pull_best().is_none());
assert_eq!(s.ply(), 0);
s.push(m);
s.clear();
assert_eq!(s.ply(), 0);
assert_eq!(s.list().len(), 0);
s.save();
s.save();
s.push(m);
assert_eq!(s.ply(), 2);
s.clear_all();
assert_eq!(s.ply(), 0);
assert_eq!(s.list().len(), 0);
}
}