mirror of
https://gitlab.com/manzerbredes/chessarbiter.git
synced 2025-05-23 07:37:40 +00:00
Init project
This commit is contained in:
commit
e9d328acf4
19 changed files with 23418 additions and 0 deletions
196
src/Board.cpp
Normal file
196
src/Board.cpp
Normal file
|
@ -0,0 +1,196 @@
|
|||
#include "Board.hpp"
|
||||
|
||||
namespace chessarbiter {
|
||||
|
||||
bool Board::IsEmpty(std::string coord) {
|
||||
for (Piece &p : pieces) {
|
||||
if (p.coord == coord) {
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool Board::AddPiece(char p, std::string coord) {
|
||||
if (IsEmpty(coord)) {
|
||||
Piece piece(p, coord);
|
||||
pieces.push_back(piece);
|
||||
return (true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool Board::RemovePiece(std::string coord) {
|
||||
for (char i = 0; i < pieces.size(); i++) {
|
||||
if (pieces[i].coord == coord) {
|
||||
pieces.erase(pieces.begin() + i);
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Piece Board::GetPieceAt(std::string coord) {
|
||||
for (Piece &p : pieces) {
|
||||
if (p.coord == coord)
|
||||
return p;
|
||||
}
|
||||
throw NoPieceFound();
|
||||
}
|
||||
|
||||
std::vector<Piece> Board::GetPlayerPieces(bool isBlack) {
|
||||
std::vector<Piece> pp;
|
||||
for (Piece &p : pieces) {
|
||||
if (p.isBlack == isBlack) {
|
||||
pp.push_back(p);
|
||||
}
|
||||
}
|
||||
return (pp);
|
||||
}
|
||||
|
||||
short Board::CountPiece(char c) {
|
||||
char count = 0;
|
||||
for (Piece &p : pieces) {
|
||||
if (p.piece == c) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return (count);
|
||||
}
|
||||
|
||||
void Board::Clear() { pieces.clear(); }
|
||||
|
||||
std::string Board::GetKingLocation(bool isBlack) {
|
||||
for (Piece &p : pieces) {
|
||||
if (p.isBlack == isBlack) {
|
||||
if (p.piece == 'k' || p.piece == 'K') {
|
||||
return (p.coord);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw NoPieceFound();
|
||||
}
|
||||
|
||||
void Board::Move(std::string move) {
|
||||
std::string src(move.substr(0, 2));
|
||||
std::string dst(move.substr(2, 2));
|
||||
if (!IsEmpty(src)) {
|
||||
if (!IsEmpty(dst)) {
|
||||
RemovePiece(dst);
|
||||
}
|
||||
for (Piece &p : pieces) {
|
||||
if (p.coord == src) {
|
||||
p.coord = dst;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Board::Serialize() {
|
||||
std::string s;
|
||||
for (short i = 0; i < 8; i++) {
|
||||
for (short j = 0; j < 8; j++) {
|
||||
try {
|
||||
Piece p = GetPieceAt((char)('a' + j) + std::string() + (char)('8' - i));
|
||||
s += p.piece;
|
||||
} catch (...) {
|
||||
s += " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
return (s);
|
||||
}
|
||||
|
||||
bool Board::IsMovePossible(std::string move) {
|
||||
std::string src(move.substr(0, 2));
|
||||
std::string dst(move.substr(2, 2));
|
||||
if (src == dst) {
|
||||
return (false);
|
||||
}
|
||||
Piece srcp = GetPieceAt(src);
|
||||
|
||||
// Check move is possible
|
||||
std::vector<std::string> srcm = srcp.GetMoves();
|
||||
if (find(srcm.begin(), srcm.end(), dst) == srcm.end()) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
// Check colors on dst square
|
||||
if (!IsEmpty(dst)) {
|
||||
Piece dstp = GetPieceAt(dst);
|
||||
if (srcp.isBlack == dstp.isBlack)
|
||||
return (false);
|
||||
}
|
||||
|
||||
// Check if no piece in between
|
||||
if (src[0] == dst[0] || src[1] == dst[1]) { // Rook like moves
|
||||
if (src[0] == dst[0]) { // Vertical
|
||||
char side = 1;
|
||||
if (src[1] > dst[1]) {
|
||||
side = -1;
|
||||
}
|
||||
char r = src[1] + 1 * side;
|
||||
while (r != dst[1]) {
|
||||
if (!IsEmpty(src[0] + std::string() + r)) {
|
||||
return (false);
|
||||
}
|
||||
r += 1 * side;
|
||||
}
|
||||
} else { // Horizontal
|
||||
char side = 1;
|
||||
if (src[0] > dst[0]) {
|
||||
side = -1;
|
||||
}
|
||||
char f = src[0] + 1 * side;
|
||||
while (f != dst[0]) {
|
||||
if (!IsEmpty(f + std::string() + src[1])) {
|
||||
return (false);
|
||||
}
|
||||
f += 1 * side;
|
||||
}
|
||||
}
|
||||
} else if ((src[0] - dst[0] == src[1] - dst[1]) ||
|
||||
(dst[0] - src[0] == src[1] - dst[1])) { // Bishop like moves
|
||||
// That code if for diagonal 1 (bottom left to up right)
|
||||
// Using the d2 variable allow to reuse the same code for the 2 diagonals
|
||||
char d2 = 1; // d2 stand for diagonal 2 (bottom right to up left)
|
||||
if ((dst[0] - src[0] == src[1] - dst[1])) { // If move is for diagonal 2
|
||||
d2 = -1;
|
||||
}
|
||||
|
||||
// Move direction check
|
||||
char side = 1;
|
||||
if (src[0] > dst[0]) {
|
||||
side = -1;
|
||||
}
|
||||
|
||||
// Setup variables
|
||||
char f = src[0] + 1 * side;
|
||||
char r = src[1] + d2 * side;
|
||||
|
||||
// Perform empty square checks
|
||||
while (f != dst[0], r != dst[1]) {
|
||||
if (!IsEmpty(f + std::string() + r)) {
|
||||
return (false);
|
||||
}
|
||||
f += 1 * side;
|
||||
r += d2 * side;
|
||||
}
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
std::vector<std::string> Board::ListPossibleMoves(bool isBlack) {
|
||||
std::vector<std::string> moves;
|
||||
for (Piece &p : pieces) {
|
||||
if (p.isBlack == isBlack) {
|
||||
for (std::string &m : p.GetMoves()) {
|
||||
if (IsMovePossible(p.coord + m)) {
|
||||
moves.push_back(p.coord + m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (moves);
|
||||
}
|
||||
} // namespace chessarbiter
|
44
src/Board.hpp
Normal file
44
src/Board.hpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include "Piece.hpp"
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace chessarbiter {
|
||||
|
||||
class Board {
|
||||
std::vector<Piece> pieces;
|
||||
|
||||
public:
|
||||
/// @brief Check if a square is empty
|
||||
bool IsEmpty(std::string);
|
||||
/// @brief Add a piece (no checks are performed on coord)
|
||||
bool AddPiece(char p, std::string);
|
||||
/// @brief Remove a piece from a square
|
||||
bool RemovePiece(std::string);
|
||||
/// @brief Get piece at a specific coordinate
|
||||
Piece GetPieceAt(std::string);
|
||||
/// @brief Get the pieces of a player
|
||||
std::vector<Piece> GetPlayerPieces(bool);
|
||||
/// @brief Count the number of a specific piece on the board
|
||||
short CountPiece(char);
|
||||
/// @brief Get the location of the first king found on the board
|
||||
std::string GetKingLocation(bool);
|
||||
/// @brief Check if a move is technically possible (does not means it is
|
||||
/// legal)
|
||||
bool IsMovePossible(std::string);
|
||||
/// @brief Clear the board
|
||||
void Clear();
|
||||
/// @brief Move a piece somewhere no matter what
|
||||
void Move(std::string);
|
||||
/// @brief Get a serialize version of the board
|
||||
std::string Serialize();
|
||||
/// @brief List all the technically possible moves of a player
|
||||
std::vector<std::string> ListPossibleMoves(bool);
|
||||
};
|
||||
|
||||
struct NoPieceFound : public std::exception {
|
||||
const char *what() const throw() { return "No piece found"; }
|
||||
};
|
||||
|
||||
} // namespace chessarbiter
|
309
src/ChessArbiter.cpp
Normal file
309
src/ChessArbiter.cpp
Normal file
|
@ -0,0 +1,309 @@
|
|||
#include "ChessArbiter.hpp"
|
||||
|
||||
namespace chessarbiter {
|
||||
ChessArbiter::ChessArbiter()
|
||||
: wPawn(1), wRook(5), wKnight(3), wBishop(3), wQueen(9), wKing(0) {}
|
||||
|
||||
void ChessArbiter::Setup(std::string fen) {
|
||||
SetFEN(fen);
|
||||
fen_last = this->fen;
|
||||
}
|
||||
|
||||
void ChessArbiter::SetFEN(FEN fen) { SetFEN(FENParser::Serialize(fen)); }
|
||||
|
||||
void ChessArbiter::SetFEN(std::string newfen) {
|
||||
fen = FENParser::Parse(newfen);
|
||||
|
||||
board.Clear();
|
||||
for (int i = 0; i < 64; i++) {
|
||||
if (fen.board[i] != ' ') {
|
||||
char f = 'a' + ((i) % 8);
|
||||
char r = '8' - ((i) / 8);
|
||||
board.AddPiece(fen.board[i], f + std::string() + r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ChessArbiter::GetFEN() { return (FENParser::Serialize(fen)); }
|
||||
|
||||
std::string ChessArbiter::GetBoard() { return (fen.board); }
|
||||
|
||||
bool ChessArbiter::IsBlackTurn() { return (fen.player == 'b'); }
|
||||
|
||||
bool ChessArbiter::IsCheck(bool isBlack) {
|
||||
std::string kingloc = board.GetKingLocation(isBlack);
|
||||
return (IsAttacked(kingloc, !isBlack));
|
||||
}
|
||||
|
||||
bool ChessArbiter::Play(std::string move) {
|
||||
std::vector<std::string> moves = ListLegalMoves(fen.player);
|
||||
if (find(moves.begin(), moves.end(), move) != moves.end()) {
|
||||
Piece moved = board.GetPieceAt(move.substr(0, 2)); // This call never fail
|
||||
std::string src = move.substr(0, 2);
|
||||
std::string dst = move.substr(2, 2);
|
||||
bool IsCapture = !board.IsEmpty(dst);
|
||||
FEN newFen = fen;
|
||||
|
||||
// Perform the move
|
||||
if (move == "O-O" || move == "O-O-O") {
|
||||
if (fen.player && move == "O-O") {
|
||||
board.Move("e8g8");
|
||||
board.Move("h8e8");
|
||||
} else if (fen.player && move == "O-O") {
|
||||
board.Move("e8c8");
|
||||
board.Move("a8d8");
|
||||
} else if (!fen.player && move == "O-O") {
|
||||
board.Move("e1g1");
|
||||
board.Move("h1e1");
|
||||
} else {
|
||||
board.Move("e1c1");
|
||||
board.Move("a1d1");
|
||||
}
|
||||
} else {
|
||||
board.Move(move);
|
||||
}
|
||||
|
||||
// Update halfmove
|
||||
newFen.halfmove++;
|
||||
// Check enpassant
|
||||
newFen.en_passant = "-";
|
||||
if (moved.piece == 'p' || moved.piece == 'P') {
|
||||
if (fen.player && (dst[1] - src[1] == 2)) {
|
||||
newFen.en_passant = src[0] + std::string() + (char)(src[1] - 1);
|
||||
} else if (!fen.player && (dst[1] - src[1] == 2)) {
|
||||
newFen.en_passant = src[0] + std::string() + (char)(src[1] + 1);
|
||||
}
|
||||
newFen.halfmove = 0; // Pawn moves reset half moves
|
||||
}
|
||||
// Captures reset half moves
|
||||
if (IsCapture) {
|
||||
newFen.halfmove = 0;
|
||||
}
|
||||
if (newFen.player) {
|
||||
newFen.move++;
|
||||
}
|
||||
newFen.board = board.Serialize();
|
||||
newFen.player = !newFen.player;
|
||||
// Castle update if one is true
|
||||
if (newFen.white_castle_long || newFen.white_castle_short ||
|
||||
newFen.black_castle_long || newFen.black_castle_short) {
|
||||
if (moved.piece == 'R' && src == "a1") {
|
||||
newFen.white_castle_long = false;
|
||||
} else if (moved.piece == 'R' && src == "h1") {
|
||||
newFen.white_castle_short = false;
|
||||
} else if (moved.piece == 'r' && src == "a8") {
|
||||
newFen.black_castle_long = false;
|
||||
} else if (moved.piece == 'r' && src == "h8") {
|
||||
newFen.black_castle_short = false;
|
||||
} else if (moved.piece == 'K' || (!fen.player && src == "O-")) {
|
||||
newFen.white_castle_long = false;
|
||||
newFen.white_castle_short = false;
|
||||
} else if (moved.piece == 'k' || (fen.player && src == "O-")) {
|
||||
newFen.black_castle_long = false;
|
||||
newFen.black_castle_short = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update fen!
|
||||
fen_last = fen;
|
||||
fen = newFen;
|
||||
|
||||
// Check for illegal move
|
||||
if (IsCheck(!fen.player)) {
|
||||
SetFEN(fen_last);
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool ChessArbiter::IsAttacked(std::string square, bool by) {
|
||||
std::vector<std::string> moves = board.ListPossibleMoves(by);
|
||||
for (std::string &m : moves) {
|
||||
std::string src = m.substr(0, 2);
|
||||
std::string dst = m.substr(2, 2);
|
||||
|
||||
if (dst == square) {
|
||||
// Pawn do not attack forward
|
||||
Piece p = board.GetPieceAt(src);
|
||||
if (p.piece == 'p' || p.piece == 'P') {
|
||||
if (src[0] != dst[0]) {
|
||||
return (true);
|
||||
}
|
||||
} else {
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool ChessArbiter::IsCastlePossible(bool isBlack, bool isLong) {
|
||||
|
||||
if (isBlack && isLong && fen.black_castle_long) {
|
||||
if (board.IsEmpty("d8") && board.IsEmpty("c8") && board.IsEmpty("b8")) {
|
||||
if (!IsAttacked("d8", false) && !IsAttacked("c8", false)) {
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
} else if (isBlack && !isLong && fen.black_castle_short) {
|
||||
if (board.IsEmpty("f8") && board.IsEmpty("g8")) {
|
||||
if (!IsAttacked("f8", false) && !IsAttacked("g8", false)) {
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
} else if (!isBlack && isLong && fen.white_castle_long) {
|
||||
if (board.IsEmpty("d1") && board.IsEmpty("c1") && board.IsEmpty("b1")) {
|
||||
if (!IsAttacked("d1", true) && !IsAttacked("c1", true)) {
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
} else if (!isBlack && !isLong && fen.white_castle_short) {
|
||||
if (board.IsEmpty("f1") && board.IsEmpty("g1")) {
|
||||
if (!IsAttacked("f1", true) && !IsAttacked("g1", true)) {
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
int ChessArbiter::GetMaterialScore() {
|
||||
int whiteScore = 0;
|
||||
int blackScore = 0;
|
||||
for (char i = 0; i < 2; i++) {
|
||||
int *score = &whiteScore;
|
||||
if (i > 0) {
|
||||
score = &blackScore;
|
||||
}
|
||||
for (Piece &p : board.GetPlayerPieces((bool)i)) {
|
||||
switch (tolower(p.piece)) {
|
||||
case 'p':
|
||||
(*score) += wPawn;
|
||||
break;
|
||||
case 'r':
|
||||
(*score) += wRook;
|
||||
break;
|
||||
case 'n':
|
||||
(*score) += wKnight;
|
||||
break;
|
||||
case 'b':
|
||||
(*score) += wBishop;
|
||||
break;
|
||||
case 'q':
|
||||
(*score) += wQueen;
|
||||
break;
|
||||
default:
|
||||
(*score) += wKing;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (whiteScore - blackScore);
|
||||
}
|
||||
|
||||
std::string ChessArbiter::GetCaptures(bool isBlack) {
|
||||
std::string captures;
|
||||
// Pawn
|
||||
char p = 'P';
|
||||
if (!isBlack)
|
||||
p = 'p';
|
||||
for (char i = 8 - board.CountPiece(p); i > 0; i--) {
|
||||
captures += p;
|
||||
}
|
||||
// Rook
|
||||
p = 'R';
|
||||
if (!isBlack)
|
||||
p = 'r';
|
||||
for (char i = 2 - board.CountPiece(p); i > 0; i--) {
|
||||
captures += p;
|
||||
}
|
||||
// Knight
|
||||
p = 'N';
|
||||
if (!isBlack)
|
||||
p = 'n';
|
||||
for (char i = 2 - board.CountPiece(p); i > 0; i--) {
|
||||
captures += p;
|
||||
}
|
||||
// Bishop
|
||||
p = 'B';
|
||||
if (!isBlack)
|
||||
p = 'b';
|
||||
for (char i = 2 - board.CountPiece(p); i > 0; i--) {
|
||||
captures += p;
|
||||
}
|
||||
// Queen
|
||||
p = 'Q';
|
||||
if (!isBlack)
|
||||
p = 'q';
|
||||
for (char i = 1 - board.CountPiece(p); i > 0; i--) {
|
||||
captures += p;
|
||||
}
|
||||
// King :D
|
||||
p = 'K';
|
||||
if (!isBlack)
|
||||
p = 'k';
|
||||
for (char i = 1 - board.CountPiece(p); i > 0; i--) {
|
||||
captures += p;
|
||||
}
|
||||
return (captures);
|
||||
}
|
||||
|
||||
std::vector<std::string> ChessArbiter::ListLegalMoves(bool isBlack) {
|
||||
std::vector<std::string> moves;
|
||||
for (std::string &move : board.ListPossibleMoves(isBlack)) {
|
||||
std::string src = move.substr(0, 2);
|
||||
std::string dst = move.substr(2, 2);
|
||||
Piece srcp = board.GetPieceAt(src); // This call never fail
|
||||
bool IsDstEmpty = board.IsEmpty(dst);
|
||||
|
||||
// Pawns side moves
|
||||
if ((srcp.piece == 'p' || srcp.piece == 'P') && (src[0] != dst[0])) {
|
||||
if (!IsDstEmpty) {
|
||||
Piece attacked = board.GetPieceAt(dst);
|
||||
if (srcp.isBlack != attacked.isBlack)
|
||||
moves.push_back(move);
|
||||
} else if (dst == fen.en_passant) {
|
||||
moves.push_back(move);
|
||||
}
|
||||
} else {
|
||||
moves.push_back(move);
|
||||
}
|
||||
}
|
||||
|
||||
// Casling
|
||||
if (IsCastlePossible(isBlack, false))
|
||||
moves.push_back("O-O");
|
||||
if (IsCastlePossible(isBlack, true))
|
||||
moves.push_back("O-O-O");
|
||||
|
||||
return (moves);
|
||||
}
|
||||
|
||||
bool ChessArbiter::IsPlayable() {
|
||||
char nK = board.CountPiece('K');
|
||||
if (nK == 1 && nK == board.CountPiece('k')) {
|
||||
if (!IsCheck(!fen.player)) {
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
bool ChessArbiter::IsCheckMate() {
|
||||
if (IsCheck(fen.player)) {
|
||||
std::vector<std::string> moves = ListLegalMoves(fen.player);
|
||||
for(std::string &move: moves){
|
||||
if(Play(move)){
|
||||
SetFEN(fen_last);
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
} // namespace chessarbiter
|
40
src/ChessArbiter.hpp
Normal file
40
src/ChessArbiter.hpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include "Board.hpp"
|
||||
#include "Fen.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace chessarbiter {
|
||||
class ChessArbiter {
|
||||
Board board;
|
||||
FEN fen;
|
||||
FEN fen_last; // To undo a move
|
||||
int wPawn, wRook, wKnight, wBishop, wQueen, wKing;
|
||||
|
||||
public:
|
||||
ChessArbiter();
|
||||
void Setup(std::string);
|
||||
void SetFEN(std::string);
|
||||
void SetFEN(FEN);
|
||||
std::string GetFEN();
|
||||
/// @brief Check which player is going to play
|
||||
bool IsBlackTurn();
|
||||
/// @brief Check if a side is in check
|
||||
bool IsCheck(bool);
|
||||
/// @brief Play a move (return false if it's illegal)
|
||||
bool Play(std::string);
|
||||
/// @brief Check if a square is attacked by a particular player
|
||||
bool IsAttacked(std::string, bool);
|
||||
/// @brief Get the serialized board
|
||||
std::string GetBoard();
|
||||
/// @brief Get current position evaluation according to player's material
|
||||
int GetMaterialScore();
|
||||
/// @brief Check if position is legal
|
||||
bool IsPlayable();
|
||||
/// @brief Get pieces captures by a player
|
||||
std::string GetCaptures(bool);
|
||||
/// @brief List all the legal moves of a player
|
||||
std::vector<std::string> ListLegalMoves(bool);
|
||||
/// @brief Check if a specific castle is possible by a player
|
||||
bool IsCastlePossible(bool, bool);
|
||||
bool IsCheckMate();
|
||||
};
|
||||
} // namespace chessarbiter
|
170
src/Fen.cpp
Normal file
170
src/Fen.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
#include "Fen.hpp"
|
||||
|
||||
namespace chessarbiter {
|
||||
|
||||
std::string FENParser::normalize_rank(std::string fen_rank) {
|
||||
std::string normalized;
|
||||
for (char &c : fen_rank) {
|
||||
if (IS_DIGIT(c)) {
|
||||
for (char i = 0; i < (c - '0'); i++) {
|
||||
normalized += ' ';
|
||||
}
|
||||
} else {
|
||||
normalized += c;
|
||||
}
|
||||
}
|
||||
return (normalized);
|
||||
}
|
||||
|
||||
char FENParser::NextToken(std::string fen, char loc) {
|
||||
while (loc < fen.size() && IS_BLANK(fen[loc])) {
|
||||
loc++;
|
||||
}
|
||||
return (loc);
|
||||
}
|
||||
|
||||
char FENParser::NextRank(std::string fen, char loc) {
|
||||
loc++;
|
||||
while (loc < fen.size() && fen[loc] != '/' && fen[loc] != ' ') {
|
||||
loc++;
|
||||
}
|
||||
return (loc);
|
||||
}
|
||||
|
||||
std::string FENParser::Serialize(FEN fen) {
|
||||
std::string s;
|
||||
char skip = 0;
|
||||
char rank = 0;
|
||||
for (char &c : fen.board) {
|
||||
rank++;
|
||||
if (c == ' ') {
|
||||
skip++;
|
||||
} else {
|
||||
if (skip > 0) {
|
||||
s += std::to_string(skip);
|
||||
skip = 0;
|
||||
}
|
||||
s += c;
|
||||
}
|
||||
if (rank == 8) {
|
||||
if (skip != 0) {
|
||||
s += std::to_string(skip);
|
||||
skip = 0;
|
||||
}
|
||||
s += '/';
|
||||
rank = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove last /
|
||||
s = s.substr(0, s.size() - 1);
|
||||
s += " ";
|
||||
|
||||
// Player
|
||||
if (fen.player) {
|
||||
s += "b ";
|
||||
} else {
|
||||
s += "w ";
|
||||
}
|
||||
|
||||
// Castle
|
||||
if (!(fen.white_castle_short || fen.white_castle_long ||
|
||||
fen.black_castle_short || fen.black_castle_long)) {
|
||||
s += "-";
|
||||
} else {
|
||||
if (fen.white_castle_short) {
|
||||
s += "K";
|
||||
}
|
||||
if (fen.white_castle_long) {
|
||||
s += "Q";
|
||||
}
|
||||
if (fen.black_castle_short) {
|
||||
s += "k";
|
||||
}
|
||||
if (fen.black_castle_long) {
|
||||
s += "q";
|
||||
}
|
||||
}
|
||||
|
||||
// Remaining
|
||||
s += " " + fen.en_passant + " " + std::to_string(fen.halfmove) + " " +
|
||||
std::to_string(fen.move);
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
FEN FENParser::Parse(std::string fen) {
|
||||
FEN parsed;
|
||||
|
||||
// Parse board
|
||||
char loc = 0;
|
||||
for (char rank = 0; rank < 8; rank++) {
|
||||
char newloc = NextRank(fen, loc);
|
||||
parsed.board += normalize_rank(fen.substr(loc, newloc - loc));
|
||||
loc = newloc + 1;
|
||||
}
|
||||
|
||||
// Parse player to move
|
||||
loc = NextToken(fen, loc);
|
||||
parsed.player = fen[loc] == 'b';
|
||||
|
||||
// Parse castling
|
||||
loc = NextToken(fen, loc + 1);
|
||||
char length = 0;
|
||||
char cur_loc = loc;
|
||||
while (!IS_BLANK(fen[cur_loc])) {
|
||||
length++;
|
||||
cur_loc++;
|
||||
}
|
||||
parsed.white_castle_short = false;
|
||||
parsed.white_castle_long = false;
|
||||
parsed.black_castle_short = false;
|
||||
parsed.black_castle_long = false;
|
||||
std::string castle = fen.substr(loc, length);
|
||||
for (char i = 0; i < length; i++) {
|
||||
switch (fen[loc + i]) {
|
||||
case 'K':
|
||||
parsed.white_castle_short = true;
|
||||
break;
|
||||
case 'Q':
|
||||
parsed.white_castle_long = true;
|
||||
break;
|
||||
case 'k':
|
||||
parsed.black_castle_short = true;
|
||||
break;
|
||||
case 'q':
|
||||
parsed.black_castle_long = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse en passant
|
||||
loc = NextToken(fen, loc + length);
|
||||
if (fen[loc] != '-') {
|
||||
parsed.en_passant = fen.substr(loc, 2);
|
||||
loc++;
|
||||
}
|
||||
loc++;
|
||||
|
||||
// Parse half move counter
|
||||
loc = NextToken(fen, loc);
|
||||
std::string halfmove;
|
||||
while (!IS_BLANK(fen[loc])) {
|
||||
halfmove += fen[loc];
|
||||
loc++;
|
||||
}
|
||||
parsed.halfmove = stoi(halfmove);
|
||||
|
||||
// Parse move counter
|
||||
loc = NextToken(fen, loc);
|
||||
std::string move;
|
||||
while (loc < fen.size() && !IS_BLANK(fen[loc])) {
|
||||
move += fen[loc];
|
||||
loc++;
|
||||
}
|
||||
parsed.move = stoi(move);
|
||||
|
||||
return (parsed);
|
||||
}
|
||||
|
||||
} // namespace chessarbiter
|
41
src/Fen.hpp
Normal file
41
src/Fen.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#define IS_DIGIT(c) \
|
||||
(c == '0' || c == '1' || c == '2' || c == '3' || c == '4' || c == '5' || \
|
||||
c == '6' || c == '7' || c == '8' || c == '9')
|
||||
#define IS_BLANK(c) (c == ' ' || c == '\n' || c == '\t' || c == '\r')
|
||||
|
||||
namespace chessarbiter {
|
||||
|
||||
class FEN {
|
||||
public:
|
||||
std::string board;
|
||||
bool player;
|
||||
bool white_castle_short;
|
||||
bool white_castle_long;
|
||||
bool black_castle_short;
|
||||
bool black_castle_long;
|
||||
std::string en_passant;
|
||||
short halfmove;
|
||||
short move;
|
||||
FEN()
|
||||
: board(""), player(false), white_castle_short(true),
|
||||
white_castle_long(true), black_castle_short(true),
|
||||
black_castle_long(true), en_passant("-"), halfmove(0), move(1) {}
|
||||
};
|
||||
|
||||
class FENParser {
|
||||
private:
|
||||
static std::string normalize_rank(std::string fen_rank);
|
||||
static char NextToken(std::string fen, char loc);
|
||||
static char NextRank(std::string fen, char loc);
|
||||
|
||||
public:
|
||||
/// @brief Parse a FEN from a string
|
||||
static FEN Parse(std::string);
|
||||
/// @brief Generate a fen string from the FEN object
|
||||
static std::string Serialize(FEN fen);
|
||||
};
|
||||
} // namespace chessarbiter
|
103
src/Piece.cpp
Normal file
103
src/Piece.cpp
Normal file
|
@ -0,0 +1,103 @@
|
|||
#include "Piece.hpp"
|
||||
|
||||
namespace chessarbiter {
|
||||
Piece::Piece(char c, std::string coord)
|
||||
: piece(c), isBlack(!isupper(c)), coord(coord) {}
|
||||
|
||||
std::vector<std::string> Piece::GetMoves() {
|
||||
std::vector<std::string> moves;
|
||||
char f = coord[0]; // File
|
||||
char r = coord[1]; // Rank
|
||||
if (piece == 'p' || piece == 'P') {
|
||||
char side = 1;
|
||||
if (piece == 'p') {
|
||||
side = -1;
|
||||
}
|
||||
// First two steps
|
||||
if ((r == '2' && piece == 'P') || (r == '7' && piece == 'p')) {
|
||||
moves.push_back(std::string() + f + (char)(r + 2 * side));
|
||||
}
|
||||
if ((piece == 'P' && r < '8') || (piece == 'p' && r > '1')) {
|
||||
moves.push_back(std::string() + f + (char)(r + 1 * side));
|
||||
}
|
||||
PIECE__ADD_MOVE(f - 1, r + 1 * side);
|
||||
PIECE__ADD_MOVE(f + 1, r + 1 * side);
|
||||
} else if (piece == 'k' || piece == 'K') {
|
||||
PIECE__ADD_MOVE(f, r - 1);
|
||||
PIECE__ADD_MOVE(f, r + 1);
|
||||
PIECE__ADD_MOVE(f + 1, r);
|
||||
PIECE__ADD_MOVE(f - 1, r);
|
||||
PIECE__ADD_MOVE(f + 1, r + 1);
|
||||
PIECE__ADD_MOVE(f - 1, r - 1);
|
||||
PIECE__ADD_MOVE(f + 1, r - 1);
|
||||
PIECE__ADD_MOVE(f - 1, r + 1);
|
||||
} else if (piece == 'n' || piece == 'N') {
|
||||
PIECE__ADD_MOVE(f + 1, r + 2);
|
||||
PIECE__ADD_MOVE(f - 1, r + 2);
|
||||
PIECE__ADD_MOVE(f + 1, r - 2);
|
||||
PIECE__ADD_MOVE(f - 1, r - 2);
|
||||
PIECE__ADD_MOVE(f + 2, r + 1);
|
||||
PIECE__ADD_MOVE(f - 2, r + 1);
|
||||
PIECE__ADD_MOVE(f + 2, r - 1);
|
||||
PIECE__ADD_MOVE(f - 2, r - 1);
|
||||
} else {
|
||||
if (piece == 'b' || piece == 'B' || piece == 'Q' || piece == 'q') {
|
||||
char rtmp = r;
|
||||
char ftmp = f;
|
||||
while (PIECE__IS_VALID(ftmp, rtmp)) {
|
||||
ftmp++;
|
||||
rtmp++;
|
||||
PIECE__ADD_MOVE(ftmp, rtmp);
|
||||
}
|
||||
rtmp = r;
|
||||
ftmp = f;
|
||||
while (PIECE__IS_VALID(ftmp, rtmp)) {
|
||||
ftmp--;
|
||||
rtmp--;
|
||||
PIECE__ADD_MOVE(ftmp, rtmp);
|
||||
}
|
||||
rtmp = r;
|
||||
ftmp = f;
|
||||
while (PIECE__IS_VALID(ftmp, rtmp)) {
|
||||
ftmp--;
|
||||
rtmp++;
|
||||
PIECE__ADD_MOVE(ftmp, rtmp);
|
||||
}
|
||||
rtmp = r;
|
||||
ftmp = f;
|
||||
while (PIECE__IS_VALID(ftmp, rtmp)) {
|
||||
ftmp++;
|
||||
rtmp--;
|
||||
PIECE__ADD_MOVE(ftmp, rtmp);
|
||||
}
|
||||
}
|
||||
if (piece == 'r' || piece == 'R' || piece == 'Q' || piece == 'q') {
|
||||
char rtmp = r;
|
||||
char ftmp = f;
|
||||
while (PIECE__IS_VALID(ftmp, rtmp)) {
|
||||
rtmp++;
|
||||
PIECE__ADD_MOVE(ftmp, rtmp);
|
||||
}
|
||||
rtmp = r;
|
||||
ftmp = f;
|
||||
while (PIECE__IS_VALID(ftmp, rtmp)) {
|
||||
rtmp--;
|
||||
PIECE__ADD_MOVE(ftmp, rtmp);
|
||||
}
|
||||
rtmp = r;
|
||||
ftmp = f;
|
||||
while (PIECE__IS_VALID(ftmp, rtmp)) {
|
||||
ftmp++;
|
||||
PIECE__ADD_MOVE(ftmp, rtmp);
|
||||
}
|
||||
rtmp = r;
|
||||
ftmp = f;
|
||||
while (PIECE__IS_VALID(ftmp, rtmp)) {
|
||||
ftmp--;
|
||||
PIECE__ADD_MOVE(ftmp, rtmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (moves);
|
||||
}
|
||||
} // namespace chessarbiter
|
27
src/Piece.hpp
Normal file
27
src/Piece.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define PIECE__IS_VALID(f, r) ((f) >= 'a' && (f) <= 'h' && (r) >= '1' && (r) <= '8')
|
||||
#define PIECE__ADD_MOVE(f, r) \
|
||||
{ \
|
||||
if (PIECE__IS_VALID(f, r)) { \
|
||||
moves.push_back(std::string() + (char)((f)) + (char)((r))); \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace chessarbiter {
|
||||
|
||||
/**
|
||||
* Member are public for conveniance
|
||||
*/
|
||||
class Piece {
|
||||
public:
|
||||
bool isBlack;
|
||||
std::string coord;
|
||||
char piece;
|
||||
Piece(char c, std::string coord);
|
||||
/// @brief Get all possible moves according to the type of piece and its position
|
||||
std::vector<std::string> GetMoves();
|
||||
};
|
||||
|
||||
} // namespace chessarbiter
|
Loading…
Add table
Add a link
Reference in a new issue