Game¶
-
class Game¶
Represents a chess game.
Constructors
- inline constexpr constexpr Game ()
Default constructor. Constructs a new game starting from the default position.
- inline explicit constexpr constexpr Game (const Position &initial_position)
Constructs a new game with a specified initial position.
Comparison operators
Element access
-
inline bool startsFromDefaultPosition() const¶
Indicates whether the game started from the default position.
-
inline const PiecePlacement &piecePlacement() const¶
Returns the piece placement of the current position.
-
inline std::optional<Square> enPassantTargetSquare() const¶
Returns the en passant target square, if any.
-
inline std::optional<Square> legalEnPassantTargetSquare() const¶
Returns the en passant target square if it’s legally capturable.
-
inline const uint32_t &halfmoveClock() const¶
Returns the number of halfmoves since the last capture or pawn move.
-
inline const uint32_t &fullmoveNumber() const¶
Returns the current fullmove number.
-
inline const CastlingRights &castlingRights() const¶
Returns the current castling rights.
-
inline const std::vector<UciMove> &uciMoves() const¶
Returns the list of moves in UCI notation played since the initial position.
-
inline const std::vector<SanMove> &sanMoves() const¶
Returns the list of moves in SAN notation played since the initial position.
-
inline const RepetitionTracker &repetitionTracker() const¶
Returns the repetition tracker.
-
inline std::optional<GameResult> result() const¶
Returns the result of the game if it is over.
-
inline std::optional<DrawReason> drawReason() const¶
Returns the draw reason if the game ended in a draw.
Modifiers
-
inline std::expected<void, MoveError> move(const UciMove &move)¶
Applies a move specified in UCI (Universal Chess Interface) format to the current game state, or returns an error if the move is illegal for the current position.
-
inline std::expected<void, MoveError> move(const SanMove &move)¶
Applies a move specified in SAN (Standard Algebraic Notation) format to the current game state, or returns an error if the move is illegal for the current position.
-
inline void undoMove()¶
Undoes the last move played, restoring the previous position.
-
inline void reset()¶
Resets the game to the initial position.
Public Types
-
using RepetitionTracker = std::unordered_map<Position, uint32_t, RepetitionHash, RepetitionEqual>¶
Type alias for mapping from positions to their repetition count, used to detect threefold repetition.
Helper classes¶
-
template<>
struct formatter : public chesscxx::internal::SpecDispatcher<chesscxx::internal::PgnSpec, chesscxx::internal::FenSpec, chesscxx::internal::AsciiSpec, chesscxx::internal::PieceListSpec, chesscxx::internal::RepetitionSpec> - #include <game_formatter.h>
formatting support for chesscxx::Game
-
template<>
struct hash - #include <game_hash.h>
hash support for chesscxx::Game
-
template<>
class Parser - #include <game_parser.h>
FEN parsing support for Game.
-
template<>
class Parser - #include <game_parser.h>
PGN parsing support for Game.
Subclassed by chesscxx::Parser< Game, const char *, parse_as::Default >
Examples¶
Full game¶
#include <chesscxx/game.h>
#include <chesscxx/game_result.h>
#include <chesscxx/parse.h>
#include <chesscxx/san_move.h>
#include <chesscxx/uci_move.h>
#include <cstdlib>
#include <optional>
#include <print>
#include <string_view>
namespace {
void verify(const auto& check) {
if (!static_cast<bool>(check)) std::abort();
}
auto parseUciMove(std::string_view str) -> chesscxx::UciMove {
auto parsed_uci_move = chesscxx::parse<chesscxx::UciMove>(str);
verify(parsed_uci_move.has_value());
return parsed_uci_move.value();
}
auto parseSanMove(std::string_view str) -> chesscxx::SanMove {
auto parsed_san_move = chesscxx::parse<chesscxx::SanMove>(str);
verify(parsed_san_move.has_value());
return parsed_san_move.value();
}
} // namespace
auto main() -> int {
chesscxx::Game game;
std::println("{}\n", game);
std::println("{:pgn}\n", game);
std::println("{:fen}\n", game);
std::println("{:ascii}\n", game);
std::println("{:lists}\n", game);
std::println("{:rep}\n", game);
verify(game.move(parseSanMove("xe4+")));
verify(game.move(parseUciMove("e7e5")));
verify(game.move(parseSanMove("Qh5")));
verify(game.move(parseSanMove("Nc6")));
verify(game.move(parseUciMove("f1c4")));
verify(game.move(parseSanMove("Nf6")));
verify(game.move(parseSanMove("Bf7")));
verify(game.result() == std::nullopt);
game.undoMove();
verify(game.move(parseSanMove("Qh5f7")));
verify(game.result() == chesscxx::GameResult::kWhiteWins);
std::println("{}\n", game);
std::println("{:pgn}\n", game);
std::println("{:fen}\n", game);
std::println("{:ascii}\n", game);
std::println("{:lists}\n", game);
std::println("{:rep}\n", game);
}
Output:
[Result "*"]
*
[Result "*"]
*
rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
rnbqkbnr
pppppppp
........
........
........
........
PPPPPPPP
RNBQKBNR
Active color: white
Castling availability: KQkq
En passant target square: -
Halfmove clock: 0
Fullmove number: 1
{ white king: [e1], white queen: [d1], white bishops: [f1, c1], white knights: [g1, b1], white rooks: [h1, a1], white pawns: [h2, g2, f2, e2, d2, c2, b2, a2], black pawns: [h7, g7, f7, e7, d7, c7, b7, a7], black king: [e8], black queen: [d8], black bishops: [f8, c8], black knights: [g8, b8], black rooks: [h8, a8] }
Active color: white
Castling availability: KQkq
En passant target square: -
Halfmove clock: 0
Fullmove number: 1
{ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -": 1 }
[Result "1-0"]
1. e4 e5 2. Qh5 Nc6 3. Bc4 Nf6 4. Qxf7# 1-0
[Result "1-0"]
1. e4 e5 2. Qh5 Nc6 3. Bc4 Nf6 4. Qxf7# 1-0
r1bqkb1r/pppp1Qpp/2n2n2/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq - 0 4
r.bqkb.r
pppp.Qpp
..n..n..
....p...
..B.P...
........
PPPP.PPP
RNB.K.NR
Active color: black
Castling availability: KQkq
En passant target square: -
Halfmove clock: 0
Fullmove number: 4
{ white king: [e1], white queen: [f7], white bishops: [c4, c1], white knights: [g1, b1], white rooks: [h1, a1], white pawns: [h2, g2, f2, d2, c2, e4, b2, a2], black pawns: [e5, h7, g7, d7, c7, b7, a7], black king: [e8], black queen: [d8], black bishops: [f8, c8], black knights: [f6, c6], black rooks: [h8, a8] }
Active color: black
Castling availability: KQkq
En passant target square: -
Halfmove clock: 0
Fullmove number: 4
{ "r1bqkb1r/pppp1Qpp/2n2n2/4p3/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq -": 1, "r1bqkb1r/pppp1ppp/2n2n2/4p2Q/2B1P3/8/PPPP1PPP/RNB1K1NR w KQkq -": 1, "r1bqkbnr/pppp1ppp/2n5/4p2Q/4P3/8/PPPP1PPP/RNB1KBNR w KQkq -": 1, "r1bqkbnr/pppp1ppp/2n5/4p2Q/2B1P3/8/PPPP1PPP/RNB1K1NR b KQkq -": 1, "rnbqkbnr/pppp1ppp/8/4p2Q/4P3/8/PPPP1PPP/RNB1KBNR b KQkq -": 1, "rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq -": 1, "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq -": 1, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -": 1 }
General¶
#include <chesscxx/game.h>
#include <chesscxx/parse.h>
#include <chesscxx/position.h>
#include <chesscxx/san_move.h>
#include <cstdlib>
#include <print>
#include <string_view>
namespace {
void verify(const auto& check) {
if (!static_cast<bool>(check)) std::abort();
}
auto parseSanMove(std::string_view str) -> chesscxx::SanMove {
auto parsed_san_move = chesscxx::parse<chesscxx::SanMove>(str);
verify(parsed_san_move);
return parsed_san_move.value();
}
auto parsePosition(std::string_view str) -> chesscxx::Position {
auto parsed_position = chesscxx::parse<chesscxx::Position>(str);
verify(parsed_position);
return parsed_position.value();
}
} // namespace
auto main() -> int {
chesscxx::Game game;
verify(game.startsFromDefaultPosition());
verify(game.initialPosition() == game.currentPosition());
game = chesscxx::Game(parsePosition(
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"));
verify(game.startsFromDefaultPosition());
verify(game.initialPosition() == game.currentPosition());
game = chesscxx::Game(parsePosition(
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"));
verify(!game.startsFromDefaultPosition());
verify(game.initialPosition() == game.currentPosition());
verify(game.move(parseSanMove("Nf6")));
verify(game.initialPosition() != game.currentPosition());
verify(game.move(parseSanMove("e5")));
std::println("uci moves: {}", game.uciMoves());
std::println("san moves: {}", game.sanMoves());
game.reset();
verify(game.initialPosition() == game.currentPosition());
std::println("{}", game);
}
Output:
uci moves: [g8f6, e4e5]
san moves: [Nf6, e5]
[Result "*"]
[FEN "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"]
[SetUp "1"]
*
Repetition¶
#include <chesscxx/draw_reason.h>
#include <chesscxx/game.h>
#include <chesscxx/game_result.h>
#include <chesscxx/parse.h>
#include <chesscxx/san_move.h>
#include <cstdlib>
#include <print>
#include <string_view>
namespace {
void verify(const auto& check) {
if (!static_cast<bool>(check)) std::abort();
}
auto parseSanMove(std::string_view str) -> chesscxx::SanMove {
auto parsed_san_move = chesscxx::parse<chesscxx::SanMove>(str);
verify(parsed_san_move);
return parsed_san_move.value();
}
void printRepetition(auto& game) { std::println("{:rep}\n", game); }
void move(auto& game, std::string_view str) {
auto move_result = game.move(parseSanMove(str));
verify(move_result);
}
} // namespace
auto main() -> int {
chesscxx::Game game;
printRepetition(game);
move(game, "Nc3");
printRepetition(game);
move(game, "Nc6");
printRepetition(game);
move(game, "Nb1");
printRepetition(game);
move(game, "Nb8");
printRepetition(game);
move(game, "Nc3");
printRepetition(game);
move(game, "Nc6");
printRepetition(game);
verify(game.repetitionTracker().at(game.currentPosition()) == 2);
move(game, "Nb1");
printRepetition(game);
move(game, "Nb8");
printRepetition(game);
verify(game.result() == chesscxx::GameResult::kDraw);
verify(game.drawReason() == chesscxx::DrawReason::kThreefoldRepetition);
}
Output:
{ "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -": 1 }
{ "rnbqkbnr/pppppppp/8/8/8/2N5/PPPPPPPP/R1BQKBNR b KQkq -": 1, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -": 1 }
{ "r1bqkbnr/pppppppp/2n5/8/8/2N5/PPPPPPPP/R1BQKBNR w KQkq -": 1, "rnbqkbnr/pppppppp/8/8/8/2N5/PPPPPPPP/R1BQKBNR b KQkq -": 1, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -": 1 }
{ "r1bqkbnr/pppppppp/2n5/8/8/8/PPPPPPPP/RNBQKBNR b KQkq -": 1, "r1bqkbnr/pppppppp/2n5/8/8/2N5/PPPPPPPP/R1BQKBNR w KQkq -": 1, "rnbqkbnr/pppppppp/8/8/8/2N5/PPPPPPPP/R1BQKBNR b KQkq -": 1, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -": 1 }
{ "r1bqkbnr/pppppppp/2n5/8/8/8/PPPPPPPP/RNBQKBNR b KQkq -": 1, "r1bqkbnr/pppppppp/2n5/8/8/2N5/PPPPPPPP/R1BQKBNR w KQkq -": 1, "rnbqkbnr/pppppppp/8/8/8/2N5/PPPPPPPP/R1BQKBNR b KQkq -": 1, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -": 2 }
{ "r1bqkbnr/pppppppp/2n5/8/8/8/PPPPPPPP/RNBQKBNR b KQkq -": 1, "r1bqkbnr/pppppppp/2n5/8/8/2N5/PPPPPPPP/R1BQKBNR w KQkq -": 1, "rnbqkbnr/pppppppp/8/8/8/2N5/PPPPPPPP/R1BQKBNR b KQkq -": 2, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -": 2 }
{ "r1bqkbnr/pppppppp/2n5/8/8/8/PPPPPPPP/RNBQKBNR b KQkq -": 1, "r1bqkbnr/pppppppp/2n5/8/8/2N5/PPPPPPPP/R1BQKBNR w KQkq -": 2, "rnbqkbnr/pppppppp/8/8/8/2N5/PPPPPPPP/R1BQKBNR b KQkq -": 2, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -": 2 }
{ "r1bqkbnr/pppppppp/2n5/8/8/8/PPPPPPPP/RNBQKBNR b KQkq -": 2, "r1bqkbnr/pppppppp/2n5/8/8/2N5/PPPPPPPP/R1BQKBNR w KQkq -": 2, "rnbqkbnr/pppppppp/8/8/8/2N5/PPPPPPPP/R1BQKBNR b KQkq -": 2, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -": 2 }
{ "r1bqkbnr/pppppppp/2n5/8/8/8/PPPPPPPP/RNBQKBNR b KQkq -": 2, "r1bqkbnr/pppppppp/2n5/8/8/2N5/PPPPPPPP/R1BQKBNR w KQkq -": 2, "rnbqkbnr/pppppppp/8/8/8/2N5/PPPPPPPP/R1BQKBNR b KQkq -": 2, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -": 3 }
Hashing¶
#include <chesscxx/game.h>
#include <chesscxx/parse.h>
#include <chesscxx/position.h>
#include <chesscxx/san_move.h>
#include <cstdlib>
#include <functional>
#include <string_view>
namespace {
void verify(const auto& check) {
if (!static_cast<bool>(check)) std::abort();
}
auto parseSanMove(std::string_view str) -> chesscxx::SanMove {
auto parsed_san_move = chesscxx::parse<chesscxx::SanMove>(str);
verify(parsed_san_move);
return parsed_san_move.value();
}
auto parsePosition(std::string_view str) -> chesscxx::Position {
auto parsed_position = chesscxx::parse<chesscxx::Position>(str);
verify(parsed_position);
return parsed_position.value();
}
void assertEqual(const auto& lhs, const auto& rhs) {
verify(lhs == rhs);
verify(std::hash<chesscxx::Game>{}(lhs) == std::hash<chesscxx::Game>{}(rhs));
}
void assertNotEqual(const auto& lhs, const auto& rhs) {
verify(lhs != rhs);
// These hashes may collide but are likely different
verify(std::hash<chesscxx::Game>{}(lhs) != std::hash<chesscxx::Game>{}(rhs));
}
} // namespace
auto main() -> int {
chesscxx::Game uninitialized;
chesscxx::Game const standard = chesscxx::Game(parsePosition(
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"));
assertEqual(uninitialized, standard);
chesscxx::Game const custom = chesscxx::Game(parsePosition(
"rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"));
assertNotEqual(uninitialized, custom);
verify(uninitialized.move(parseSanMove("e4")));
assertNotEqual(uninitialized, custom);
assertNotEqual(uninitialized, standard);
uninitialized.undoMove();
assertEqual(uninitialized, standard);
}
Output: