| Type: | Package |
| Title: | Bitboard Chess Engine |
| Version: | 0.1.0 |
| Author: | Qusai Jouda [aut, cre] |
| Maintainer: | Qusai Jouda <jouda.qusai@gmail.com> |
| Description: | A fully legal chess move generator and game engine implemented in C++17 via 'Rcpp'. Provides FEN (Forsyth-Edwards Notation) parsing, PGN (Portable Game Notation) replay, position feature enrichment, and a multi-game registry backed by a bitboard representation. |
| License: | MIT + file LICENSE |
| Imports: | Rcpp (≥ 1.0.0) |
| LinkingTo: | Rcpp |
| Suggests: | testthat (≥ 3.0.0) |
| Encoding: | UTF-8 |
| RoxygenNote: | 7.3.3 |
| NeedsCompilation: | yes |
| Packaged: | 2026-03-24 23:30:18 UTC; qusai |
| Repository: | CRAN |
| Date/Publication: | 2026-03-30 09:20:02 UTC |
ply: A Bitboard Chess Engine for R
Description
Provides a fully legal chess move generator and game engine implemented in C++17 via Rcpp. The package exposes two layers:
Details
A high-level R API (
ply_*functions) for everyday use.An internal Rcpp bridge (
cpp_*functions) used by the exported R helpers and test infrastructure.
Author(s)
Maintainer: Qusai Jouda qjouda@gmu.edu
Enrich a batch of positions with chess features
Description
Returns a data.frame with one row per (FEN, UCI-move) pair and
columns covering captures, checks, castling, promotion, material balance,
pawn structure, mobility, king safety, and pin count.
Usage
ply_enrich_batch(fens, uci_moves)
Arguments
fens |
Character vector of FEN strings. |
uci_moves |
Character vector of UCI move strings parallel to
|
Value
A data.frame with one row per position and columns:
is_capture, is_castling, is_promotion,
is_en_passant, gives_check, gives_discovered_check,
in_check, material_bal, num_pieces,
legal_move_count, moved_piece_value,
captured_piece_value, is_checkmate, is_stalemate,
num_captures_avail, num_checks_avail, can_castle,
max_capture_value, king_safety_own, king_safety_opp,
mob_pawn, mob_knight, mob_bishop, mob_rook,
mob_queen, mob_king, doubled_pawns,
isolated_pawns, passed_pawns, pin_count,
en_passant_avail, promotion_available.
Examples
start <- "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
df <- ply_enrich_batch(start, "e2e4")
df$is_capture # FALSE
df$legal_move_count # 20
Parse a FEN string into a ChessState list
Description
Parse a FEN string into a ChessState list
Usage
ply_fen_parse(fen)
Arguments
fen |
A FEN string. |
Value
A named list of class ChessState representing the board state.
Examples
state <- ply_fen_parse("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1")
state$sideToMove # 0 = white
Convert a ChessState list back to a FEN string
Description
Convert a ChessState list back to a FEN string
Usage
ply_fen_serialize(state)
Arguments
state |
A |
Value
A FEN string.
Examples
state <- ply_game_init()
ply_fen_serialize(state)
Accept an outstanding draw offer in a managed game
Description
Accept an outstanding draw offer in a managed game
Usage
ply_game_accept_draw(game_id, player)
Arguments
game_id |
Integer game id. |
player |
Player name — must be the player who did not offer. |
Value
TRUE on success; FALSE if no offer is pending or wrong
player.
See Also
Examples
ply_game_reset_registry()
id <- ply_game_new("alice")
ply_game_join(id, "bob")
ply_game_move(id, "alice", "e2e4")
ply_game_move(id, "bob", "e7e5")
ply_game_offer_draw(id, "alice")
ply_game_accept_draw(id, "bob") # TRUE — game ends as Draw
Cancel a waiting managed game
Description
Only the game creator can cancel, and only while the game is still in Waiting status (i.e.\ the opponent has not yet joined).
Usage
ply_game_cancel(game_id, player)
Arguments
game_id |
Integer game id. |
player |
Creator's player name. |
Value
TRUE on success; FALSE if not the creator or game is
already active.
Examples
ply_game_reset_registry()
id <- ply_game_new("alice") # Waiting — no opponent yet
ply_game_cancel(id, "alice") # TRUE
ply_game_info(id)$termination # 10 = Cancelled
Return the number of games currently held in the registry
Description
Return the number of games currently held in the registry
Usage
ply_game_count()
Value
A non-negative integer.
See Also
ply_game_new, ply_game_reset_registry
Examples
ply_game_reset_registry()
ply_game_count() # 0
ply_game_new("alice")
ply_game_count() # 1
Get the current FEN for a managed game
Description
Get the current FEN for a managed game
Usage
ply_game_fen(game_id)
Arguments
game_id |
Integer game id. |
Value
A FEN string.
Examples
ply_game_reset_registry()
id <- ply_game_new("alice")
ply_game_join(id, "bob")
ply_game_move(id, "alice", "d2d4")
ply_game_fen(id) # FEN with white pawn on d4
Get the full ply history of a managed game as UCI strings
Description
Get the full ply history of a managed game as UCI strings
Usage
ply_game_history(game_id)
Arguments
game_id |
Integer game id. |
Value
A character vector of UCI move strings, one per ply played.
Examples
ply_game_reset_registry()
id <- ply_game_new("alice")
ply_game_join(id, "bob")
ply_game_move(id, "alice", "e2e4")
ply_game_move(id, "bob", "c7c5")
ply_game_history(id) # c("e2e4", "c7c5")
Get metadata for a managed game
Description
Get metadata for a managed game
Usage
ply_game_info(game_id)
Arguments
game_id |
Integer game id. |
Value
A named list with fields white, black,
status (0=Waiting, 1=Active, 2=Finished),
result (0=InProgress, 1=WhiteWins,
2=BlackWins, 3=Draw), winner, termination,
fen, and plyCount.
Examples
ply_game_reset_registry()
id <- ply_game_new("alice")
ply_game_join(id, "bob")
info <- ply_game_info(id)
info$white # "alice"
info$status # 1 (Active)
Return a ChessState list at the standard starting position
Description
Stateless helper that does not interact with the game registry. To create a
managed game use ply_game_new.
Usage
ply_game_init()
Value
A named list of class ChessState representing the starting
chess position.
Examples
state <- ply_game_init()
state$sideToMove # 0 = white to move
state$fullMoveNumber # 1
Join an existing game as the opposing player
Description
Join an existing game as the opposing player
Usage
ply_game_join(game_id, player)
Arguments
game_id |
Integer game id. |
player |
Player name string. |
Value
TRUE on success.
Examples
ply_game_reset_registry()
id <- ply_game_new("alice")
ply_game_join(id, "bob") # TRUE — game is now Active
ply_game_info(id)$status # 1
Make a move in a managed game by UCI string
Description
Make a move in a managed game by UCI string
Usage
ply_game_move(game_id, player, uci, settle = TRUE)
Arguments
game_id |
Integer game id. |
player |
Player name string. |
uci |
UCI move string (e.g. |
settle |
If |
Value
TRUE on success.
Examples
ply_game_reset_registry()
id <- ply_game_new("alice")
ply_game_join(id, "bob")
ply_game_move(id, "alice", "e2e4") # TRUE
ply_game_move(id, "bob", "e7e5") # TRUE
ply_game_history(id) # c("e2e4", "e7e5")
Create a new managed game
Description
Adds a game to the global registry without resetting existing games. Use
ply_game_reset_registry first if you want a clean slate.
Usage
ply_game_new(creator, mode = 0L, time_limit = 600)
Arguments
creator |
Name of the creating player. |
mode |
Settlement mode: |
time_limit |
Ply time limit in seconds ( |
Value
Integer game id.
See Also
ply_game_join, ply_game_move,
ply_game_reset_registry
Examples
ply_game_reset_registry()
id <- ply_game_new("alice")
ply_game_count() # 1
Create a new managed game from a custom starting position
Description
Create a new managed game from a custom starting position
Usage
ply_game_new_from_fen(creator, fen, mode = 0L, time_limit = 600)
Arguments
creator |
Name of the creating player. |
fen |
A FEN string for the starting position. |
mode |
Settlement mode: |
time_limit |
Ply time limit in seconds ( |
Value
Integer game id.
See Also
Examples
ply_game_reset_registry()
fen <- "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"
id <- ply_game_new_from_fen("alice", fen)
ply_game_join(id, "bob")
ply_game_fen(id) # starts after 1.e4
Offer or toggle a draw in a managed game
Description
Records a draw offer from player. If the opponent has already offered
a draw, this call accepts it and concludes the game as a draw by agreement.
A player can cancel their own outstanding offer by calling this again with
the same player name.
Usage
ply_game_offer_draw(game_id, player)
Arguments
game_id |
Integer game id. |
player |
Player name string. |
Value
TRUE on success.
See Also
Examples
ply_game_reset_registry()
id <- ply_game_new("alice")
ply_game_join(id, "bob")
ply_game_move(id, "alice", "e2e4")
ply_game_move(id, "bob", "e7e5")
ply_game_offer_draw(id, "alice") # alice offers a draw
ply_game_accept_draw(id, "bob") # bob accepts
ply_game_info(id)$result # 3 = Draw
Reset (clear) the global game registry
Description
Destroys all managed games. Game ids from before the reset become invalid.
Subsequent ply_game_count calls return 0.
Usage
ply_game_reset_registry()
Value
Invisibly NULL.
See Also
Examples
ply_game_new("alice")
ply_game_reset_registry()
ply_game_count() # 0
Resign a managed game
Description
Resign a managed game
Usage
ply_game_resign(game_id, player)
Arguments
game_id |
Integer game id. |
player |
Resigning player's name. |
Value
TRUE on success.
Examples
ply_game_reset_registry()
id <- ply_game_new("alice")
ply_game_join(id, "bob")
ply_game_move(id, "alice", "e2e4")
ply_game_resign(id, "bob") # bob resigns; alice wins
ply_game_info(id)$result # 1 = WhiteWins
Compute a Zobrist-style position hash
Description
Compute a Zobrist-style position hash
Usage
ply_hash(state)
Arguments
state |
A |
Value
A 16-character lowercase hex string uniquely identifying the position.
Examples
h <- ply_hash(ply_game_init())
nchar(h) # 16
Test whether a side is in check
Description
Test whether a side is in check
Usage
ply_in_check(state, color = state$sideToMove)
Arguments
state |
A ChessState list. |
color |
Integer color: |
Value
TRUE if the specified side is in check.
Examples
ply_in_check(ply_game_init()) # FALSE at the start
ply_in_check(ply_game_init(), color = 0L) # white not in check
Test whether the position is checkmate
Description
Test whether the position is checkmate
Usage
ply_is_checkmate(state)
Arguments
state |
A |
Value
TRUE if the side to move is checkmated.
Examples
# Scholar's Mate — black is checkmated
s <- ply_fen_parse(
"r1bqkb1r/pppp1Qpp/2n2n2/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 4"
)
ply_is_checkmate(s) # TRUE
Test whether the position is a draw by insufficient material
Description
Test whether the position is a draw by insufficient material
Usage
ply_is_insufficient_material(state)
Arguments
state |
A |
Value
TRUE if neither side can force checkmate.
Examples
# Only kings remain
s <- ply_fen_parse("8/8/8/8/8/8/8/K6k w - - 0 1")
ply_is_insufficient_material(s) # TRUE
Test whether the position is stalemate
Description
Test whether the position is stalemate
Usage
ply_is_stalemate(state)
Arguments
state |
A |
Value
TRUE if the side to move is stalemated.
Examples
# Black king a8, white queen c7, white king b6 — stalemate
s <- ply_fen_parse("k7/2Q5/1K6/8/8/8/8/8 b - - 0 1")
ply_is_stalemate(s) # TRUE
List all legal moves from a position as UCI strings
Description
List all legal moves from a position as UCI strings
Usage
ply_legal_moves(state)
Arguments
state |
A |
Value
A character vector of UCI move strings.
Examples
moves <- ply_legal_moves(ply_game_init())
length(moves) # 20 from the starting position
Apply a UCI move string to a position
Description
Apply a UCI move string to a position
Usage
ply_move_apply(state, uci)
Arguments
state |
A |
uci |
A UCI move string such as |
Value
The new ChessState list after the move.
Examples
s1 <- ply_game_init()
s2 <- ply_move_apply(s1, "e2e4")
s2$sideToMove # 1 = black to move
Extract clean SAN movetext tokens from a PGN game block
Description
Strips tags, comments, variations, NAGs, and result strings.
Usage
ply_pgn_extract_movetext(game_text)
Arguments
game_text |
A character string containing one PGN game block. |
Value
A single trimmed character string of space-separated SAN tokens.
Examples
g <- '[Event "Test"]\n\n1.e4 {Best by test} e5 2.Nf3 Nc6 1-0'
ply_pgn_extract_movetext(g) # "e4 e5 Nf3 Nc6"
Load a PGN file and return a data.frame of game-level metadata
Description
Returns a data.frame with columns Event, White,
Black, Result, ECO, and Plys (number of
half-moves).
Usage
ply_pgn_load_games(pgn_path)
Arguments
pgn_path |
Path to a PGN file. |
Value
A data.frame with one row per game and columns
Event, White, Black, Result, ECO,
Plys.
Examples
pgn_file <- system.file("extdata", "example.pgn", package = "ply")
if (file.exists(pgn_file)) {
games <- ply_pgn_load_games(pgn_file)
head(games)
}
Parse PGN tag pairs from a raw game block
Description
Parse PGN tag pairs from a raw game block
Usage
ply_pgn_parse_tags(game_text)
Arguments
game_text |
A character string containing one PGN game block. |
Value
A named list of tag key/value pairs.
Examples
g <- '[Event "Test"]\n[White "Alice"]\n[Black "Bob"]\n\n1.e4 e5 1-0'
tags <- ply_pgn_parse_tags(g)
tags$White # "Alice"
Validate a position (piece counts, king count, etc.)
Description
Validate a position (piece counts, king count, etc.)
Usage
ply_validate(state)
Arguments
state |
A |
Value
TRUE if the position is legal.
Examples
ply_validate(ply_game_init()) # TRUE