NAME

Chess::Plisco - A comprehensive chess library for Perl

SYNOPSIS

use Chess::Plisco(:all);

$pos = Chess::Plisco->new;
$pos = Chess::Plisco->new('k7/8/8/8/8/8/8/7K w - - 0 1');

DESCRIPTION

Chess::Plisco is a comprehensive chess library for Perl, aiming at being as fast and efficient as possible for a scripting language. It is also somewhat opinionated but this is not an end in itself but owed to its intention of being fast and efficient. In doubt, flexibility is sacrificed for performance and efficiency.

The library features:

bitboards for board representation
macros/inline functions for often used computations
legality checks for moves
magic bitboards for generation of sliding piece moves and attacks
handling of moves in Standard-Algebraic Notation (SAN) as well as coordinate notation
FEN (Forsyth-Edwards Notation) import and export
EPD (Extended Position Notation) parser
Static Exchange Evaluation (SEE)
Zobrist Keys
Syzygy endgame table probing

For a gentler introduction, please see Chess::Plisco::Tutorial. The rest of this document contains reference documentation only.

If performance is key for you, you are strongly advised to have a look at Chess::Plisco::Macro which documents macros resp. inline functions that speed up tasks that can be done with Chess::Plisco significantly.

The class exports a number of constants that can either be imported individually, by export tag, or all at once by the export tag ':all'. All constants are prefixed with 'CP_' and you will have little reason to not import all constants.

Internals

An instance of a Chess::Plisco is a blessed array reference. You can access its properties through accessor macros or by using constants for the array indices.

A move in Chess::Plisco is a regular scalar, more precisely an unsigned integer. You can access its properties with the move methods described below.

It is guaranteed that every legal chess move is represented by a non-zero integer. It is therefore safe to use moves in boolean context.

Terminology

For the sake of brevity, this document uses the following terms without further explanation:

Square

A square is a square of the chess board as a string like "e4" or "f7".

Coordinates

Coordinates are a pair of a file (0-7) and a rank (0-7).

Shift

A "shift" is an integer in the range of 0-63 where 0 is the shift for "a1" and 63 is the shift for "h8".

Bitboard

A bitboard is an unsigned 64-bit integer. Each bit stands for one square of the chess board.

Shift Mask

A shift mask is a bitboard with exactly one bit set. The shift mask representing "e4" is a 1 shifted left 28 bits, because the shift for "e4" is 28.

Move

When an argument is called "move", it is really an integer representing a chess move.

Notation

When an argument is called "notation", it is a supported notation of a chess move, either Standard-Algebraic Notation SAN or coordinate notation.

Limitations

Chess::Plisco requires 64-bit support for Perl. It will not run on 32-bit Perls.

Similar Software

Chess::Rep and Chess::Play provide similar functionality. Both compile a lot faster than Chess::Plisco but once compiled, a perft test of Chess::Plisco runs more than 30 times faster than one of Chess::Play and more than 100 times faster than one of Chess::Rep.

At the time of this writing, Chess::Plisco also outperforms python-chess by about 50 %.

CONSTRUCTORS

new([FEN][, RELAXED])

Creates a new Chess::Plisco instance that represents the starting position of standard chess.

If an argument is passed to the constructor, it is interpreted as a position in Forsyth-Edwards Notation (FEN). This has the same effect as using the constructor newFromFEN.

Beginning with version v0.8.0, you can now pass a second parameter RELAXED that disables a lot of legality checks on the position.

newFromFEN(FEN[, RELAXED])

Creates a new Chess::Plisco instance from a string containing the Forsyth-Edwards Notation (FEN) of a chess position. The only difference to new() is that the string argument is required.

This constructor may throw an exception if the described position does not meet the requirements of the library.

All legal chess positions meet the requirements of the library. But positions that are not legal and may cause the library to malfunction, are rejected.

Beginning with version v0.8.0, you can now pass a second parameter RELAXED that disables a lot of legality checks on the position.

copy(POSITION)

Clones POSITION.

METHODS

General Methods

FEN[, OPTIONS]
toFEN[, OPTIONS]
fen[, OPTIONS]

Renders the position to Forsyth-Edwards Notation. Alternatively, you can just use the object as a string:

$position = Chess::Plisco->new;
say $position->FEN;

OPTIONS is an optional hash (or hash reference). The only supported property at the moment is "force_en_passant_square" (alias "force_ep_square"). The original FEN standard stated that the en passant square in a FEN string should be set regardless of whether a pawn capture is possible respectively legal. The de-facto standard today is that the square should be omitted if there is no legal en passant capture in the particular position. Passing a truthy value will set the en-passant square even if there is no opponent pawn on a square that is able to capture or if capturing would leave the own king in check. The default is to do a legality check. Important! The behaviour in versions prior to 1.x was to follow the old convention.

You should also keep in mind that the library internally uses the old convention. In other words, a pawn push by two squares will always set the en-passant shift of the position object.

legalMoves

Returns a list of legal moves for the current position. A move is just an integer.

inCheck

Returns false if the side to move is not in check, a truthy value otherwise. The truthy value returned is a bitboard of all pieces giving check.

In array context, the method returns extended information about the check that is required by checkPseudoLegalMove, see below. In detail, this is the bitboard of all pieces giving check, the position of the king (0-63), and a so-called defence bitboard (64 bits). To understand the defence bitboard, you have to recall how a check can be defended:

1. The king moves out of check, eventually capturing the attacker. 2. Another piece captures the attacker. 3. Another piece moves between the piece giving check and the king.

Option 1 will always work. In this case, the defence bitboard will be 0 (and can be ignored).

Option 2 only works if there is just one piece giving check. In this case, the defence bitboard is identical to the bitboard of the pieces giving check.

Option 3 only works if a sliding piece (queen, rook, or bishop) gives check. Knights jump over other pieces and a pawn can only give check from an adjacent square. In this case, the defence bitboard includes all squares between the king and the piece giving check, including the square of the attacking piece.

pseudoLegalMoves

Generates all pseudo-legal moves for the current position.

Pseudo-legal are all moves that can be executed by the pieces on the side to move ignoring whether the side to move is in check after the move.

The only reason why you want to call pseudoLegalMoves is performance because filtering out those moves that will leave the king in check is somewhat expensive. In a search with alpha beta pruning, it is more efficient to delay that check because the engine may decide to prune that move and not try it out.

For a concrete implementation, it is helpful to know how legalMoves() is implemented:

sub legalMoves {
    my ($self) = @_;

    my @check_info = $self->inCheck($self);

    my @legal;
    foreach my $move ($self->pseudoLegalMoves) {
        $move = $self->checkPseudoLegalMove($move, @check_info) or next;
        push @legal, $move;
    }

    return @legal;
}

The method calls inCheck() in list context and passes that information to the legality checker.

pseudoLegalAttacks

Like "pseudoLegalMoves" but only returns "interesting" moves. Interesting moves are captures and promotions. Moves giving check would also be interesting but they are currently not generated.

You will want to call this method for generating moves in a quiescence search.

moveNumbers

Class method that returns an array of all theoretically possible moves in standard chess. These move numbers have the following structure:

colour (1 bit): Either "CP_BLACK" or "CP_WHITE".
capture (3 bits): The captured piece if any, one of "CP_NONE", "CP_PAWN", "CP_KNIGHT", "CP_BISHOP", "CP_ROOK", "CP_QUEEN", or (!) "CP_KING", see below.
mover (3 bits): The piece that move, one of "CP_PAWN", "CP_KNIGHT", "CP_BISHOP", "CP_ROOK", "CP_QUEEN", or <L/CP_KING>.
promote (3 bits): The piece that is promoted to if any, one of "CP_KNIGHT", "CP_BISHOP", "CP_ROOK", or "CP_QUEEN".
from (6 bits): The starting square of the piece as a bit shift. In case of castling, this is the king's starting square.
to (6 bits): The destination square of the piece as a bit shift. In case of castling, this is the king's destination square.

If the captured piece is encoded as a king, it is really a pawn that gets captured en passant.

The size of this array is 45356, see this document at the chess programming wiki.

equals(POSITION)

Returns true if the current position is equivalent to POSITION.

Methods for Accessing Position Properties

whitePieces

Returns the bitboard of all white pieces.

blackPieces

Returns the bitboard of all black pieces.

occupied

Returns the bitboard of all squares that are occupied by any piece.

vacant

Returns the bitboard of all squares that are not occupied by any piece.

kings

Returns the bitboard of all kings (black and white).

queens

Returns the bitboard of all queens (black and white).

rooks

Returns the bitboard of all rooks (black and white).

bishops

Returns the bitboard of all bishops (black and white).

knights

Returns the bitboard of all knights (black and white).

pawns

Returns the bitboard of all pawns (black and white).

turn
toMove

Returns the side to move, either "CP_BLACK" or "CP_WHITE".

halfmoves

Returns the number of half-moves made. Initially, this is 0. After white has made their first move, it is 1. After black has made their first move, it is 2, and so on.

castlingRights

Returns the castling state of the position. This is a bitmask with four bits. Bit 1 is set if white can castle king-side. Bit 2 is set if white can castle queen-side. Bit 3 is set if black can castle king-side. Bit 4 is set if black can castle queen-side.

halfmoveClock

Number of subsequent irreversible halfmoves. Irreversible moves in this sense are all captures and pawn moves. Changes to the castling state are ignored.

The half-move-clock is important because both players can claim a draw, when the half-move clock has reached 100.

signature

Returns a 64-bit Zobrist key (aka 64-bit integer) that identifies the position. Properties taken into account are:

piece positions
colour to move
castling rights
en-passant status

Note that hash collisions albeit unlikely, may occur because 64 bit are, of course, not sufficient to uniquely identify a chess position.

The signature computed for a certain position is guaranteed not to change for one release of Chess::Plisco. In order to compute different signatures, you have to override either the pseudo-random number generator "RNG" or its seed "CP_RANDOM_SEED". If you turn the constant "CP_RANDOM_SEED" into a non-constant subroutine, you can also get different signatures, whenever you re-load the library.

The en-passant status only takes a double pawn push into account and does not check whether an en-passant capture is actually possible or legal.

enPassantShift

Returns the shift of the en passant square or 0 if en passant is not possible 0 happens to be the shift for a1 but since en passant is never possible on a1, this is not a problem.

Note that this returns a non-zero value even if an en-passant capture is not possible or not legal. It is sufficient that a pawns had been pushed two squares in the last move.

material

Gives the material balance from the perspective of the white player. That means that all white pieces have a positive value, and all black pieces have a negative value.

The values are:

"CP_QUEEN_VALUE" for each queen
"CP_ROOK_VALUE" for each rook
"CP_BISHOP_VALUE" for each bishop
"CP_KNIGHT_VALUE" for each knight
"CP_PAWN_VALUE" for each pawn

These constants can be overridden by inheriting from Chess::Plisco.

Note: Kings do not count!

whiteKingSideCastlingRight

Returns a truthy value if white still has the right to castle king-side ("O-O"), false otherwise.

whiteQueenSideCastlingRight

Returns a truthy value if white still has the right to castle queen-side, ("O-O-O"), false otherwise.

blackKingSideCastlingRight

Returns a truthy value if black still has the right to castle king-side ("O-O"), false otherwise.

blackQueenSideCastlingRight

Returns a truthy value if black still has the right to castle queen-side, ("O-O-O"), false otherwise.

castlingRights

Returns a bitmap of castling rights for the current position:

0x1 is set if white can still castle king-side
0x2 is set if white can still castle queen-side
0x4 is set if black can still castle king-side
0x8 is set if black can still castle queen-side
kingShift

Return the shift of the square of the king of the side to move.

lastMove

Returns the move (as an integer) that has led to the position. It may be 0, when there was no prior move.

Move Methods

parseMove(NOTATION[, PSEUDO_LEGAL])

Parses the string NOTATION into an integer representing the move or returns false, if the NOTATION cannot be parsed or is an illegal move. Note that the parser does not do a complete legality check. Use "applyMove()" to test whether the move is really legal.

NOTATION can either be a move in Standard Algebraic Notation (SAN) or in coordinate notation. Coordinate notation is the format used by most chess engines and is the concatenation of the start and destination square and a possible promotion piece, for example "e2e4" or "f2f1q".

IMPORTANT! Beginning with version 0.5, NOTATION is case-sensitive! This incompatible change was necessary because a move like "B2d4" may otherwise be ambiguous.

Beginning with version v0.8.0, you can pass an optional flag PSEUDO_LEGAL. If it has a truthy value, the move is not checked for legality.

SAN(MOVE)
san(MOVE)

Renders the integer MOVE into Standard-Algebraic Notation SAN, for example "e4", "Bxc4", "O-O", or "fxe1=Q".

moveCoordinateNotation(MOVE)

Renders the integer MOVE into coordinate notation, for example "e2e4" or "f2f1q".

LAN(MOVE[, OPTIONS])
lan(MOVE[, OPTIONS])

Returns the Long Algebraic Notation LAN of the move. The following optional options (passed as a hash or hash reference) are supported:

no_hyphen

Use a hyphen between the from and to square ("e2e4" instead of "e2-e4").

encode_pawn

Add a leading "P" for pawn moves, i.e. "Pe2-e4" instead of "e2-e4".

Before version 1.x this was an alias for "moveCoordinateNotation".

moveEquivalent(MOVE1, MOVE2)

Returns true if the significant parts of MOVE1 and MOVE2 are equivalent. This is the case, when the start and destination square, and a possible promotion piece are equal.

Note that the piece that moves is not significant because it can be retrieved from the position. Nevertheless, the piece that moves is set by the methods "pseudoLegalMoves" and "pseudoLegalAttacks" on the moves returned. If you store a best move in a transposition table make sure to keep that in mind, when you compare moves.

Although, this is technically a class method you should keep in mind that the redundant parts of the moves depend on the current position.

moveSignificant(MOVE)

Returns MOVE with all insignificant bits stripped off. See "moveEquivalent" for what is considered significant.

Although, this is technically a class method you should keep in mind that the redundant parts of the moves depend on the current position.

moveFrom(MOVE)

Extracts the shift (0-63) of the starting square.

moveSetFrom(MOVE, FROM)

Sets the shift (0-63) of the starting square in MOVE to FROM and returns the move.

moveTo(MOVE)

Extracts the shift (0-63) of the destination square.

moveSetTo(MOVE, TO)

Sets the shift (0-63) of the destination square in MOVE to TO and returns the move.

movePromote(MOVE)

Extracts the piece that a pawn is promoted to if the move is a promotion. Returns either "CP_QUEEN", "CP_ROOK", "CP_BISHOP", or "CP_KNIGHT".

moveSetPromote(MOVE, PROMOTE)

Sets the piece to promote to in MOVE to PROMOTE and returns the move. The piece should be one of "CP_QUEEN", "CP_ROOK", "CP_BISHOP", or "CP_KNIGHT".

movePiece(MOVE)

Extracts the piece that does the move. Returns one of "CP_KING", "CP_QUEEN", "CP_ROOK", "CP_BISHOP", "CP_KNIGHT", or "CP_PAWN".

moveSetPiece(MOVE, PIECE)

Sets the piece that moves in MOVE to PIECE and returns the move. The piece should be one of "CP_KING", "CP_QUEEN", "CP_ROOK", "CP_BISHOP", "CP_KNIGHT", or "CP_PAWN".

moveCaptured(MOVE)

Extracts the piece that gets captured if any. Returns one of "CP_QUEEN", "CP_ROOK", "CP_BISHOP", "CP_KNIGHT", "CP_PAWN", "CP_NO_PIECE".

moveSetCaptured(MOVE, PIECE)

Sets the piece that gets captured in MOVE to PIECE and returns the move. The piece should be one of "CP_QUEEN", "CP_ROOK", "CP_BISHOP", "CP_KNIGHT", "CP_PAWN", "CP_NO_PIECE".

moveColour(MOVE)
moveColor(MOVE)

Extracts the colour that does the move.

moveSetColour(MOVE, COLOUR)
moveSetColor(MOVE, COLOR)

Sets the colour in MOVE to COLOUR and returns the move.

moveEnPassant(MOVE)

Returns 1 if the move is an en passant capture, 0 otherwise.

moveSetEnPassant(MOVE, FLAG)

Sets the en passant flag in MOVE to FLAG and returns the move.

checkPseudoLegalMove(MOVE)

Checks the legality of MOVE (integer). The method only makes sense if you pass it a move that was produced by pseudoLegalMoves. Otherwise, the behaviour is undefined.

The return value is falsy, if the legality check failed. Otherwise the modified move is returned. The difference to the original move is that the captured piece, the colour, and possible the en passant flag are set.

moveLegal(MOVE|NOTATION)

Returns a truthy value, when the argument is a valid and legal move. The move can be given either as an integer returned by "parseMove" or as a string that is accepted by "parseMove".

applyMove(MOVE|NOTATION)

Parses the move given in NOTATION and applies it to the position if the move is valid and legal. Otherwise false is returned. You can also pass the move as an integer as returned by "parseMove".

The method returns state information that can later be used to undo the move and reset the position to the state it had before the move was applied.

Engines should use the method "move" instead because it is faster.

unapplyMove(STATE)

If STATE is state information returned by "applyMove", takes back the move that was given as an argument to "applyMove".

doMove(MOVE)

Applies the move represented by the integer(!) MOVE to the position. If the move is legal, the method returns state information that can be used to undo the move with "undoMove".

The method returns false, if the move is illegal. Note that this is not a complete legality check but works only for those moves that "pseudoLegalMoves" has returned for the current position.

The method returns state information that can later be used to undo the move and reset the position to the state it had before the move was applied.

undoMove(STATE)

If STATE is state information returned by "doMove", takes back the move that was given as an argument to "doMove".

move(MOVE)

Does the move MOVE which must be legal because no legality check is performed.

Unlike doMove(), the method does not return state information that can be used to later undo the move. The caller is responsible for that. A typical pattern looks like this:

my @backup = @$position; # A shallow copy is sufficient.
foreach my $move ($position->legalMoves) {
    my $state = $position->move($move);
    # ... do something with $position.
    @$position = @backup;
}

Consequently, there is no unmove() method.

SEE(MOVE)

Does a static exchange evaluation SEE for move MOVE. MOVE must be a capture, a promotion, a move giving check, or any combination of it. It returns the raw material balance expected from the move.

The routine assumes, that after MOVE had been made, all moves that re-capture on the target field of MOVE will be executed, starting with the least valuable attacker of each side proceeding to the most valuable attacker. As soon as a re-capture becomes disadvantageous, the sequence stops, and the balance up to that point is returned.

Disadvantageous means that advancing to the "next round" would make the result worse. For example a bishop would not normally capture a pawn that is protected.

If you assign different values to bishops and knights by overriding "CP_KNIGHT_VALUE" and "CP_BISHOP_VALUE" you may receive small values by this routine, if the exchange of bishops and knights is involved. Depending on your preferences you may ignore absolute values under a certain threshold, for example 100 centipawns.

It should also be noted that the routine assumes that the values of pieces follows this relation:

pawn < knight <= bishop < rook < queen (< king)

Methods for Converting Locations

squareToShift(SQUARE)

Converts SQUARE to a shift.

This is a class method.

squareToCoordinates(SQUARE)

Converts SQUARE to coordinates. It returns a list, not a an array reference. Be sure to call it in array context!

This is a class method.

shiftToSquare(SHIFT)

Converts SHIFT to a square.

This is a class method.

shiftToCoordinates(SHIFT)

Converts a shift to coordinates. It returns a list, not a an array reference. Be sure to call it in array context!

This is a class method.

coordinatesToSquare(FILE, RANK)

Converts coordinates to a square.

This is a class method.

coordinatesToShift(FILE, RANK)

Converts coordinates to a shift.

This is a class method.

Methods for Inspecting a Square

The following methods answer the question which piece of which colour occupies a particular location on the chess board.

pieceAtSquare(SQUARE)

In array context returns a pair of a piece and a colour. In scalar context, only the piece is returned. The piece is one of "CP_PAWN", "CP_KNIGHT", "CP_BISHOP", "CP_ROOK", "CP_QUEEN", "CP_KING", or "CP_NO_PIECE" if the square is empty.

The colour is one of "CP_BLACK" or "CP_WHITE". If the square is empty, undef is returned instead of a colour.

This method is relatively expensive!

pieceAtCoordinates(FILE, RANK)

In array context returns a pair of a piece and a colour. In scalar context, only the piece is returned. The piece is one of "CP_PAWN", "CP_KNIGHT", "CP_BISHOP", "CP_ROOK", "CP_QUEEN", "CP_KING", or "CP_NO_PIECE" if the square is empty.

The colour is one of "CP_BLACK" or "CP_WHITE". If the square is empty, undef is returned instead of a colour.

This method is relatively expensive!

pieceAtShift(SHIFT)

In array context returns a pair of a piece and a colour. In scalar context, only the piece is returned. The piece is one of "CP_PAWN", "CP_KNIGHT", "CP_BISHOP", "CP_ROOK", "CP_QUEEN", "CP_KING", or "CP_NO_PIECE" if the square is empty.

The colour is one of "CP_BLACK" or "CP_WHITE". If the square is empty, undef is returned instead of a colour.

This method is relatively expensive!

Analysis Methods

These methods can be used to analyze features of the current position.

insufficientMaterial [FORCIBLE]

Returns true if none of the two sides has sufficient material to deliver checkmate, false otherwise.

The exact behaviour is controlled by the optional flag FORCIBLE which was introduced in version 0.7. By default, the method only reports a draw if a checkmate is logically impossible. This is in line with the rules of chess. So if you write a chess GUI, you will call the method without the FORCIBLE flag for signalling "game over".

But there are also positions, wherea checkmate can be delivered but only, with the help of the opponent. The following table illustrates different cases:

+-----+-------+---------+----------+-----------------------+
|     | Pcs.  | Default | FORCIBLE | Bishop pair           |
+-----+-------+---------+----------+-----------------------+
| 1.  | KNvKN | no draw | draw     |                       |
| 2.  | KNvKB | no draw | draw     |                       |
| 3.  | KBvKN | no draw | draw     |                       |
| 4.  | KNNvK | no draw | draw     |                       |
| 5a. | KBvKB | no draw | no draw  | different-coloured    |
| 5b. | KBvKB | draw    | draw     | same-coloured         |
| 6a. | KBBvK | no draw | no draw  | different-coloured    |
| 6b. | KBBvK | draw    | draw.    | same-coloured         |
| 7.  | KPvP  | no draw | no draw  |                       |
+-----+-------+---------+----------+-----------------------+

Let's go over all cases. Black is always on move.

1. King and Knight versus King and Knight

A mate is possible but only with the help of the opponent:

  a b c d e f g h
 +-+-+-+-+-+-+-+-+
8|. . . . . . n k|8
7|. . . . . . . .|7
6|. . . . . . N K|6
5|. . . . . . . .|5
4|. . . . . . . .|4
3|. . . . . . . .|3
2|. . . . . . . .|2
1|. . . . . . . .|1
 +-+-+-+-+-+-+-+-+

The black knight prevents the king from escaping.

2. King and Knight versus King and Bishop
  a b c d e f g h
 +-+-+-+-+-+-+-+-+
8|. . . . . . b k|8
7|. . . . . . . .|7
6|. . . . . . N K|6
5|. . . . . . . .|5
4|. . . . . . . .|4
3|. . . . . . . .|3
2|. . . . . . . .|2
1|. . . . . . . .|1
 +-+-+-+-+-+-+-+-+

Almost the same as before but now the black bishop helps by blocking the black king.

3. King and Bishop versus King and Knight
  a b c d e f g h
 +-+-+-+-+-+-+-+-+
8|. . . . . . n k|8
7|. . . . . . . .|7
6|. . . . . B . K|6
5|. . . . . . . .|5
4|. . . . . . . .|4
3|. . . . . . . .|3
2|. . . . . . . .|2
1|. . . . . . . .|1
 +-+-+-+-+-+-+-+-+

Now the white bishop delivers check, and the black knight helps by being in the way of the black king.

4. King and Two Knights versus Lone King
  a b c d e f g h
 +-+-+-+-+-+-+-+-+
8|. . . . . . . k|8
7|. . . . . K . .|7
6|. . . . . N N .|6
5|. . . . . . . .|5
4|. . . . . . . .|4
3|. . . . . . . .|3
2|. . . . . . . .|2
1|. . . . . . . .|1
 +-+-+-+-+-+-+-+-+

This mate can also not be forced. The black king has to help by moving into a corner.

5a. King and Bishop versus King and Different-coloured Bishop
  a b c d e f g h
 +-+-+-+-+-+-+-+-+
8|. . . . . . b k|8
7|. . . . . . . .|7
6|. . . . . B . K|6
5|. . . . . . . .|5
4|. . . . . . . .|4
3|. . . . . . . .|3
2|. . . . . . . .|2
1|. . . . . . . .|1
 +-+-+-+-+-+-+-+-+

The mate cannot be forced. The black bishop has to help.

5b. King and Bishop versus King and Same-coloured Bishop
  a b c d e f g h
 +-+-+-+-+-+-+-+-+
8|. . . . . b . k|8
7|. . . . . . . .|7
6|. . . . . B . K|6
5|. . . . . . . .|5
4|. . . . . . . .|4
3|. . . . . . . .|3
2|. . . . . . . .|2
1|. . . . . . . .|1
 +-+-+-+-+-+-+-+-+

A mate is not possible. The position is always a draw.

6a. King and Different-coloured Bishop Pair versus Lone King
  a b c d e f g h
 +-+-+-+-+-+-+-+-+
8|. . . . . . . k|8
7|. . . . . B . .|7
6|. . . . . B . K|6
5|. . . . . . . .|5
4|. . . . . . . .|4
3|. . . . . . . .|3
2|. . . . . . . .|2
1|. . . . . . . .|1
 +-+-+-+-+-+-+-+-+

This mate can be forced and the constellation is therefore never a draw.

6a. King and Same-coloured Bishop Pair versus Lone King

This constellation can only be achieved by an underpromotion of a pawn to a bishop. The closest you can get to a checkmate is something like this:

  a b c d e f g h
 +-+-+-+-+-+-+-+-+
8|. . . . . K . k|8
7|. . . . . . . .|7
6|. . . . . B . B|6
5|. . . . . . . .|5
4|. . . . . . . .|4
3|. . . . . . . .|3
2|. . . . . . . .|2
1|. . . . . . . .|1
 +-+-+-+-+-+-+-+-+

The black king can still escape to h7. No matter how you move the pieces around, you will never find a mate. This constellation is therefore always a draw. And, by the way, it would still be a draw if white has three or even more bishops as long as they move on same-coloured squares.

King and Pawn versus Lone King
  a b c d e f g h
 +-+-+-+-+-+-+-+-+
8|. . . . . . . k|8
7|. . . . . . . .|7
6|. . . . . . . P|6
5|. . . . . . . .|5
4|. . . . . . K .|4
3|. . . . . . . .|3
2|. . . . . . . .|2
1|. . . . . . . .|1
 +-+-+-+-+-+-+-+-+

In this particular case, a checkmate can still be delivered because the pawn can be promoted to a queen or rook. But if black does not make a mistake, this cannot be forced.

According to the rules of chess, this is not a draw. But a chess engine or an experienced human player will probably still consider the position a draw, because checkmate cannot be forced.

However, even with the FORCIBLE flag, the method insufficientMaterial() will not report this as a draw because that evaluation would require an analysis of the game. Rationale: It is common belief that neither side can force a win from the starting position of chess. This is is the same situation as in the pawn endgame from the example. Given that both sides play perfectly, the outcome will be a draw. But if one side blunders, a win is possible.

In brief: The FORCIBLE flag additionally evaluates positions as a draw where a checkmate can only be delivered with active help by the opponent.

gameOver [FORCIBLE]

This method has been added in version 0.7.

Returns a truthy value if the game is over. Returns a falsy value, if the game is ongoing.

The return value can be further analysed to get the details.

$state = $pos->gameOver();
if ($state & CP_GAME_OVER) {
    say "The game is over.";
}
if ($state & CP_GAME_WHITE_WINS) {
    say "White has won.";
} elsif ($state & CP_GAME_BLACK_WINS) {
    say "Black has won.";
} else {
    if ($state & CP_GAME_STALEMATE) {
        say "The position is a stalemate";
    } elsif ($state & CP_GAME_FIFTY_MOVES) {
        say "The position is a draw because of the 50-moves-rule.";
    } elsif ($state & CP_GAME_INSUFFICIENT_MATERIAL) {
        say "The position is a draw because insufficient material.";
    } else {
        say "You have found a bug in Chess::Plisco.";
    }
}

Important! A draw because of 3-fold repetition is *not* handled. The reason is that this would require access to the game history. You have to implement this check yourself in your application.

The FORCIBLE flag has the same semantics as for insufficientMaterial() above. If you want to find out whether one side can claim a draw according to the rules of chess, do not pass the flag. But a chess engine that evaluates a position should pass a truthy value so that dead positions can be detected.

attacked(SHIFT)

Returns true if the square indicated by SHIFT is attacked by a piece of the opponent.

moveAttacked(MOVE|NOTATION[, PSEUDO_LEGAL])

Returns true if when executing MOVE, the moving piece would be attacked by a piece of the opponent.

You can give the move either as an integer or in one of the supported move notations.

Beginning with version v0.8.0, you can pass an optional flag PSEUDO_LEGAL. If it has a truthy value, the move is not checked for legality. You want to use this for checking whether the king would move into chess.

movePinned(MOVE|NOTATION)

Returns true if a piece doing MOVE is pinned. A piece is pinned if it would leave the king in check when doing the move. Exposing other pieces to an opponent attack is not considered a pin by this method.

You can give the move either as an integer or in one of the supported move notations.

Beginning with version v0.8.0, you can pass an optional flag PSEUDO_LEGAL. If it has a truthy value, the move is not checked for legality. You want to use this for checking whether the move would put the king into check.

rMagic(SHIFT, OCCUPANCY)

Returns a bitboard of all squares that a rook can reach from SHIFT. OCCUPANCY is a bitboard of all squares that are occupied by pieces. The first piece that the sliding piece would reach is considered a potential captured of a capture and is a valid target square.

See "Understanding rMagic and bMagic" in Chess::Plisco::Tutorial for more information.

bMagic(SHIFT, OCCUPANCY)

Returns a bitboard of all squares that a bishop can reach from SHIFT. OCCUPANCY is a bitboard of all squares that are occupied by pieces. The first piece that the sliding piece would reach is considered a potential captured of a capture and is a valid target square.

See "Understanding rMagic and bMagic" in Chess::Plisco::Tutorial for more information.

Bit(board) Fiddling Methods

bitboardPopcount(BITBOARD)

Counts and returns the bits sets in BITBOARD.

This does the same as the builtin function __builtin_popcountll of the C compilers llvm and gcc.

bitboardClearLeastSet(BITBOARD)

Clears the least signicant bit that is set in BITBOARD.

bitboardClearButLeastSet(BITBOARD)

Clears all set bits in BITBOARD except for the least significant one that is set.

bitboardCountTrailingZbits(BITBOARD)

Counts all trailing zero bits, that is all bits that are not set starting with the least significant bit (bit number 0).

bitboardCountIsolatedTrailingZbits(BITBOARD)

Counts all trailing zero bits, that is all bits that are not set starting with the least significant bit (bit number 0). In other words, this method gives you the shift of the set bit in BITBOARD. It only works if exactlye one bit is set in BITBOARD, otherwise the behavior is undefined.

This does the same as the builtin function __builtin_ctzll of the C compilers llvm and gcc.

bitboardMoreThanOneSet(BITBOARD)

Returns a truthy value if more than one bit in BITBOARD is set, false otherwise.

Perft Methods

A "perft" (PERFormance Test) is a standard test for measuring the performance of the move generator. Starting from the current position, it generates all legal moves up to the specified depth. It is also important for testing the correct functioning of the move generator because the number of leave nodes for certain positions are well known.

Internally, each legal move is applied to the position, then the next level is computed from the resulting position, and finally the move is undone.

perft(DEPTH)

Does a performance test to depth DEPTH. Returns the number of leaf nodes found.

perftWithOutput(DEPTH, FILEHANDLE)

Does the the same as perft but prints out all top-level moves found with the number of subnodes for each move. The time needed is measured with "gettimeofday" in Time::HiRes and reported at the end as well as the number of nodes found.

This method can be used directly to implement the command "go perft" for a UCI compatible chess engine.

Methods for Debugging and Diagnostics

consistent

Does an extensive consistency check on the position and throws an exception if any inconsistency is encountered.

dumpBitboard(BITBOARD)

Generate a string representation of BITBOARD in ASCII art.

dumpAll

Generates a string representation of all bitboards in ASCII art plus some additional information.

dumpInfo(INFO)

Returns a string with the decoded position information as retured by "info".

movesCoordinateNotation(MOVES)

Takes an array of moves (as integers) and converts it into an array of moves in coordinate notation.

Other Methods

RNG

Returns a pseudo-random integer created by the well-known xor-shift pseudo-random number generator.

The random-number generator is always seeded with the same seed (initial value). This is on purpose, so that the numbers returned are deterministic. The method is currently only used for generating the Zobrist keys for position signatures.

If you want a different seed, you should override the constant "CP_RANDOM_SEED".

PROPERTIES

You can access individual properties either by using index constants or by using accessor macros from Chess::Plisco::Macro. All accessor macros can be assigned to; they are L-values. But you are strongly advised to modify properties of a Chess::Plisco instance only with the methods documented here.

For getting or setting the bitboard of all white pieces, you have these options:

$whites_pieces = $pos->[CP_W_PIECES];
$white_pieces = cp_w_pieces $pos;
$white_pieces = cp_w_pieces($pos);
$pos->[CP_W_PIECES] = $white_pieces;
cp_w_pieces $pos = $white_pieces;
cp_w_pieces($pos) = $white_pieces;

The macros (all starting with "cp_") are only available when you have loaded Chess::Plisco::Macro, see there for more information.

All elements of the position array are documented below under "Accessor Indices (:accessors)".

EXPORT TAGS

The module exports only constants, all prefixed with "CP_".

Note that (lowercase) macros "cp_" are defined by using Chess::Plisco::Macro.

All Constants (:all)

You can import all constants with the export tag ":all".

Accessor Indices (:accessors)

The array indices were carefully selected so that the following conditions are met:

All piece types ("CP_PAWN", "CP_KNIGHT", ..., "CP_KING") can be used as indexes into the instance in order to retrieve their respective bitboard
The white bitboard comes directly before the black bitboard.

The bitboard for the pieces of the side to move is therefore always at the location CP_POS_WHITE_PIECES + $pos-toMove>. Or in other words, the constants CP_POS_PAWNS and CP_PAWN (likewise for the other piece types) are guaranteed to be the same and to point to the corresponding piece bitboard.

POS_>[CP_POS_HALFMOVES]

The number of halfmoves in this position.

POS->[CP_POS_PAWNS]

A bitboard of all pawns (black and white) on the board. See also "pawns".

POS->[CP_POS_KNIGHTS]

A bitboard of all knights (black and white) on the board. See also "knights".

POS->[CP_POS_BISHOPS]

A bitboard of all bishops (black and white) on the board. See also "bishops".

POS->[CP_POS_QUEENS]

A bitboard of all rooks (black and white) on the board. See also "queens".

POS->[CP_POS_KINGS]

A bitboard of all kings (black and white) on the board. See also "kings".

POS->[CP_POS_ROOKS]

A bitboard of all rooks (black and white) on the board. See also "rooks".

POS->[CP_POS_WHITE_PIECES]

A bitboard of all white pieces on the board. See also "whitePieces".

POS->[CP_POS_BLACK_PIECES]

A bitboard of all black pieces on the board. See also "blackPieces".

POS->[CP_POS_LAST_MOVE]

The last mvoe if any. See also "lastMove".

POS->[CP_POS_MATERIAL]

The raw material balance. See also "material".

POS->[CP_POS_HALFMOVE_CLOCK]

The count of halfmoves for the 50-move rule. If it is 100 or more, either side can claim a draw. See also "halfmoveClock".

POS->[CP_POS_TURN]
POS->[CP_POS_TO_MOVE]

The side whose turn it is. See also "turn".

POS->[CP_POS_EN_PASSANT_SHIFT]

The position of a pawn where an en-passant capture is theoretically possible. It is theoretically possible if a pawn was pushed two squares in the last move. Whether it can be captured or whether the capture is legal is ignored in this context.

See also "enPassantShift".

POS->[CP_POS_USR1]
POS->[CP_POS_USR2]
POS->[CP_POS_USR3]
POS->[CP_POS_USR4]
POS->[CP_POS_USR5]

Unused. These slots can be used by a chess engine.

POS->[CP_POS_CASTLING_RIGHTS

The castling rights of the position. See also "castlingRights".

Piece Constants (:pieces)

CP_WHITE => 0

Symbolic constant for white pieces, side to move, and so on.

CP_BLACK => 1

Symbolic constant for black pieces, side to move, and so on. Actually, usage of this constant is avoided internally so that any truthy value can be used. Most of the time, this is simply the else branch of a conditional.

CP_NO_PIECE => 0

Symbolic constant for no piece.

CP_PAWN

Symbolic constant for a pawn.

CP_KNIGHT

Symbolic constant for a knight.

CP_BISHOP

Symbolic constant for a bishop.

CP_ROOK

Symbolic constant for a rook.

CP_QUEEN

Symbolic constant for a queen.

CP_KING

Symbolic constant for a king.

CP_PAWN_VALUE => 100

Value of a pawn in centipawns. Feel free to override this constant in derived classes.

CP_KNIGHT_VALUE => 300

Value of a knight in centipawns. Feel free to override this constant in derived classes.

CP_BISHOP_VALUE => 300

Value of a bishop in centipawns. Feel free to override this constant in derived classes.

CP_ROOK_VALUE => 500

Value of a rook in centipawns. Feel free to override this constant in derived classes.

CP_QUEEN_VALUE => 900

Value of a queen in centipawns. Feel free to override this constant in derived classes.

Note that there is no value for a king. This is on purpose.

CP_PIECE_CHARS

An array of arrays that maps numeric piece constants (CP_PAWN, CP_KNIGHT, ...) to characters. The first array are uppercase letters, normally used for white pieces, the second one are lowercase letters, normally used for black pieces.

Example to get the character for a black knight:

$char = CP_PIECE_CHARS->[CP_BLACK]->[CP_KNIGHT];

Board Constants (:board)

CP_A_MASK .. CP_H_MASK

These are bitboards of all files ("a" to "h") of the chess board.

CP_1_MASK .. CP_8_MASK

These are bitboards of all ranks ("1" to "8") of the chess board.

CP_WHITE_MASK

Bitboard of all white squares (b1, d1, ... g8)

CP_BLACK_MASK

Bitboard of all black squares (a1, c1, ... h8)

CP_LIGHT_MASK

An alias for CP_WHITE_MASK. Added in version 0.7.

CP_DARK_MASK

An alias for CP_BLACK_MASK. Added in version 0.7.

CP_FILE_A .. CP_FILE_H

0-based numbers of all files ("a" to "h").

CP_RANK_1 .. CP_RANK_8

0-based numbers of all ranks ("1" to "8").

CP_A1 .. CP_H8

Shifts for all squares of the chess board.

Magic Moves Resp. Magic Bitboard Constants (:magicmoves)

These are all large data tables that are used internally for the magic bitboards that generate the attack masks for the sliding pieces (queens, bishops, and rooks). See the source if you are curious. Otherwise just import them if you want to use the macros cp_mm_bmagic() and cp_mm_rmagic() from Chess::Plisco::Macro.

CP_MAGICMOVES_B_MAGICS

Internal.

CP_MAGICMOVES_R_MAGICS

Internal.

CP_MAGICMOVES_B_MASK

Internal.

CP_MAGICMOVES_R_MASK

Internal.

CP_MAGICMOVESBDB

Internal.

CP_MAGICMOVESRDB

Internal.

Game State Constants (:game)

This tag has been added in version 0.7.

CP_GAME_OVER

Bit mask for game state "game over".

CP_GAME_WHITE_WINS

Bit mask for game state where white has won.

CP_GAME_BLACK_WINS

Bit mask for game state where black has won.

CP_GAME_STALEMATE

Bit mask for game state stalemate.

CP_GAME_FIFTY_MOVES

Bit mask for draw because of the 50-move-rule.

CP_GAME_THREEFOLD_REPETITION

Bit mask for draw because of threefold repetition.

CP_GAME_INSUFFICIENT_MATERIAL

Bit mask for draw because of threefold repetition.

Auxiliary Constants (:aux)

CP_INT_SIZE

The size in bits of an integer. Should be at least 64.

CP_CHAR_BITS

The number of bits in a char. Should be 8.

CP_RANDOM_SEED

A pretty arbitrary value used to initialize the pseudo-random number generator "RNG".

FAQ

Is It Colour or Color?

That does not have to be decided. You can use either spelling in this library.

What Does Plisco Mean?

I originally wrote a chess engine in C and named it "tate", just because I liked the name. But then I found out about Emory Andrew Tate Jr., an international master of chess. As it turned out, Tate hated chess computers and never used them. I therefore decided that the name "tate" was inappropriate.

The chess engine was then renamed to "lisco" (in Bulgarian: лиско) which means fox in Bulgarian. The name of the engine had changed but its countless bugs remained. I decided that it would be easier to debug the engine in Perl but all available chess libraries were either too buggy or too slow or both for using them. So I decided to write a library that was well tested and fast enough, and that library is now Chess::Plisco. The first letter P stands for Perl.

COPYRIGHT

Copyright (C) 2021-2025 Guido Flohr <guido.flohr@cantanea.com>.

SEE ALSO

Chess::Plisco::Macro, perl(1)