Skip to content

Commit 8d0a83a

Browse files
committed
implement legal_destinations_from
1 parent 5bdeb4a commit 8d0a83a

4 files changed

Lines changed: 221 additions & 40 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ Check CHANGELOG.md for details - all breaking changes should be marked as *BREAK
181181
Some of the improvements made since the fork:
182182
- Build times are _drastically_ improved. rust-analyzer actually works now (thanks KarelPeeters)
183183
- Checking the `status` of the `Board` is 2-3x faster for a fully populated board (thanks AlexanderHarrison)
184-
- Using `Game` is 10-20x faster for reasonably sized games when using the new `cache_game_state` feature (more for larger games). If you're in an embedded environment and can't tolerate the extra kb of memory, you can disable this feature.
184+
- Using `Game` is 10-20x faster for reasonably sized games when using the new `cache_game_state` feature (more for larger games). If you're in an embedded environment and can't tolerate the extra KB of memory, you can disable this feature.
185185
- Legality checking of unsanitized moves is 4-5x faster
186186
- `Game::make_move` now returns `Option<String>` with the SAN representation of the move. `Board::make_move` still returns a bool to avoid overhead in the hot path
187187
- Optional instrumentation added to `Game`, using [tracing](https://github.com/tokio-rs/tracing) - just use the `instrument_game` feature.

src/board.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,10 @@ impl Board {
854854
MoveGen::legal_unsanitized(self, m)
855855
}
856856

857+
pub fn legal_destinations_from(&self, square: Square) -> BitBoard {
858+
MoveGen::legal_destinations_from(self, square)
859+
}
860+
857861
/// Make a chess move onto a new board.
858862
///
859863
/// panic!() if king is captured.
@@ -1131,3 +1135,29 @@ fn test_null_move_en_passant() {
11311135
Board::from_str("rnbqkbnr/pppp2pp/8/4pP2/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 0").unwrap();
11321136
assert_eq!(start.null_move().unwrap(), expected);
11331137
}
1138+
1139+
#[test]
1140+
fn test_legal_destinations_from() {
1141+
use crate::magic::line;
1142+
let board = Board::default();
1143+
let expected = EMPTY;
1144+
assert_eq!(board.legal_destinations_from(Square::D1), expected);
1145+
let expected = BitBoard::from_square(Square::D3) | BitBoard::from_square(Square::D4);
1146+
assert_eq!(board.legal_destinations_from(Square::D2), expected);
1147+
let expected = BitBoard::from_square(Square::A3) | BitBoard::from_square(Square::C3);
1148+
assert_eq!(board.legal_destinations_from(Square::B1), expected);
1149+
1150+
let board =
1151+
Board::from_str("rnbqkbnr/pp1p1ppp/8/2pPp3/8/8/PPP1PPPP/RNBQKBNR w KQkq e6 0 3").unwrap();
1152+
let expected = BitBoard::from_square(Square::D6) | BitBoard::from_square(Square::E6);
1153+
let board = board.legal_destinations_from(Square::D5);
1154+
assert_eq!(board, expected); // en passant
1155+
1156+
let board = Board::from_str("8/2k5/8/8/4q3/8/1K6/8 w - - 2 35").unwrap();
1157+
let expected = (line(Square::E1, Square::E8)
1158+
| line(Square::A4, Square::H4)
1159+
| line(Square::B1, Square::H7)
1160+
| line(Square::A8, Square::H1))
1161+
& !BitBoard::from_square(Square::E4);
1162+
assert_eq!(board.legal_destinations_from(Square::E4), expected); // unrestricted queen
1163+
}

src/movegen/movegen.rs

Lines changed: 148 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -98,21 +98,21 @@ impl MoveGen {
9898
let mut movelist = NoDrop::new(ArrayVec::<[SquareAndBitBoard; 18]>::new());
9999

100100
if checkers == EMPTY {
101-
PawnType::legals::<NotInCheckType>(&mut movelist, &board, mask);
102-
KnightType::legals::<NotInCheckType>(&mut movelist, &board, mask);
103-
BishopType::legals::<NotInCheckType>(&mut movelist, &board, mask);
104-
RookType::legals::<NotInCheckType>(&mut movelist, &board, mask);
105-
QueenType::legals::<NotInCheckType>(&mut movelist, &board, mask);
106-
KingType::legals::<NotInCheckType>(&mut movelist, &board, mask);
101+
PawnType::legals::<NotInCheckType>(&mut movelist, &board, mask, None);
102+
KnightType::legals::<NotInCheckType>(&mut movelist, &board, mask, None);
103+
BishopType::legals::<NotInCheckType>(&mut movelist, &board, mask, None);
104+
RookType::legals::<NotInCheckType>(&mut movelist, &board, mask, None);
105+
QueenType::legals::<NotInCheckType>(&mut movelist, &board, mask, None);
106+
KingType::legals::<NotInCheckType>(&mut movelist, &board, mask, None);
107107
} else if checkers.popcnt() == 1 {
108-
PawnType::legals::<InCheckType>(&mut movelist, &board, mask);
109-
KnightType::legals::<InCheckType>(&mut movelist, &board, mask);
110-
BishopType::legals::<InCheckType>(&mut movelist, &board, mask);
111-
RookType::legals::<InCheckType>(&mut movelist, &board, mask);
112-
QueenType::legals::<InCheckType>(&mut movelist, &board, mask);
113-
KingType::legals::<InCheckType>(&mut movelist, &board, mask);
108+
PawnType::legals::<InCheckType>(&mut movelist, &board, mask, None);
109+
KnightType::legals::<InCheckType>(&mut movelist, &board, mask, None);
110+
BishopType::legals::<InCheckType>(&mut movelist, &board, mask, None);
111+
RookType::legals::<InCheckType>(&mut movelist, &board, mask, None);
112+
QueenType::legals::<InCheckType>(&mut movelist, &board, mask, None);
113+
KingType::legals::<InCheckType>(&mut movelist, &board, mask, None);
114114
} else {
115-
KingType::legals::<InCheckType>(&mut movelist, &board, mask);
115+
KingType::legals::<InCheckType>(&mut movelist, &board, mask, None);
116116
}
117117

118118
movelist
@@ -144,12 +144,12 @@ impl MoveGen {
144144
KingType::legals::<InCheckType>,
145145
]
146146
} else {
147-
KingType::legals::<InCheckType>(&mut movelist, &board, mask);
147+
KingType::legals::<InCheckType>(&mut movelist, &board, mask, None);
148148
return movelist.len() != 0;
149149
};
150150

151151
for f in legal_fns {
152-
f(&mut movelist, &board, mask);
152+
f(&mut movelist, &board, mask, None);
153153
if movelist.len() != 0 {
154154
return true;
155155
}
@@ -218,6 +218,66 @@ impl MoveGen {
218218
}
219219
}
220220

221+
pub fn legal_destinations_from(board: &Board, square: Square) -> BitBoard {
222+
let piece = board.piece_on(square);
223+
if piece.is_none() {
224+
return EMPTY;
225+
}
226+
227+
let piece = piece.unwrap();
228+
let checkers = *board.checkers();
229+
let mask = !board.color_combined(board.side_to_move());
230+
let mut movelist = NoDrop::new(ArrayVec::<[SquareAndBitBoard; 18]>::new());
231+
232+
if checkers == EMPTY {
233+
match piece {
234+
Piece::Pawn => {
235+
PawnType::legals::<NotInCheckType>(&mut movelist, &board, mask, Some(square))
236+
}
237+
Piece::Knight => {
238+
KnightType::legals::<NotInCheckType>(&mut movelist, &board, mask, Some(square))
239+
}
240+
Piece::Bishop => {
241+
BishopType::legals::<NotInCheckType>(&mut movelist, &board, mask, Some(square))
242+
}
243+
Piece::Rook => {
244+
RookType::legals::<NotInCheckType>(&mut movelist, &board, mask, Some(square))
245+
}
246+
Piece::Queen => {
247+
QueenType::legals::<NotInCheckType>(&mut movelist, &board, mask, Some(square))
248+
}
249+
Piece::King => {
250+
KingType::legals::<NotInCheckType>(&mut movelist, &board, mask, Some(square))
251+
}
252+
}
253+
} else if checkers.popcnt() == 1 {
254+
match piece {
255+
Piece::Pawn => {
256+
PawnType::legals::<InCheckType>(&mut movelist, &board, mask, Some(square))
257+
}
258+
Piece::Knight => {
259+
KnightType::legals::<InCheckType>(&mut movelist, &board, mask, Some(square))
260+
}
261+
Piece::Bishop => {
262+
BishopType::legals::<InCheckType>(&mut movelist, &board, mask, Some(square))
263+
}
264+
Piece::Rook => {
265+
RookType::legals::<InCheckType>(&mut movelist, &board, mask, Some(square))
266+
}
267+
Piece::Queen => {
268+
QueenType::legals::<InCheckType>(&mut movelist, &board, mask, Some(square))
269+
}
270+
Piece::King => {
271+
KingType::legals::<InCheckType>(&mut movelist, &board, mask, Some(square))
272+
}
273+
}
274+
} else {
275+
KingType::legals::<InCheckType>(&mut movelist, &board, mask, Some(square));
276+
}
277+
278+
movelist.iter().fold(EMPTY, |acc, x| acc | x.bitboard)
279+
}
280+
221281
pub fn legal_unsanitized(board: &Board, chess_move: ChessMove) -> bool {
222282
let piece = board.piece_on(chess_move.get_source());
223283
if piece.is_none() {
@@ -231,25 +291,85 @@ impl MoveGen {
231291

232292
if checkers == EMPTY {
233293
match piece {
234-
Piece::Pawn => PawnType::legals::<NotInCheckType>(&mut movelist, &board, mask),
235-
Piece::Knight => KnightType::legals::<NotInCheckType>(&mut movelist, &board, mask),
236-
Piece::Bishop => BishopType::legals::<NotInCheckType>(&mut movelist, &board, mask),
237-
Piece::Rook => RookType::legals::<NotInCheckType>(&mut movelist, &board, mask),
238-
Piece::Queen => QueenType::legals::<NotInCheckType>(&mut movelist, &board, mask),
239-
Piece::King => KingType::legals::<NotInCheckType>(&mut movelist, &board, mask),
294+
Piece::Pawn => PawnType::legals::<NotInCheckType>(
295+
&mut movelist,
296+
&board,
297+
mask,
298+
Some(chess_move.get_source()),
299+
),
300+
Piece::Knight => KnightType::legals::<NotInCheckType>(
301+
&mut movelist,
302+
&board,
303+
mask,
304+
Some(chess_move.get_source()),
305+
),
306+
Piece::Bishop => BishopType::legals::<NotInCheckType>(
307+
&mut movelist,
308+
&board,
309+
mask,
310+
Some(chess_move.get_source()),
311+
),
312+
Piece::Rook => RookType::legals::<NotInCheckType>(
313+
&mut movelist,
314+
&board,
315+
mask,
316+
Some(chess_move.get_source()),
317+
),
318+
Piece::Queen => QueenType::legals::<NotInCheckType>(
319+
&mut movelist,
320+
&board,
321+
mask,
322+
Some(chess_move.get_source()),
323+
),
324+
Piece::King => KingType::legals::<NotInCheckType>(
325+
&mut movelist,
326+
&board,
327+
mask,
328+
Some(chess_move.get_source()),
329+
),
240330
}
241331
} else if checkers.popcnt() == 1 {
242332
match piece {
243-
Piece::Pawn => PawnType::legals::<InCheckType>(&mut movelist, &board, mask),
244-
Piece::Knight => KnightType::legals::<InCheckType>(&mut movelist, &board, mask),
245-
Piece::Bishop => BishopType::legals::<InCheckType>(&mut movelist, &board, mask),
246-
Piece::Rook => RookType::legals::<InCheckType>(&mut movelist, &board, mask),
247-
Piece::Queen => QueenType::legals::<InCheckType>(&mut movelist, &board, mask),
248-
Piece::King => KingType::legals::<InCheckType>(&mut movelist, &board, mask),
333+
Piece::Pawn => PawnType::legals::<InCheckType>(
334+
&mut movelist,
335+
&board,
336+
mask,
337+
Some(chess_move.get_source()),
338+
),
339+
Piece::Knight => KnightType::legals::<InCheckType>(
340+
&mut movelist,
341+
&board,
342+
mask,
343+
Some(chess_move.get_source()),
344+
),
345+
Piece::Bishop => BishopType::legals::<InCheckType>(
346+
&mut movelist,
347+
&board,
348+
mask,
349+
Some(chess_move.get_source()),
350+
),
351+
Piece::Rook => RookType::legals::<InCheckType>(
352+
&mut movelist,
353+
&board,
354+
mask,
355+
Some(chess_move.get_source()),
356+
),
357+
Piece::Queen => QueenType::legals::<InCheckType>(
358+
&mut movelist,
359+
&board,
360+
mask,
361+
Some(chess_move.get_source()),
362+
),
363+
Piece::King => KingType::legals::<InCheckType>(
364+
&mut movelist,
365+
&board,
366+
mask,
367+
Some(chess_move.get_source()),
368+
),
249369
}
250370
} else {
251371
if matches!(piece, Piece::King) {
252-
KingType::legals::<InCheckType>(&mut movelist, &board, mask);
372+
KingType::legals::<InCheckType>(&mut movelist, &board, mask, None);
253373
}
254374
};
255375

src/movegen/piece_type.rs

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,25 @@ pub trait PieceType {
1616
fn into_piece() -> Piece;
1717
fn pseudo_legals(src: Square, color: Color, combined: BitBoard, mask: BitBoard) -> BitBoard;
1818
#[inline(always)]
19-
fn legals<T>(movelist: &mut MoveList, board: &Board, mask: BitBoard)
20-
where
19+
fn legals<T>(
20+
movelist: &mut MoveList,
21+
board: &Board,
22+
mask: BitBoard,
23+
from_square: Option<Square>,
24+
) where
2125
T: CheckType,
2226
{
2327
let combined = board.combined();
2428
let color = board.side_to_move();
2529
let my_pieces = board.color_combined(color);
2630
let ksq = board.king_square(color);
2731

28-
let pieces = board.pieces(Self::into_piece()) & my_pieces;
32+
let from_mask = match from_square {
33+
Some(from) => BitBoard::from_square(from),
34+
None => *my_pieces,
35+
};
36+
37+
let pieces = board.pieces(Self::into_piece()) & from_mask;
2938
let pinned = board.pinned();
3039
let checkers = board.checkers();
3140

@@ -127,16 +136,25 @@ impl PieceType for PawnType {
127136
}
128137

129138
#[inline(always)]
130-
fn legals<T>(movelist: &mut MoveList, board: &Board, mask: BitBoard)
131-
where
139+
fn legals<T>(
140+
movelist: &mut MoveList,
141+
board: &Board,
142+
mask: BitBoard,
143+
from_square: Option<Square>,
144+
) where
132145
T: CheckType,
133146
{
134147
let combined = board.combined();
135148
let color = board.side_to_move();
136149
let my_pieces = board.color_combined(color);
137150
let ksq = board.king_square(color);
138151

139-
let pieces = board.pieces(Self::into_piece()) & my_pieces;
152+
let from_mask = match from_square {
153+
Some(from) => BitBoard::from_square(from),
154+
None => *my_pieces,
155+
};
156+
157+
let pieces = board.pieces(Self::into_piece()) & from_mask;
140158
let pinned = board.pinned();
141159
let checkers = board.checkers();
142160

@@ -224,16 +242,25 @@ impl PieceType for KnightType {
224242
}
225243

226244
#[inline(always)]
227-
fn legals<T>(movelist: &mut MoveList, board: &Board, mask: BitBoard)
228-
where
245+
fn legals<T>(
246+
movelist: &mut MoveList,
247+
board: &Board,
248+
mask: BitBoard,
249+
from_square: Option<Square>,
250+
) where
229251
T: CheckType,
230252
{
231253
let combined = board.combined();
232254
let color = board.side_to_move();
233255
let my_pieces = board.color_combined(color);
234256
let ksq = board.king_square(color);
235257

236-
let pieces = board.pieces(Self::into_piece()) & my_pieces;
258+
let from_mask = match from_square {
259+
Some(from) => BitBoard::from_square(from),
260+
None => *my_pieces,
261+
};
262+
263+
let pieces = board.pieces(Self::into_piece()) & from_mask;
237264
let pinned = board.pinned();
238265
let checkers = board.checkers();
239266

@@ -344,8 +371,12 @@ impl PieceType for KingType {
344371
}
345372

346373
#[inline(always)]
347-
fn legals<T>(movelist: &mut MoveList, board: &Board, mask: BitBoard)
348-
where
374+
fn legals<T>(
375+
movelist: &mut MoveList,
376+
board: &Board,
377+
mask: BitBoard,
378+
from_square: Option<Square>, // ignored
379+
) where
349380
T: CheckType,
350381
{
351382
let combined = board.combined();

0 commit comments

Comments
 (0)