summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLoic Guegan <manzerbredes@mailbox.org>2022-01-29 11:52:47 +0100
committerLoic Guegan <manzerbredes@mailbox.org>2022-01-29 11:52:47 +0100
commite9d328acf4ee45bd8771d422fa4db40298e6e16a (patch)
tree68a86c53539fe57b434c49555feafac9081f75e0 /src
Init project
Diffstat (limited to 'src')
-rw-r--r--src/Board.cpp196
-rw-r--r--src/Board.hpp44
-rw-r--r--src/ChessArbiter.cpp309
-rw-r--r--src/ChessArbiter.hpp40
-rw-r--r--src/Fen.cpp170
-rw-r--r--src/Fen.hpp41
-rw-r--r--src/Piece.cpp103
-rw-r--r--src/Piece.hpp27
8 files changed, 930 insertions, 0 deletions
diff --git a/src/Board.cpp b/src/Board.cpp
new file mode 100644
index 0000000..afd32e3
--- /dev/null
+++ b/src/Board.cpp
@@ -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 \ No newline at end of file
diff --git a/src/Board.hpp b/src/Board.hpp
new file mode 100644
index 0000000..aee2e10
--- /dev/null
+++ b/src/Board.hpp
@@ -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 \ No newline at end of file
diff --git a/src/ChessArbiter.cpp b/src/ChessArbiter.cpp
new file mode 100644
index 0000000..37dd5e2
--- /dev/null
+++ b/src/ChessArbiter.cpp
@@ -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 \ No newline at end of file
diff --git a/src/ChessArbiter.hpp b/src/ChessArbiter.hpp
new file mode 100644
index 0000000..5739904
--- /dev/null
+++ b/src/ChessArbiter.hpp
@@ -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 \ No newline at end of file
diff --git a/src/Fen.cpp b/src/Fen.cpp
new file mode 100644
index 0000000..32c66a2
--- /dev/null
+++ b/src/Fen.cpp
@@ -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 \ No newline at end of file
diff --git a/src/Fen.hpp b/src/Fen.hpp
new file mode 100644
index 0000000..a477bb0
--- /dev/null
+++ b/src/Fen.hpp
@@ -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
diff --git a/src/Piece.cpp b/src/Piece.cpp
new file mode 100644
index 0000000..6f9a3be
--- /dev/null
+++ b/src/Piece.cpp
@@ -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 \ No newline at end of file
diff --git a/src/Piece.hpp b/src/Piece.hpp
new file mode 100644
index 0000000..73791a0
--- /dev/null
+++ b/src/Piece.hpp
@@ -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 \ No newline at end of file