summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLoic Guegan <manzerbredes@mailbox.org>2022-02-19 18:23:06 +0100
committerLoic Guegan <manzerbredes@mailbox.org>2022-02-19 18:23:06 +0100
commit5e78a4172da975ac227779456e670492217de206 (patch)
tree8f71add7ce8323b83b2810e6484a89c462835a00
parentf60b684790ae7a726763e4c6f9122371c71d17e9 (diff)
Now SAN moves can be parsed
-rw-r--r--src/ChessArbiter.cpp109
-rw-r--r--src/ChessArbiter.hpp1
-rw-r--r--tests/chessarbiter.cpp33
3 files changed, 143 insertions, 0 deletions
diff --git a/src/ChessArbiter.cpp b/src/ChessArbiter.cpp
index 18e1e1c..e747397 100644
--- a/src/ChessArbiter.cpp
+++ b/src/ChessArbiter.cpp
@@ -400,4 +400,113 @@ bool ChessArbiter::IsCheckMate() {
std::string ChessArbiter::GetSAN() { return (SAN); }
char ChessArbiter::GetCapture() { return (capture); }
+
+std::string ChessArbiter::ParseSAN(std::string SANMove) {
+ std::string src, dst;
+ char piece = ' ';
+ char hint = ' ';
+ bool isHintRank = false;
+
+ // First castling
+ if (SANMove[0] == 'O' || SANMove[0] == '0') {
+ char c3 = (SANMove.size() >= 3) ? SANMove[3] : '?';
+ // Long castle
+ if (c3 == '-') {
+ if (fen.player && IsCastlePossible(fen.player, true)) {
+ return ("e8c8");
+ } else if (IsCastlePossible(fen.player, true)) {
+ return ("e1c1");
+ }
+ } else {
+ if (fen.player && IsCastlePossible(fen.player, false)) {
+ return ("e8g8");
+ } else if (IsCastlePossible(fen.player, false)) {
+ return ("e1g1");
+ }
+ }
+ }
+
+ // First deduce dst square in the move
+ if (SANMove.size() > 0) {
+ // Pawn moves
+ if (std::islower(SANMove[0])) {
+ if (fen.player) {
+ piece = 'p';
+ } else {
+ piece = 'P';
+ }
+ // Not a capture
+ if (SANMove[1] != 'x') {
+ dst = SANMove.substr(0, 2);
+ } else {
+ dst = SANMove.substr(2, 2);
+ }
+ } else {
+ piece = SANMove[0];
+ char c1 = (SANMove.size() >= 2) ? SANMove[1] : '?';
+ char c2 = (SANMove.size() >= 3) ? SANMove[2] : '?';
+ char c3 = (SANMove.size() >= 4) ? SANMove[3] : '?';
+ if (c1 == 'x') {
+ dst = SANMove.substr(2, 2);
+ } else if (c2 == 'x') {
+ hint = c1;
+ dst = SANMove.substr(3, 2);
+ } else if (IS_DIGIT(c2)) {
+ dst = SANMove.substr(1, 2);
+ } else {
+ hint = c1;
+ dst = SANMove.substr(2, 2);
+ }
+ }
+ }
+ isHintRank = IS_DIGIT(hint);
+
+ // Now find src thanks to legal moves
+ std::vector<std::string> src_candidates;
+ for (std::string &move : ListLegalMoves(fen.player)) {
+ std::string current_src = move.substr(0, 2);
+ std::string current_dst = move.substr(2, 2);
+ if (current_dst == dst) {
+ src_candidates.push_back(current_src);
+ }
+ }
+
+ // Now filter the legals move
+ if (src_candidates.size() > 0) {
+ if (src_candidates.size() > 1) {
+ std::vector<std::string> src_candidates_filtered;
+ // Filter according to pieces:
+ for (std::string &cand : src_candidates) {
+ Piece p = board.GetPieceAt(cand); // This call never fails
+ if (std::toupper(p.piece) == piece) {
+ src_candidates_filtered.push_back(cand);
+ }
+ }
+ src_candidates = src_candidates_filtered;
+ src_candidates_filtered.clear();
+ // Last Filtering
+ if (src_candidates.size() > 1) {
+ for (std::string &cand : src_candidates) {
+ char cand_hint = cand[0];
+ if (isHintRank) {
+ cand_hint = cand[1];
+ }
+ if (hint == cand_hint) {
+ src_candidates_filtered.push_back(cand);
+ }
+ }
+ }
+ src_candidates = src_candidates_filtered;
+ }
+ src = src_candidates[0];
+ }
+
+ // Ensure that we return empty string if no matches
+ if(src.size()<=0){
+ return("");
+ }
+ // Else return "srcdst" string
+ return (src + dst);
+}
+
} // namespace chessarbiter \ No newline at end of file
diff --git a/src/ChessArbiter.hpp b/src/ChessArbiter.hpp
index 6d31073..e012b4a 100644
--- a/src/ChessArbiter.hpp
+++ b/src/ChessArbiter.hpp
@@ -65,5 +65,6 @@ public:
bool IsDrawByNoMoves();
bool IsDrawByRepetitions();
bool IsDraw();
+ std::string ParseSAN(std::string SANMove);
};
} // namespace chessarbiter
diff --git a/tests/chessarbiter.cpp b/tests/chessarbiter.cpp
index 9c1407d..8687d4e 100644
--- a/tests/chessarbiter.cpp
+++ b/tests/chessarbiter.cpp
@@ -413,3 +413,36 @@ TEST_CASE("SimpleEnPassant", "[SimpleEnPassant]") {
CHECK(a.GetFEN() ==
"rnbqkbnr/ppppp1pp/8/8/8/4p3/PPPP1PPP/RNBQKBNR w KQkq - 0 2");
}
+
+TEST_CASE("ParseSAN", "[ParseSAN]") {
+ ChessArbiter a;
+
+ // Initial position test
+ a.Setup("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
+ CHECK(a.ParseSAN("d4") == "d2d4");
+ CHECK(a.ParseSAN("e3") == "e2e3");
+ CHECK(a.ParseSAN("e4") == "e2e4");
+ CHECK(a.ParseSAN("Nc3") == "b1c3");
+ CHECK(a.ParseSAN("Nf3") == "g1f3");
+
+ // Check when two similar piece can go to the same square
+ a.Setup("rnbqkbnr/pppppppp/8/8/P6P/7R/1PPPPPP1/RNBQKBN1 w Qkq - 0 1");
+ CHECK(a.ParseSAN("Raa3") == "a1a3");
+ CHECK(a.ParseSAN("Rha3") == "h3a3");
+ a.Setup("rnbqkbnr/pppppppp/8/8/P6P/R7/1PPPPPP1/RNBQKBN1 w Qkq - 0 1");
+ CHECK(a.ParseSAN("R1a2") == "a1a2");
+ CHECK(a.ParseSAN("R3a2") == "a3a2");
+ a.Setup("r1bqkb1r/pppppppp/5n2/8/P6P/R1N5/1PPPPnP1/R1BQKBN1 b Qkq - 0 1");
+ CHECK(a.ParseSAN("N6e4") == "f6e4");
+ CHECK(a.ParseSAN("N2e4") == "f2e4");
+
+ // Castling
+ a.Setup("rnbqkbnr/pppppppp/8/8/8/4NB2/PPPPPPPP/RNBQK2R w KQkq - 0 1");
+ CHECK(a.ParseSAN("O-O") == "e1g1");
+ a.Setup("rnbqkbnr/pppppppp/8/8/8/2NBQ3/PPPPPPPP/R3KBNR w KQkq - 0 1");
+ CHECK(a.ParseSAN("O-O-O") == "e1c1");
+ a.Setup("rnbqk2r/pppppppp/4bn2/8/8/2NBQ3/PPPPPPPP/R3KBNR b KQkq - 0 1");
+ CHECK(a.ParseSAN("O-O") == "e8g8");
+ a.Setup("r3kb1r/pppppppp/2qnbn2/8/8/2NBQ3/PPPPPPPP/R3KBNR b KQkq - 0 1");
+ CHECK(a.ParseSAN("O-O-O") == "e8c8");
+}