#include "Chessboard.h" #include #include #include #include #include #include #include namespace { constexpr std::array positions = { "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2", "a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3", "a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4", "a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5", "a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6", "a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7", "a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8", }; constexpr char INVALID_POS = positions.size(); constexpr int R = 0; // rank index constexpr int F = 1; // file index #define FILE (c[F] - '1') #define RANK (c[R] - 'a') constexpr char operator ""_P(const char * c, size_t size) { return size < 2 || RANK < 0 || RANK > 7 || FILE < 0 || FILE > 7 ? INVALID_POS : FILE * 8 + RANK; } #undef FILE #undef RANK struct sview { const char * ptr = nullptr; size_t size = 0; sview() = default; sview(const char * p, size_t s) : ptr(p), size(s) {} sview(const std::string& s) : ptr(s.data()), size(s.size()) {} size_t find(char del, size_t pos) { while (pos < size && ptr[pos] != del) ++pos; return pos < size ? pos : std::string::npos; } }; std::vector split(sview str, char del) { std::vector res; size_t cur = 0; size_t last = 0; while (cur != std::string::npos) { if (str.ptr[last] == ' ') { ++last; continue; } cur = str.find(del, last); size_t len = cur == std::string::npos ? str.size - last : cur - last; res.emplace_back(str.ptr + last, len); last = cur + 1; } return res; } char strToPos(sview str) { return operator ""_P(str.ptr, str.size); } constexpr std::array pieceNames = { "pawn", "knight", "bishop", "rook", "queen", "king", }; static constexpr std::array blackShort = { 'p', 'n', 'b', 'r', 'q', 'k', }; static constexpr std::array whiteShort = { 'P', 'N', 'B', 'R', 'Q', 'K', }; char strToType(sview str) { auto it = std::find_if(pieceNames.begin(), pieceNames.end(), [str] (const char* name) { return strncmp(name, str.ptr, str.size) == 0; }); return it != pieceNames.end() ? it - pieceNames.begin() : pieceNames.size(); } // directions using Direction = std::array; constexpr Direction N = {(char) 0, (char) 1}; constexpr Direction NNE = {(char) 1, (char) 2}; constexpr Direction NE = {(char) 1, (char) 1}; constexpr Direction ENE = {(char) 2, (char) 1}; constexpr Direction E = {(char) 1, (char) 0}; constexpr Direction ESE = {(char) 2, (char) -1}; constexpr Direction SE = {(char) 1, (char) -1}; constexpr Direction SSE = {(char) 1, (char) -2}; constexpr Direction S = {(char) 0, (char) -1}; constexpr Direction SSW = {(char) -1, (char) -2}; constexpr Direction SW = {(char) -1, (char) -1}; constexpr Direction WSW = {(char) -2, (char) -1}; constexpr Direction W = {(char) -1, (char) 0}; constexpr Direction WNW = {(char) -2, (char) 1}; constexpr Direction NW = {(char) -1, (char) 1}; constexpr Direction NNW = {(char) -1, (char) 2}; char makeStep(char pos, const Direction& d) { char next[2] = { char(positions[pos][R] + d[R]) , char(positions[pos][F] + d[F]) }; return strToPos(sview{next, sizeof(next)}); } template char traverse(char pos, const Direction& d, const Modifier& m, int count = 8) { while (--count >= 0) { pos = makeStep(pos, d); if (pos == INVALID_POS || m(pos)) break; } return pos; } Direction normalize(const Direction& distance) { //return {char((distance[R] > 0) - (distance[R] < 0)), char((distance[F] > 0) - (distance[F] < 0))}; const int drp = distance[R] > 0 ? 1 : 0; const int drn = distance[R] < 0 ? 1 : 0; const int dfp = distance[F] > 0 ? 1 : 0; const int dfn = distance[F] < 0 ? 1 : 0; return {char(drp - drn), char(dfp - dfn)}; } struct Pin { Direction d; Piece* pinner; Piece* pinned; }; using Pins = std::list; using Board = std::array; std::vector filter(const Direction& pin, std::initializer_list directions) { if (pin[R] == 0 && pin[F] == 0) return directions; std::vector result; for (auto& d : directions) { if ((d[R] == pin[R] || d[R] == -pin[R]) && (d[F] == pin[F] || d[F] == -pin[F])) result.push_back(d); } return result; } } class Piece { public: enum Types : char { Pawn, Knight, Bishop, Rook, Queen, King, // NUM_PIECES }; enum Colors : char { White, Black, }; const char* name() const; char initial() const; Types type() const { return m_type; } Colors color() const { return m_color; } char pos() const { return m_pos; } void setPos(char pos) { m_pos = pos; invalidate(); } const char* coord() const; const std::set& allowed() const { return m_allowed; } bool canReach(char pos) const; virtual bool movePattern(char pos) const = 0; void take(); virtual void reinit(const State& state) = 0; void invalidate(); protected: Piece(Types type, Colors color, char pos, std::set allowed) : m_type(type), m_color(color), m_pos(pos), m_allowed(std::move(allowed)) {} Piece(const Piece&) = delete; ~Piece() = default; const Types m_type; const Colors m_color; char m_pos; std::set m_allowed; bool m_update = false; }; struct Pawn : public Piece { Pawn(Colors color, char pos, std::set next) : Piece(Types::Pawn, color, pos, std::move(next)) {} bool is_first_move() const { return m_color ? coord()[F] == '7' : coord()[F] == '2'; } virtual bool movePattern(char pos) const override { if (m_pos == INVALID_POS) return false; auto cur = coord(); auto next = positions[pos]; Direction distance = {char(next[R] - cur[R]), char(next[F] - cur[F])}; char forward = m_color ? -1 : 1; return (forward == distance[F] && distance[R] * distance[R] <= 1) || (is_first_move() && 2 * forward == distance[F] && distance[R] == 0); } virtual void reinit(const State& state) override; }; struct Knight : public Piece { Knight(Colors color, char pos, std::set next) : Piece(Types::Knight, color, pos, std::move(next)) {} virtual bool movePattern(char pos) const override { if (m_pos == INVALID_POS) return false; auto cur = coord(); auto next = positions[pos]; Direction diff = {char(next[R] - cur[R]), char(next[F] - cur[F])}; return diff[R]*diff[R] + diff[F]*diff[F] == 5; } virtual void reinit(const State& state) override; }; struct Bishop : public Piece { Bishop(Colors color, char pos) : Piece(Types::Bishop, color, pos, {}) {} virtual bool movePattern(char pos) const override { if (m_pos == INVALID_POS) return false; auto cur = coord(); auto next = positions[pos]; return cur[R] - cur[F] == next[R] - next[F] || cur[R] + cur[F] == next[R] + next[F]; } virtual void reinit(const State& state) override; }; struct Rook : public Piece { Rook(Colors color, char pos) : Piece(Types::Rook, color, pos, {}) {} virtual bool movePattern(char pos) const override { if (m_pos == INVALID_POS) return false; auto cur = coord(); auto next = positions[pos]; return cur[R] == next[R] || cur[F] == next[F]; } virtual void reinit(const State& state) override; }; struct Queen : public Piece { Queen(Colors color, char pos) : Piece(Types::Queen, color, pos, {}) {} virtual bool movePattern(char pos) const override { if (m_pos == INVALID_POS) return false; auto cur = coord(); auto next = positions[pos]; return cur[R] == next[R] || cur[F] == next[F] || cur[R] - cur[F] == next[R] - next[F] || cur[R] + cur[F] == next[R] + next[F]; } virtual void reinit(const State& state) override; }; struct King : public Piece { King(Colors color, char pos) : Piece(Types::King, color, pos, {}) {} virtual bool movePattern(char pos) const override { if (m_pos == INVALID_POS) return false; auto cur = coord(); auto next = positions[pos]; Direction diff = {char(next[R] - cur[R]), char(next[F] - cur[F])}; return diff[R]*diff[R] + diff[F]*diff[F] <= 2; } virtual void reinit(const State& state) override; }; struct PieceSet { Piece* begin() { return &p1; } Piece* end() { return &r2 + 1; } const Piece* begin() const { return &p1; } const Piece* end() const { return &r2 + 1; } Piece& operator[](int i) { return *(begin() + i); } const Piece& operator[](int i) const { return *(begin() + i); } Pawn p1; Pawn p2; Pawn p3; Pawn p4; Pawn p5; Pawn p6; Pawn p7; Pawn p8; Rook r1; Knight n1; Bishop b1; Queen q; King k; Bishop b2; Knight n2; Rook r2; }; struct State { State(); PieceSet blacks; PieceSet whites; Board board; Pins blackPins; Pins whitePins; }; Direction findPin(const Piece& piece, const State& state) { auto& pins = piece.color() ? state.blackPins : state.whitePins; auto it = std::find_if(pins.begin(), pins.end(), [&] (const Pin& pin) { return pin.pinned == &piece; }); if (it != pins.end()) return it->d; return {0, 0}; } struct Find { Find(const Board& board) : m_board(board) {} bool operator() (char pos) const { return m_board[pos]; } const Board& m_board; }; struct Add { Add(const Board& board, std::set& moves, Piece::Colors color) : m_board(board), m_moves(moves), m_color(color) {} bool operator() (char pos) const { if (!m_board[pos] || m_board[pos]->color() != m_color) m_moves.insert(pos); return m_board[pos]; } const Board& m_board; std::set& m_moves; Piece::Colors m_color; }; void Pawn::reinit(const State& state) { if (m_pos == INVALID_POS) return; if (!m_update) return; m_update = false; m_allowed.clear(); auto pin = findPin(*this, state); auto & left = m_color ? SW : NW; auto & right = m_color ? SE : NE; for (auto& direction : filter(pin, { left, right })) { auto pos = makeStep(m_pos, direction); if (pos != INVALID_POS && state.board[pos] && state.board[pos]->color() != m_color) m_allowed.insert(pos); } auto & forward = m_color ? S : N; if (!filter(pin, {forward}).empty()) { traverse(m_pos, forward, [&] (char pos) { if (!state.board[pos]) m_allowed.insert(pos); return state.board[pos] || !is_first_move(); }, 2); } } void Knight::reinit(const State& state) { if (m_pos == INVALID_POS) return; if (!m_update) return; m_update = false; m_allowed.clear(); auto pin = findPin(*this, state); if (pin[R] != 0 || pin[F] != 0) return; for (auto& direction : { NNE, ENE, ESE, SSE, SSW, WSW, WNW, NNW }) { auto pos = makeStep(m_pos, direction); if (pos != INVALID_POS && (!state.board[pos] || state.board[pos]->color() != m_color)) m_allowed.insert(pos); } } void Bishop::reinit(const State& state) { if (m_pos == INVALID_POS) return; if (!m_update) return; m_update = false; m_allowed.clear(); auto pin = findPin(*this, state); for (auto& direction : filter(pin, { NE, SE, SW, NW })) { traverse(m_pos, direction, Add(state.board, m_allowed, m_color)); } } void Rook::reinit(const State& state) { if (m_pos == INVALID_POS) return; if (!m_update) return; m_update = false; m_allowed.clear(); auto pin = findPin(*this, state); for (auto& direction : filter(pin, { N, E, S, W })) { traverse(m_pos, direction, Add(state.board, m_allowed, m_color)); } } void Queen::reinit(const State& state) { if (m_pos == INVALID_POS) return; if (!m_update) return; m_update = false; m_allowed.clear(); auto pin = findPin(*this, state); for (auto& direction : filter(pin, { N, NE, E, SE, S, SW, W, NW })) { traverse(m_pos, direction, Add(state.board, m_allowed, m_color)); } } void King::reinit(const State& state) { if (m_pos == INVALID_POS) return; if (!m_update) return; m_update = false; m_allowed.clear(); auto& enemyPieces = m_color ? state.whites : state.blacks; auto& pawnAttackLeft = m_color ? SW : NW; auto& pawnAttackRight = m_color ? SE : NE; for (auto& direction : { N, NE, E, SE, S, SW, W, NW }) { auto pos = makeStep(m_pos, direction); bool accept = pos != INVALID_POS && !(state.board[pos] && state.board[pos]->color() == m_color); if (accept) { for (auto& p : enemyPieces) { if (!p.movePattern(pos)) continue; if (p.type() == Piece::Knight || p.type() == Piece::King) { accept = false; break; } else if (p.type() == Piece::Pawn) { auto from = positions[pos]; auto to = p.coord(); Direction d {char(to[R] - from[R]), char(to[F] - from[F])}; if (d == pawnAttackLeft || d == pawnAttackRight) { accept = false; break; } } else { auto from = positions[pos]; auto to = p.coord(); Direction d = normalize({char(to[R] - from[R]), char(to[F] - from[F])}); auto reached = traverse(pos, d, Find(state.board)); if (p.pos() == reached) { accept = false; break; } } } } if (accept) m_allowed.insert(pos); } } const char* Piece::name() const { static_assert(pieceNames.size() == Piece::NUM_PIECES, "Mismatch between piece names and types"); return pieceNames[m_type]; } char Piece::initial() const { static_assert(blackShort.size() == Piece::NUM_PIECES, "Mismatch between piece names and types"); static_assert(whiteShort.size() == Piece::NUM_PIECES, "Mismatch between piece names and types"); return m_color ? blackShort[m_type] : whiteShort[m_type]; } void Piece::invalidate() { m_update = true; } const char* Piece::coord() const { if (m_pos == INVALID_POS) return ""; return positions[m_pos]; } bool Piece::canReach(char pos) const { return movePattern(pos) && m_allowed.count(pos); } void Piece::take() { m_pos = INVALID_POS; m_allowed = {}; } State::State() : blacks { {Piece::Black, "a7"_P, {"a5"_P, "a6"_P} }, {Piece::Black, "b7"_P, {"b5"_P, "b6"_P} }, {Piece::Black, "c7"_P, {"c5"_P, "c6"_P} }, {Piece::Black, "d7"_P, {"d5"_P, "d6"_P} }, {Piece::Black, "e7"_P, {"e5"_P, "e6"_P} }, {Piece::Black, "f7"_P, {"f5"_P, "f6"_P} }, {Piece::Black, "g7"_P, {"g5"_P, "g6"_P} }, {Piece::Black, "h7"_P, {"h5"_P, "h6"_P} }, {Piece::Black, "a8"_P}, {Piece::Black, "b8"_P, {"a6"_P, "c6"_P} }, {Piece::Black, "c8"_P}, {Piece::Black, "d8"_P}, {Piece::Black, "e8"_P}, {Piece::Black, "f8"_P}, {Piece::Black, "g8"_P, {"f6"_P, "h6"_P} }, {Piece::Black, "h8"_P}, } , whites { {Piece::White, "a2"_P, {"a3"_P, "a4"_P} }, {Piece::White, "b2"_P, {"b3"_P, "b4"_P} }, {Piece::White, "c2"_P, {"c3"_P, "c4"_P} }, {Piece::White, "d2"_P, {"d3"_P, "d4"_P} }, {Piece::White, "e2"_P, {"e3"_P, "e4"_P} }, {Piece::White, "f2"_P, {"f3"_P, "f4"_P} }, {Piece::White, "g2"_P, {"g3"_P, "g4"_P} }, {Piece::White, "h2"_P, {"h3"_P, "h4"_P} }, {Piece::White, "a1"_P}, {Piece::White, "b1"_P, {"a3"_P, "c3"_P} }, {Piece::White, "c1"_P}, {Piece::White, "d1"_P}, {Piece::White, "e1"_P}, {Piece::White, "f1"_P}, {Piece::White, "g1"_P, {"f3"_P, "h3"_P} }, {Piece::White, "h1"_P}, } , board {{ &whites[ 8], &whites[ 9], &whites[10], &whites[11], &whites[12], &whites[13], &whites[14], &whites[15], &whites[ 0], &whites[ 1], &whites[ 2], &whites[ 3], &whites[ 4], &whites[ 5], &whites[ 6], &whites[ 7], nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &blacks[ 0], &blacks[ 1], &blacks[ 2], &blacks[ 3], &blacks[ 4], &blacks[ 5], &blacks[ 6], &blacks[ 7], &blacks[ 8], &blacks[ 9], &blacks[10], &blacks[11], &blacks[12], &blacks[13], &blacks[14], &blacks[15], }} {} Chessboard::Chessboard() : m_state(new State()) { setGrammar(); } Chessboard::~Chessboard() = default; void Chessboard::setPrompt(const std::string& prompt) { m_prompt = prompt; setGrammar(); } void Chessboard::setGrammar() { m_grammar.clear(); std::string result; if (m_prompt.empty()) { result += "move ::= \" \" ((piece | frompos) \" \" \"to \"?)? topos\n"; //result += "move ::= \" \" frompos \" \" \"to \"? topos\n"; } else { // result += "move ::= prompt \" \" ((piece | frompos) \" \" \"to \"?)? topos\n" result += "move ::= prompt \" \" frompos \" \" \"to \"? topos\n" "prompt ::= \" " + m_prompt + "\"\n"; } std::set pieceTypes; std::set from_pos; std::set to_pos; auto& pieces = m_moveCounter % 2 ? m_state->blacks : m_state->whites; std::set flags; for (auto& p : pieces) { if (p.allowed().empty()) continue; bool addPiece = false; if (!m_inCheck || p.type() == Piece::King) { to_pos.insert(p.allowed().begin(), p.allowed().end()); addPiece = !p.allowed().empty(); } else { for (auto move : p.allowed()) { if (m_allowedInCheck.count(move)) { to_pos.insert(move); addPiece = true; } } } if (addPiece) { pieceTypes.insert(p.type()); from_pos.insert(p.pos()); } } if (pieceTypes.empty()) return; result += "piece ::= ("; for (auto& p : pieceTypes) result += " \"" + std::string(pieceNames[p]) + "\" |"; result.pop_back(); result += ")\n\n"; result += "frompos ::= ("; for (auto& p : from_pos) result += " \"" + std::string(positions[p]) + "\" |"; result.pop_back(); result += ")\n"; result += "topos ::= ("; for (auto& p : to_pos) result += " \"" + std::string(positions[p]) + "\" |"; result.pop_back(); result += ")\n"; m_grammar = std::move(result); } std::string Chessboard::stringifyBoard() { std::string result; result.reserve(16 + 2 * 64 + 16); for (char rank = 'a'; rank <= 'h'; ++rank) { result.push_back(rank); result.push_back(' '); } result.back() = '\n'; for (int i = 7; i >= 0; --i) { for (int j = 0; j < 8; ++j) { auto p = m_state->board[i * 8 + j]; if (p) result.push_back(p->initial()); else result.push_back((i + j) % 2 ? '.' : '*'); result.push_back(' '); } result.push_back('0' + i + 1); result.push_back('\n'); } return result; } std::string Chessboard::process(const std::string& command) { const auto t_start = std::chrono::high_resolution_clock::now(); auto color = Piece::Colors(m_moveCounter % 2); Piece* piece = nullptr; auto pos_to = INVALID_POS; if (!parseCommand(command, piece, pos_to)) return ""; auto pos_from = piece->pos(); if (!move(*piece, pos_to)) return ""; flagUpdates(pos_from, pos_to); detectChecks(); auto& enemyPieces = color ? m_state->whites : m_state->blacks; for (auto& p : enemyPieces) p.reinit(*m_state); // only enemy moves needed next std::string result = {positions[pos_from][R], positions[pos_from][F], '-', positions[pos_to][R], positions[pos_to][F]}; ++m_moveCounter; setGrammar(); const auto t_end = std::chrono::high_resolution_clock::now(); auto t_ms = std::chrono::duration_cast(t_end - t_start).count(); fprintf(stdout, "%s: Move '%s%s%s', (t = %d ms)\n", __func__, "\033[1m", result.data(), "\033[0m", (int) t_ms); if (m_grammar.empty()) result.push_back('#'); return result; } bool Chessboard::parseCommand(const std::string& command, Piece*& piece, char& pos_to) { auto color = Piece::Colors(m_moveCounter % 2); fprintf(stdout, "%s: Command to %s: '%s%.*s%s'\n", __func__, (color ? "Black" : "White"), "\033[1m", int(command.size()), command.data(), "\033[0m"); if (command.empty()) return false; auto tokens = split(command, ' '); auto pos_from = INVALID_POS; auto type = Piece::Types::NUM_PIECES; if (tokens.size() == 1) { type = Piece::Types::Pawn; pos_to = strToPos(tokens.front()); } else { pos_from = strToPos(tokens.front()); if (pos_from == INVALID_POS) type = Piece::Types(strToType(tokens.front())); pos_to = strToPos(tokens.back()); } if (pos_to == INVALID_POS) return false; if (pos_from == INVALID_POS) { if (type == Piece::Types::NUM_PIECES) return false; auto& pieces = color ? m_state->blacks : m_state->whites; for (auto& p : pieces) { if (p.type() == type && p.canReach(pos_to)) { pos_from = p.pos(); break; } } } if (pos_from == INVALID_POS) return false; if (m_state->board[pos_from] == nullptr) return false; piece = m_state->board[pos_from]; if (piece->color() != color) return false; return true; } void Chessboard::flagUpdates(char pos_from, char pos_to) { auto color = Piece::Colors(m_moveCounter % 2); auto& enemyPieces = color ? m_state->whites : m_state->blacks; auto& ownPieces = color ? m_state->blacks : m_state->whites; for (auto& p : enemyPieces) { if (p.movePattern(pos_to) || p.movePattern(pos_from)) { updatePins(p); p.invalidate(); } } for (auto& p : ownPieces) { if (p.movePattern(pos_to) || p.movePattern(pos_from)) { updatePins(p); p.invalidate(); } } } void Chessboard::updatePins(Piece& piece) { if (piece.type() == Piece::Pawn || piece.type() == Piece::Knight || piece.type() == Piece::King) return; auto& enemyPieces = piece.color() ? m_state->whites : m_state->blacks; auto& enemyPins = piece.color() ? m_state->whitePins : m_state->blackPins; auto& king = enemyPieces.k; auto it = std::find_if(enemyPins.begin(), enemyPins.end(), [&] (const Pin& pin) { return pin.pinner == &piece; }); if (it != enemyPins.end()) { it->pinned->invalidate(); enemyPins.erase(it); } if (piece.movePattern(king.pos())) { auto to = positions[king.pos()]; auto from = piece.coord(); Direction d = normalize({char(to[R] - from[R]), char(to[F] - from[F])}); auto reached = traverse(piece.pos(), d, Find(m_state->board)); auto foundPiece = m_state->board[reached]; if (&king == foundPiece) { // check king.invalidate(); } else if (foundPiece && foundPiece->color() != piece.color()) { reached = traverse(reached, d, Find(m_state->board)); if (&king == m_state->board[reached]) { enemyPins.push_back({d, &piece, foundPiece}); foundPiece->invalidate(); } } } } void Chessboard::detectChecks() { auto color = Piece::Colors(m_moveCounter % 2); auto& enemyPieces = color ? m_state->whites : m_state->blacks; auto& ownPieces = color ? m_state->blacks : m_state->whites; auto& king = enemyPieces.k; auto& pawnAttackLeft = color ? SW : NW; auto& pawnAttackRight = color ? SE : NE; for (auto& p : ownPieces) { if (!p.movePattern(king.pos())) continue; auto to = positions[king.pos()]; auto from = p.coord(); if (p.type() == Piece::Knight) { if (!m_inCheck) { m_allowedInCheck = { p.pos() }; } else { m_allowedInCheck.clear(); } m_inCheck = true; } else if (p.type() == Piece::Pawn) { Direction d {char(to[R] - from[R]), char(to[F] - from[F])}; if (d == pawnAttackLeft || d == pawnAttackRight) { if (!m_inCheck) { m_allowedInCheck = { p.pos() }; } else { m_allowedInCheck.clear(); } m_inCheck = true; } } else { Direction d = normalize({char(to[R] - from[R]), char(to[F] - from[F])}); std::set tmp; auto pos = traverse(p.pos(), d, Add(m_state->board, tmp, king.color())); if (pos == king.pos()) { tmp.insert(p.pos()); if (!m_inCheck) { m_allowedInCheck = std::move(tmp); } else { m_allowedInCheck.clear(); } m_inCheck = true; } } } } bool Chessboard::move(Piece& piece, char pos_to) { auto& allowed = piece.allowed(); if (allowed.count(pos_to) == 0 || (m_inCheck && piece.type() != Piece::King && m_allowedInCheck.count(pos_to) == 0)) return false; if (m_state->board[pos_to] && m_state->board[pos_to]->color() == piece.color()) return false; if (m_state->board[pos_to]) m_state->board[pos_to]->take(); m_state->board[piece.pos()] = nullptr; m_state->board[pos_to] = &piece; piece.setPos(pos_to); m_inCheck = false; m_allowedInCheck.clear(); return true; }