Introduce movement Rays
These reflect the fact that a piece can't jump over another piece (unless it's a knight). Issue #5
This commit is contained in:
115
rs/src/board.rs
115
rs/src/board.rs
@@ -47,12 +47,6 @@ impl Position {
|
||||
self.file.delta(delta_file)?,
|
||||
))
|
||||
}
|
||||
fn deltas_to_valid_positions(&self, deltas: &[(i8, i8)]) -> Vec<Position> {
|
||||
deltas
|
||||
.iter()
|
||||
.filter_map(|&delta_rank_file| self.delta(delta_rank_file).ok())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
||||
@@ -173,30 +167,73 @@ pub struct Piece {
|
||||
pub piece_type: PieceType,
|
||||
}
|
||||
|
||||
struct Ray {
|
||||
unit: (i8, i8),
|
||||
coefs: Option<Vec<u8>>,
|
||||
source: Position,
|
||||
current: u8,
|
||||
}
|
||||
|
||||
impl Ray {
|
||||
fn new(unit: (i8, i8), coefs: Option<Vec<u8>>, source: Position) -> Self {
|
||||
Ray {
|
||||
unit,
|
||||
coefs,
|
||||
source,
|
||||
current: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Ray {
|
||||
type Item = Position;
|
||||
fn next(&mut self) -> Option<Position> {
|
||||
let (delta_unit_rank, delta_unit_file) = self.unit;
|
||||
let coef = i8::try_from(match &self.coefs {
|
||||
None => self.current + 1,
|
||||
Some(coefs) => {
|
||||
let crt_usize = self.current as usize;
|
||||
if crt_usize >= coefs.len() {
|
||||
return None;
|
||||
}
|
||||
coefs[crt_usize]
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let delta = (
|
||||
delta_unit_rank.checked_mul(coef).unwrap(),
|
||||
delta_unit_file.checked_mul(coef).unwrap(),
|
||||
);
|
||||
let rval = self.source.delta(delta).ok();
|
||||
self.current = self.current.wrapping_add(1);
|
||||
rval
|
||||
}
|
||||
}
|
||||
|
||||
impl Piece {
|
||||
fn get_moves(&self, position: &Position) -> Vec<Position> {
|
||||
let deltas = match self.piece_type {
|
||||
PieceType::Pawn => self._pawn_get_move_deltas(position),
|
||||
};
|
||||
position.deltas_to_valid_positions(&deltas)
|
||||
}
|
||||
fn get_captures(&self, position: &Position) -> Vec<Position> {
|
||||
let deltas = match self.piece_type {
|
||||
PieceType::Pawn => self._pawn_get_capture_deltas(),
|
||||
};
|
||||
position.deltas_to_valid_positions(&deltas)
|
||||
}
|
||||
fn _pawn_get_move_deltas(&self, position: &Position) -> Vec<(i8, i8)> {
|
||||
fn get_move_rays(&self, position: &Position) -> Vec<Ray> {
|
||||
match self.piece_type {
|
||||
PieceType::Pawn => {
|
||||
let direction = self.color.get_direction();
|
||||
if position.rank == self.color.get_pawn_rank() {
|
||||
vec![(direction, 0), (2 * direction, 0)]
|
||||
let coefs = if position.rank == self.color.get_pawn_rank() {
|
||||
vec![1, 2]
|
||||
} else {
|
||||
vec![(direction, 0)]
|
||||
vec![1]
|
||||
};
|
||||
vec![Ray::new((direction, 0), Some(coefs), position.clone())]
|
||||
}
|
||||
}
|
||||
fn _pawn_get_capture_deltas(&self) -> Vec<(i8, i8)> {
|
||||
}
|
||||
fn get_capture_rays(&self, position: &Position) -> Vec<Ray> {
|
||||
match self.piece_type {
|
||||
PieceType::Pawn => {
|
||||
let direction = self.color.get_direction();
|
||||
vec![(direction, 1), (direction, -1)]
|
||||
vec![
|
||||
Ray::new((direction, 1), Some(vec![1]), position.clone()),
|
||||
Ray::new((direction, -1), Some(vec![1]), position.clone()),
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +251,7 @@ impl Board {
|
||||
None => self.state.remove(&position),
|
||||
};
|
||||
}
|
||||
fn move_from_to(
|
||||
fn relocate(
|
||||
&mut self,
|
||||
source: &Position,
|
||||
target: Position,
|
||||
@@ -240,6 +277,17 @@ impl Board {
|
||||
pub fn iter(&self) -> impl Iterator<Item = (&Position, &Piece)> {
|
||||
self.state.iter()
|
||||
}
|
||||
fn cut_move_ray(&self, ray: Ray) -> impl Iterator<Item = Position> + '_ {
|
||||
ray.into_iter().take_while(|position| self.get_at(position).is_none())
|
||||
}
|
||||
fn find_capture(&self, ray: Ray) -> Option<Position> {
|
||||
// I'm not expecting to call this if there's no piece in the position
|
||||
let crt_piece = self.get_at(&ray.source).unwrap();
|
||||
ray.into_iter().find(|p| match self.get_at(p) {
|
||||
None => false,
|
||||
Some(piece) => piece.color != crt_piece.color,
|
||||
})
|
||||
}
|
||||
pub fn get_legal_moves(
|
||||
&self,
|
||||
position: &Position,
|
||||
@@ -249,16 +297,13 @@ impl Board {
|
||||
Some(piece) => piece,
|
||||
};
|
||||
let valid_moves = piece
|
||||
.get_moves(position)
|
||||
.get_move_rays(position)
|
||||
.into_iter()
|
||||
.filter(|pos| self.get_at(pos).is_none());
|
||||
let valid_captures =
|
||||
piece.get_captures(position).into_iter().filter(|pos| match self
|
||||
.get_at(pos)
|
||||
{
|
||||
None => false,
|
||||
Some(other_piece) => (other_piece.color != piece.color),
|
||||
});
|
||||
.flat_map(|ray| self.cut_move_ray(ray));
|
||||
let valid_captures = piece
|
||||
.get_capture_rays(position)
|
||||
.into_iter()
|
||||
.filter_map(|ray| self.find_capture(ray));
|
||||
Some(valid_moves.chain(valid_captures).collect())
|
||||
}
|
||||
pub fn make_move(
|
||||
@@ -270,7 +315,7 @@ impl Board {
|
||||
Err(())
|
||||
} else {
|
||||
// We checked that there is a piece at source in get_legal_moves
|
||||
self.move_from_to(source, target)
|
||||
self.relocate(source, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user