Chess-Plisco
view release on metacpan or search on metacpan
lib/Chess/Plisco/Tutorial.pod view on Meta::CPAN
Task: Print the square for every white pawn on the board.
=head4 Conventional and Slow Approach
Iterating over a bitboard can be done in an intuitive and straight-forward way:
use integer;
use Chess::Plisco qw(:all);
use Chess::Plisco::Macro;
my $white_pawn_mask = cp_pos_white_pieces($pos) & cp_pos_pawns($pos);
foreach my $shift (0 .. 63) {
my $shift_mask = 1 << $shift;
if ($shift_mask & $white_pawn_mask) {
my $square = cp_shift_to_square $shift;
print "There is a white pawn on $square.\n";
}
}
You basically shift a 1 bit subsequently to the left, and then test whether the
corresponding bit is set in the bitboard. There is nothing wrong with this
approach and it is actually reasonably fast.
=head4 Efficient and Fast Approach
But there is a another technique that achieves the same result and is often
faster:
use integer;
use Chess::Plisco qw(:all);
use Chess::Plisco::Macro;
my $pos = Chess::Plisco->new;
my $white_pawn_mask = cp_pos_white_pieces($pos) & cp_pos_pawns($pos);
while ($white_pawn_mask) {
my $shift = cp_bitboard_count_trailing_zbits $shift_mask;
my $square = cp_shift_to_square $shift;
print "There is a white pawn on $square.\n";
$white_pawn_mask = cp_bitboard_clear_least_set $white_pawn_mask;
}
If you count the trailing zero bits with the macro
C<cp_bitboard_count_trailing_zbits()> you get the position of that bit in the
bitboard and you can now use that information for whatever you want to do.
At the end of the loop body, you clear the least significant bit of the
bitboard. Therefore, the population count of the bitboard decreases by
one with each iteration.
If you want to benchmark both approaches, make sure to remove the expensive
operations inside the loop body, that is the call to C<cp_shift_to_square()>
and especially the print to the console.
What you will find out is that the second approach runs around 10-15 %
faster than the conventional approach. On the one hand you have less
iterations (8 vs. 64) but masking out the bits and especially counting the
trailing zero bits outweighs that mostly.
But if you replace "pawn" with "king" in the code, the 2nd variation runs
around 700 % or seven times faster than the conventional approach. This is
because you always have exactly one loop iteration instead of 64. And in
the case of kings you could improve that even further by taking advantage of
the fact that there is always exactly one white king on the board. So you
do not even need a loop.
Most of the time, the bitboards you are dealing with are sparsely populated
and it is advantageous to take the second approach. Only in the exceptional
case that you want to iterate over a bitboard with all pieces of one colour
or even all pieces of both colours, you are better off using the conventional
approach.
=head2 Game Play
=head3 Initializing a Position
You use the constructor to instantiate a chess position. For the start
position you call it with arguments. If you have the position represented
as a string in Forsyth-Edwards Notation (FEN) you pass that as an
argument to the constructor:
$pos = Chess::Plisco->new;
# Or:
$fen = 'r4rk1/1p3pp1/1q2b2p/1B2R3/1Q2n3/1K2PN2/1PP3PP/7R w - - 3 22';
$pos = Chess::Plisco->new($fen);
=head3 Making Moves
Applying a move to a position usually requires first parsing a chess move
as a string into the internal representation. You then pass that move
(which is just an integer) to the method C<doMove()>:
# Parse the move in one of the supported formats.
$move = $pos->parseMove('Nc3');
$move = $pos->parseMove('Nb1-c3');
$move = $pos->parseMove('b1c3');
$state = $pos->doMove($move) or die "illegal move";
The method C<doMove()> returns a reference to an array containing state
information needed to undo the move later. If the move was illegal,
the method returns a falsy value.
=head3 Undoing Moves
You will often find that you have to undo a move so that the position is
reverted to the state before the move had been played.
In general, there are two strategies for undoing moves in chess software.
The brute force approach is to make a deep copy of the position and revert
by copying back later, resp. by simply applying the move to the copy only and
throwing it away.
The other possibility is to undo all modifications made by applying the
move. This is, of course, more complicated but often faster than the brute
force approach.
( run in 1.640 second using v1.01-cache-2.11-cpan-71847e10f99 )