├── README.md ├── img ├── black_king.png ├── black_pawn.png ├── black_queen.png ├── black_rook.png ├── white_king.png ├── white_pawn.png ├── white_queen.png ├── white_rook.png ├── black_bishop.png ├── black_knight.png ├── white_bishop.png └── white_knight.png ├── frame.h ├── main.h ├── main.cpp ├── frame.cpp ├── .gitignore ├── symbol.h ├── panel.h ├── piece.cpp ├── symbol.cpp ├── board.h ├── piece.h ├── panel.cpp └── board.cpp /README.md: -------------------------------------------------------------------------------- 1 | # chess-cpp 2 | GUI chess program in C++ 3 | -------------------------------------------------------------------------------- /img/black_king.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/black_king.png -------------------------------------------------------------------------------- /img/black_pawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/black_pawn.png -------------------------------------------------------------------------------- /img/black_queen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/black_queen.png -------------------------------------------------------------------------------- /img/black_rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/black_rook.png -------------------------------------------------------------------------------- /img/white_king.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/white_king.png -------------------------------------------------------------------------------- /img/white_pawn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/white_pawn.png -------------------------------------------------------------------------------- /img/white_queen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/white_queen.png -------------------------------------------------------------------------------- /img/white_rook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/white_rook.png -------------------------------------------------------------------------------- /img/black_bishop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/black_bishop.png -------------------------------------------------------------------------------- /img/black_knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/black_knight.png -------------------------------------------------------------------------------- /img/white_bishop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/white_bishop.png -------------------------------------------------------------------------------- /img/white_knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ctgk/chess-cpp/HEAD/img/white_knight.png -------------------------------------------------------------------------------- /frame.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class Frame : public wxFrame 4 | { 5 | public: 6 | Frame(const wxString& title); 7 | }; -------------------------------------------------------------------------------- /main.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class ChessApp : public wxApp 4 | { 5 | public: 6 | virtual bool OnInit(); 7 | }; 8 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "main.h" 2 | #include "frame.h" 3 | 4 | IMPLEMENT_APP(ChessApp) 5 | 6 | bool ChessApp::OnInit() 7 | { 8 | wxInitAllImageHandlers(); 9 | Frame *frame = new Frame(wxT("Chess")); 10 | frame->Show(true); 11 | 12 | return true; 13 | } 14 | -------------------------------------------------------------------------------- /frame.cpp: -------------------------------------------------------------------------------- 1 | #include "frame.h" 2 | #include "panel.h" 3 | #include "board.h" 4 | 5 | Frame::Frame(const wxString& title) 6 | : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(480, 520)) 7 | { 8 | wxStatusBar *sb = CreateStatusBar(); 9 | Board *board = new Board(); 10 | Panel *panel = new Panel(this, board); 11 | panel->SetFocus(); 12 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # VSCode project settings 35 | .vscode/ 36 | -------------------------------------------------------------------------------- /symbol.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class Symbol 4 | { 5 | public: 6 | Symbol(const wxString path); 7 | void PlaceAt(int x, int y) { boardX = x; boardY = y; } 8 | void Draw(wxDC& dc, int length); 9 | bool BeginMove(wxPoint pt); 10 | void FinishMove(wxPoint pt, bool moved); 11 | void Move(wxPoint pt); 12 | bool isDragging() { return dragging; } 13 | int getBoardX() { return boardX; } 14 | int getBoardY() { return boardY; } 15 | void remove() { captured = true; } 16 | 17 | private: 18 | int spacingLength; 19 | int symbolSize; 20 | int spacingOffset; 21 | bool dragging; 22 | bool captured; 23 | int boardX, boardY; 24 | int pixelX, pixelY; 25 | wxImage img; 26 | }; -------------------------------------------------------------------------------- /panel.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "symbol.h" 6 | #include "board.h" 7 | 8 | class Panel : public wxPanel 9 | { 10 | public: 11 | Panel(wxFrame *parent, Board *chessboard); 12 | 13 | protected: 14 | void OnPaint(wxPaintEvent& event); 15 | void OnMouseDown(wxMouseEvent& event); 16 | void OnMouseUp(wxMouseEvent& event); 17 | void OnMove(wxMouseEvent& event); 18 | 19 | private: 20 | int SquareLength(); 21 | void DrawSquare(wxPaintDC &dc, int x, int y); 22 | void LoadPiece(); 23 | wxStatusBar *statusbar; 24 | Board *board; 25 | int boardLength; 26 | std::vector symbol; 27 | std::string origin; 28 | std::map symbolPath; 29 | }; 30 | -------------------------------------------------------------------------------- /piece.cpp: -------------------------------------------------------------------------------- 1 | #include "piece.h" 2 | 3 | Piece::Piece(char color, std::string name, char abbr) : color(color), opponentColor(color == 'w' ? 'b' : 'w'), name(name), abbreviation(abbr) 4 | { 5 | } 6 | 7 | Piece* abbr2piece(char abbreviation) 8 | { 9 | char abbr; 10 | char color; 11 | if('A' < abbreviation && abbreviation < 'Z'){ 12 | abbr = std::tolower(abbreviation, std::locale()); 13 | color = 'w'; 14 | } else if('a' < abbreviation && abbreviation < 'z'){ 15 | abbr = abbreviation; 16 | color = 'b'; 17 | } 18 | Piece* piece; 19 | switch(abbr){ 20 | case 'p': 21 | piece = new Pawn(color); 22 | break; 23 | case 'n': 24 | piece = new Knight(color); 25 | break; 26 | case 'b': 27 | piece = new Bishop(color); 28 | break; 29 | case 'r': 30 | piece = new Rook(color); 31 | break; 32 | case 'q': 33 | piece = new Queen(color); 34 | break; 35 | case 'k': 36 | piece = new King(color); 37 | break; 38 | default: 39 | piece = new Vacant(); 40 | break; 41 | } 42 | return piece; 43 | } 44 | -------------------------------------------------------------------------------- /symbol.cpp: -------------------------------------------------------------------------------- 1 | #include "symbol.h" 2 | 3 | Symbol::Symbol(const wxString path) 4 | { 5 | captured = false; 6 | dragging = false; 7 | img = wxImage(path, wxBITMAP_TYPE_ANY); 8 | } 9 | 10 | void Symbol::Draw(wxDC& dc, int length) 11 | { 12 | spacingLength = length; 13 | symbolSize = length * 0.8; 14 | spacingOffset = length * 0.1; 15 | if(dragging){ 16 | dc.DrawBitmap( 17 | wxBitmap(img.Scale(symbolSize, symbolSize)), 18 | pixelX, pixelY, false 19 | ); 20 | } else if(!captured){ 21 | dc.DrawBitmap( 22 | wxBitmap(img.Scale(symbolSize, symbolSize)), 23 | boardX * length + spacingOffset, boardY * length + spacingOffset, false 24 | ); 25 | } 26 | } 27 | 28 | bool Symbol::BeginMove(wxPoint pt) 29 | { 30 | if(captured){ 31 | return false; 32 | } 33 | pixelX = boardX * spacingLength + spacingOffset; 34 | pixelY = boardY * spacingLength + spacingOffset; 35 | if(pixelX <= pt.x && pt.x <= pixelX + symbolSize && pixelY <= pt.y && pt.y <= pixelY + symbolSize){ 36 | pixelX = pt.x - symbolSize / 2; 37 | pixelY = pt.y - symbolSize / 2; 38 | dragging = true; 39 | return true; 40 | } else { 41 | return false; 42 | } 43 | } 44 | 45 | void Symbol::FinishMove(wxPoint pt, bool moved) 46 | { 47 | if(dragging){ 48 | if(moved){ 49 | boardX = pt.x / spacingLength; 50 | boardY = pt.y / spacingLength; 51 | } 52 | dragging = false; 53 | } 54 | } 55 | 56 | void Symbol::Move(wxPoint pt) 57 | { 58 | if(dragging){ 59 | pixelX = pt.x - symbolSize / 2; 60 | pixelY = pt.y - symbolSize / 2; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /board.h: -------------------------------------------------------------------------------- 1 | #ifndef BOARD_H 2 | #define BOARD_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "piece.h" 8 | 9 | class Board 10 | { 11 | public: 12 | Board(); 13 | void FEN2Board(); 14 | void FEN2Board(std::string fen); 15 | void board2FEN(); 16 | void setPieceAt(Piece* piece, std::string notation); 17 | void setPieceAt(Piece* piece, int i, int j) { setPieceAt(piece, getNotation(i, j)); } 18 | Piece* getPieceAt(std::string notation) { return piecePlacement[notation]; } 19 | Piece* getPieceAt(int i, int j) { return piecePlacement[getNotation(i, j)]; } 20 | char getPieceCharAt(std::string notation) { return getPieceAt(notation)->abbreviation; } 21 | char getPieceCharAt(int i, int j) { return getPieceAt(i, j)->abbreviation; } 22 | std::vector getIndices(std::string notation); 23 | std::string getNotation(int i, int j); 24 | char getPieceColorAt(std::string notation); 25 | char getPieceColorAt(int i, int j); 26 | bool move(std::string origin, std::string destination); 27 | std::string getDestination(std::string origin, int fileDirection, int rankDirection); 28 | std::vector getAttackingSquares(char color); 29 | std::string getFEN() { return fen; } 30 | char getActiveColor() { return activeColor; } 31 | std::string getCastlability() { return castlability; } 32 | int getHalfmoveClock() { return halfmoveClock; } 33 | int getFullmoveNumber() { return fullmoveNumber; } 34 | std::string enPassantTarget; 35 | std::string castlability; 36 | int halfmoveClock; 37 | int fullmoveNumber; 38 | bool isFinished() { return finished; } 39 | std::string getResult() { return result; } 40 | void changeActiveColor(); 41 | std::string getKingPosition(char color); 42 | bool inCheck(char color); 43 | void print(); 44 | static const int Length = 8; 45 | 46 | private: 47 | bool finished; 48 | std::string result; 49 | std::string fen; 50 | char activeColor; 51 | std::map piecePlacement; 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /piece.h: -------------------------------------------------------------------------------- 1 | #ifndef PIECE_H 2 | #define PIECE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class Board; 9 | 10 | class Piece 11 | { 12 | public: 13 | Piece(char color, std::string name, char abbr); 14 | void placeAt(Board* board, std::string placement); 15 | std::string getDestination(int fileMove, int RankMove); 16 | virtual std::vector getPossibleMoves() = 0; 17 | virtual std::vector getAttackingSquares() = 0; 18 | virtual bool moveTo(std::string destination); 19 | std::vector removeUnableMoves(std::vector moves); 20 | char const color; 21 | char const opponentColor; 22 | std::string const name; 23 | char const abbreviation; 24 | 25 | protected: 26 | Board* board; 27 | std::string position; 28 | }; 29 | 30 | class Pawn : public Piece 31 | { 32 | public: 33 | Pawn(char color) : Piece(color, "Pawn", (color == 'w' ? 'P' : 'p')), movingDirection(color == 'w' ? 1 : -1){} 34 | std::vector getPossibleMoves(); 35 | std::vector getAttackingSquares(); 36 | bool moveTo(std::string destination); 37 | 38 | protected: 39 | int const movingDirection; 40 | }; 41 | 42 | class Knight : public Piece 43 | { 44 | public: 45 | Knight(char color) : Piece(color, "Knight", (color == 'w' ? 'N' : 'n')){} 46 | std::vector getPossibleMoves(); 47 | std::vector getAttackingSquares(); 48 | }; 49 | 50 | class Bishop : public Piece 51 | { 52 | public: 53 | Bishop(char color) : Piece(color, "Bishop", (color == 'w' ? 'B' : 'b')){} 54 | std::vector getPossibleMoves(); 55 | std::vector getAttackingSquares(); 56 | }; 57 | 58 | class Rook : public Piece 59 | { 60 | public: 61 | Rook(char color) : Piece(color, "Rook", (color == 'w' ? 'R' : 'r')){} 62 | std::vector getPossibleMoves(); 63 | std::vector getAttackingSquares(); 64 | bool moveTo(std::string destination); 65 | }; 66 | 67 | class Queen : public Piece 68 | { 69 | public: 70 | Queen(char color) : Piece(color, "Queen", (color == 'w' ? 'Q' : 'q')){} 71 | std::vector getPossibleMoves(); 72 | std::vector getAttackingSquares(); 73 | }; 74 | 75 | class King : public Piece 76 | { 77 | public: 78 | King(char color) : Piece(color, "King", (color == 'w' ? 'K' : 'k')){} 79 | std::vector getPossibleMoves(); 80 | std::vector getAttackingSquares(); 81 | bool moveTo(std::string destination); 82 | }; 83 | 84 | class Vacant : public Piece 85 | { 86 | public: 87 | Vacant() : Piece('-', "Vacant", '-'){} 88 | std::vector getPossibleMoves(); 89 | std::vector getAttackingSquares() { return getPossibleMoves(); } 90 | bool moveTo(std::string destination) { return false; } 91 | }; 92 | 93 | Piece* abbr2piece(char abbreviation); 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /panel.cpp: -------------------------------------------------------------------------------- 1 | #include "panel.h" 2 | #include 3 | #include 4 | 5 | Panel::Panel(wxFrame *parent, Board *chessboard) 6 | : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE) 7 | { 8 | symbolPath['p'] = wxT("img/black_pawn.png"); 9 | symbolPath['n'] = wxT("img/black_knight.png"); 10 | symbolPath['b'] = wxT("img/black_bishop.png"); 11 | symbolPath['r'] = wxT("img/black_rook.png"); 12 | symbolPath['q'] = wxT("img/black_queen.png"); 13 | symbolPath['k'] = wxT("img/black_king.png"); 14 | symbolPath['P'] = wxT("img/white_pawn.png"); 15 | symbolPath['N'] = wxT("img/white_knight.png"); 16 | symbolPath['B'] = wxT("img/white_bishop.png"); 17 | symbolPath['R'] = wxT("img/white_rook.png"); 18 | symbolPath['Q'] = wxT("img/white_queen.png"); 19 | symbolPath['K'] = wxT("img/white_king.png"); 20 | board = chessboard; 21 | boardLength = chessboard->Length; 22 | statusbar = parent->GetStatusBar(); 23 | statusbar->SetStatusText(chessboard->getFEN()); 24 | LoadPiece(); 25 | 26 | Connect(wxEVT_PAINT, wxPaintEventHandler(Panel::OnPaint)); 27 | Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(Panel::OnMouseDown)); 28 | Connect(wxEVT_LEFT_UP, wxMouseEventHandler(Panel::OnMouseUp)); 29 | Connect(wxEVT_MOTION, wxMouseEventHandler(Panel::OnMove)); 30 | } 31 | 32 | void Panel::OnPaint(wxPaintEvent& event) 33 | { 34 | statusbar->SetStatusText(board->getFEN()); 35 | wxPaintDC dc(this); 36 | 37 | for (int i = 0; i < boardLength; i++){ 38 | for (int j = 0; j < boardLength; j++){ 39 | DrawSquare(dc, i, j); 40 | } 41 | } 42 | 43 | for(int i = 0; i < symbol.size(); i++){ 44 | symbol[i]->Draw(dc, SquareLength()); 45 | } 46 | } 47 | 48 | void Panel::OnMouseDown(wxMouseEvent& event) 49 | { 50 | for(int i = 0; i < symbol.size(); i++){ 51 | if(symbol[i]->BeginMove(event.GetPosition())){ 52 | origin = board->getNotation(symbol[i]->getBoardY(), symbol[i]->getBoardX()); 53 | break; 54 | } 55 | } 56 | } 57 | 58 | void Panel::OnMouseUp(wxMouseEvent& event) 59 | { 60 | for(int i = 0; i < symbol.size(); i++){ 61 | if (symbol[i]->isDragging()){ 62 | int targetX = event.GetPosition().x / SquareLength(); 63 | int targetY = event.GetPosition().y / SquareLength(); 64 | std::string dest = board->getNotation(targetY, targetX); 65 | bool moved = board->move(origin, dest); 66 | symbol[i]->FinishMove(event.GetPosition(), moved); 67 | if(moved) LoadPiece(); 68 | break; 69 | } 70 | } 71 | statusbar->SetStatusText(board->getFEN()); 72 | Refresh(true); 73 | } 74 | 75 | void Panel::OnMove(wxMouseEvent& event) 76 | { 77 | for(int i = 0; i < symbol.size(); i++){ 78 | symbol[i]->Move(ScreenToClient(wxGetMousePosition())); 79 | } 80 | Refresh(true); 81 | } 82 | 83 | int Panel::SquareLength() 84 | { 85 | int width = GetClientSize().GetWidth(); 86 | int height = GetClientSize().GetHeight(); 87 | int length = std::min(width, height) / boardLength; 88 | return length; 89 | } 90 | 91 | void Panel::DrawSquare(wxPaintDC& dc, int x, int y) 92 | { 93 | static wxColor light = wxColor(255, 222, 173); 94 | static wxColor dark = wxColor(205, 133, 63); 95 | int boardtop = GetClientSize().GetHeight() - boardLength * SquareLength(); 96 | 97 | dc.SetPen(*wxTRANSPARENT_PEN); 98 | if (x % 2 == y % 2){ 99 | dc.SetBrush(wxBrush(light)); 100 | } else { 101 | dc.SetBrush(wxBrush(dark)); 102 | } 103 | dc.DrawRectangle(x * SquareLength(), y * SquareLength(), 104 | SquareLength(), SquareLength()); 105 | } 106 | 107 | void Panel::LoadPiece() 108 | { 109 | symbol.resize(0); 110 | char piece; 111 | for(int i = 0; i < boardLength; i++){ 112 | for(int j = 0; j < boardLength; j++){ 113 | piece = board->getPieceCharAt(i, j); 114 | if(piece != '-'){ 115 | Symbol* sym = new Symbol(symbolPath[piece]); 116 | sym->PlaceAt(j, i); 117 | symbol.push_back(sym); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /board.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "board.h" 9 | 10 | std::vector split(std::string str, char sep) 11 | { 12 | std::vector str_blocks; 13 | std::stringstream ss(str); 14 | std::string buffer; 15 | while(std::getline(ss, buffer, sep)){ 16 | str_blocks.push_back(buffer); 17 | } 18 | return str_blocks; 19 | } 20 | 21 | Board::Board() 22 | { 23 | fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; 24 | FEN2Board(); 25 | finished = false; 26 | } 27 | 28 | void Board::FEN2Board() 29 | { 30 | std::vector fenBlocks = split(fen, ' '); 31 | assert(fenBlocks.size() == 6); 32 | 33 | for(int i = 0; i < fenBlocks[0].size(); i++){ 34 | if(std::isdigit(fenBlocks[0][i])){ 35 | fenBlocks[0].replace(i, 1, std::string(fenBlocks[0][i] - '0', '-')); 36 | } 37 | } 38 | std::vector placements = split(fenBlocks[0], '/'); 39 | assert(placements.size() == 8); 40 | for(int i = 0; i < Length; i++){ 41 | for(int j = 0; j < Length; j++){ 42 | setPieceAt(abbr2piece(placements[i][j]), i, j); 43 | } 44 | } 45 | 46 | activeColor = fenBlocks[1][0]; 47 | castlability = fenBlocks[2]; 48 | enPassantTarget = fenBlocks[3]; 49 | halfmoveClock = std::stoi(fenBlocks[4]); 50 | fullmoveNumber = std::stoi(fenBlocks[5]); 51 | } 52 | 53 | void Board::FEN2Board(std::string fen) 54 | { 55 | this->fen = fen; 56 | FEN2Board(); 57 | } 58 | 59 | void Board::board2FEN() 60 | { 61 | fen = ""; 62 | int vacant; 63 | for(int i = 0; i < Length; i++){ 64 | vacant = 0; 65 | for(int j = 0; j < Length; j++){ 66 | if(getPieceCharAt(i, j) == '-'){ 67 | vacant += 1; 68 | } else { 69 | if(vacant != 0){ 70 | fen += vacant + '0'; 71 | vacant = 0; 72 | } 73 | fen += getPieceCharAt(i, j); 74 | } 75 | } 76 | if(vacant != 0){ 77 | fen += vacant + '0'; 78 | } 79 | if(i != Length - 1){ 80 | fen += '/'; 81 | } 82 | } 83 | 84 | fen += " "; 85 | fen += activeColor; 86 | fen += " " + castlability; 87 | fen += " " + enPassantTarget; 88 | fen += " " + std::to_string(halfmoveClock); 89 | fen += " " + std::to_string(fullmoveNumber); 90 | } 91 | 92 | void Board::setPieceAt(Piece* piece, std::string notation) 93 | { 94 | piece->placeAt(this, notation); 95 | piecePlacement[notation] = piece; 96 | } 97 | 98 | std::vector Board::getIndices(std::string notation) 99 | { 100 | assert(notation.size() == 2); 101 | assert('a' <= notation[0] && notation[0] <= 'h'); 102 | assert('1' <= notation[1] && notation[1] <= '8'); 103 | std::vector indices; 104 | indices.push_back(7 - (notation[1] - '1')); 105 | indices.push_back(notation[0] - 'a'); 106 | return indices; 107 | } 108 | 109 | std::string Board::getNotation(int i, int j) 110 | { 111 | assert(0 <= i && i < Length); 112 | assert(0 <= j && j < Length); 113 | char file = 'a'; 114 | char rank = '1'; 115 | std::string notation; 116 | file += j; 117 | rank += Length - 1 - i; 118 | notation += file; 119 | notation += rank; 120 | return notation; 121 | } 122 | 123 | char Board::getPieceColorAt(std::string notation) 124 | { 125 | return getPieceAt(notation)->color; 126 | } 127 | 128 | char Board::getPieceColorAt(int i, int j) 129 | { 130 | return getPieceAt(i, j)->color; 131 | } 132 | 133 | bool Board::move(std::string origin, std::string destination) 134 | { 135 | if(finished) return false; 136 | bool moved = getPieceAt(origin)->moveTo(destination); 137 | int n_moves = 0; 138 | if(moved){ 139 | for(int i = 0; i < Length; i++){ 140 | for(int j = 0; j < Length; j++){ 141 | if(getPieceColorAt(i, j) == activeColor){ 142 | n_moves += getPieceAt(i, j)->getPossibleMoves().size(); 143 | } 144 | } 145 | } 146 | if(n_moves == 0){ 147 | finished = true; 148 | if(!inCheck(activeColor)){ 149 | result = "0.5-0.5"; 150 | } else { 151 | result = activeColor == 'w' ? "0-1" : "1-0"; 152 | } 153 | std::cout << result << std::endl; 154 | } 155 | } 156 | return moved; 157 | } 158 | 159 | std::string Board::getDestination(std::string origin, int fileDirection, int rankDirection) 160 | { 161 | char originFile = origin[0]; 162 | char originRank = origin[1]; 163 | std::string destination; 164 | char destinationFile = originFile + fileDirection; 165 | char destinationRank = originRank + rankDirection; 166 | 167 | if('a' <= destinationFile && destinationFile <= 'h' 168 | && '1' <= destinationRank && destinationRank <= '8'){ 169 | destination += destinationFile; 170 | destination += destinationRank; 171 | } else { 172 | destination = "-"; 173 | } 174 | return destination; 175 | } 176 | 177 | std::vector Board::getAttackingSquares(char color) 178 | { 179 | std::vector squares; 180 | std::vector tmp; 181 | for(int i = 0; i < Length; i++){ 182 | for(int j = 0; j < Length; j++){ 183 | if(getPieceColorAt(i, j) == color){ 184 | tmp = getPieceAt(i, j)->getAttackingSquares(); 185 | squares.insert(squares.end(), tmp.begin(), tmp.end()); 186 | } 187 | } 188 | } 189 | return squares; 190 | } 191 | 192 | void Board::print() 193 | { 194 | char file = 'a'; 195 | for(int i = 0; i < Length; i++){ 196 | std::cout << 8 - i << ' '; 197 | for(int j = 0; j < Length; j++){ 198 | std::cout << getPieceCharAt(i, j) << ' '; 199 | } 200 | std::cout << std::endl; 201 | } 202 | std::cout << " "; 203 | for(int i = 0; i < Length; i++){ 204 | std::cout << file << ' '; 205 | file += 1; 206 | } 207 | std::cout << std::endl << getFEN() << std::endl; 208 | } 209 | 210 | void Board::changeActiveColor() 211 | { 212 | if(activeColor == 'w'){ 213 | activeColor = 'b'; 214 | } else { 215 | activeColor = 'w'; 216 | } 217 | } 218 | 219 | std::string Board::getKingPosition(char color) 220 | { 221 | for(int i = 0; i < Length; i++){ 222 | for(int j = 0; j < Length; j++){ 223 | if(getPieceAt(i, j)->name == "King"){ 224 | if(getPieceColorAt(i, j) == color) 225 | return getNotation(i, j); 226 | } 227 | } 228 | } 229 | return "-"; 230 | } 231 | 232 | bool Board::inCheck(char color) 233 | { 234 | std::string kingPosition = getKingPosition(color); 235 | std::vector attacked = getAttackingSquares(color == 'w' ? 'b' : 'w'); 236 | return std::find(attacked.begin(), attacked.end(), kingPosition) != attacked.end(); 237 | } 238 | 239 | std::vector Piece::removeUnableMoves(std::vector moves) 240 | { 241 | std::vector possibleMoves; 242 | std::string origin = position; 243 | Piece* target; 244 | for(int i = 0; i < moves.size(); i++){ 245 | target = board->getPieceAt(moves[i]); 246 | board->setPieceAt(this, moves[i]); 247 | board->setPieceAt(new Vacant(), origin); 248 | if(!board->inCheck(this->color)){ 249 | possibleMoves.push_back(moves[i]); 250 | } 251 | board->setPieceAt(this, origin); 252 | board->setPieceAt(target, moves[i]); 253 | } 254 | return possibleMoves; 255 | } 256 | 257 | void Piece::placeAt(Board* board, std::string placement) 258 | { 259 | this->board = board; 260 | position = placement; 261 | } 262 | 263 | std::string Piece::getDestination(int fileMove, int rankMove) 264 | { 265 | return board->getDestination(position, fileMove, rankMove); 266 | } 267 | 268 | bool Piece::moveTo(std::string destination) 269 | { 270 | if(this->color != board->getActiveColor()) return false; 271 | std::vector moves = getPossibleMoves(); 272 | if(std::find(moves.begin(), moves.end(), destination) == moves.end()) return false; 273 | 274 | bool captured = (board->getPieceCharAt(destination) != '-'); 275 | std::string origin = position; 276 | board->setPieceAt(this, destination); 277 | board->setPieceAt(new Vacant(), origin); 278 | 279 | board->changeActiveColor(); 280 | board->enPassantTarget = "-"; 281 | if(captured){ 282 | board->halfmoveClock = 0; 283 | } else { 284 | board->halfmoveClock += 1; 285 | } 286 | if(this->color == 'b') board->fullmoveNumber += 1; 287 | board->board2FEN(); 288 | return true; 289 | } 290 | 291 | std::vector Pawn::getPossibleMoves() 292 | { 293 | std::vector moves; 294 | std::string candidate; 295 | char homeRank = color == 'w' ? '2' : '7'; 296 | 297 | candidate = getDestination(0, movingDirection); 298 | if(candidate != "-"){ 299 | if(board->getPieceCharAt(candidate) == '-'){ 300 | moves.push_back(candidate); 301 | if(position[1] == homeRank){ 302 | candidate = getDestination(0, 2 * movingDirection); 303 | if(board->getPieceCharAt(candidate) == '-'){ 304 | moves.push_back(candidate); 305 | } 306 | } 307 | } 308 | } 309 | 310 | for(int i = 0; i < 2; i++){ 311 | candidate = getDestination(2 * i - 1, movingDirection); 312 | if(candidate != "-"){ 313 | if(board->getPieceColorAt(candidate) == opponentColor){ 314 | moves.push_back(candidate); 315 | } else if(candidate == board->enPassantTarget){ 316 | moves.push_back(candidate); 317 | } 318 | } 319 | } 320 | 321 | return removeUnableMoves(moves); 322 | } 323 | 324 | std::vector Pawn::getAttackingSquares() 325 | { 326 | std::string candidate; 327 | std::vector squares; 328 | for(int i = 0; i < 2; i++){ 329 | candidate = getDestination(2 * i - 1, movingDirection); 330 | if(candidate != "-"){ 331 | squares.push_back(candidate); 332 | } 333 | } 334 | return squares; 335 | } 336 | 337 | bool Pawn::moveTo(std::string destination) 338 | { 339 | if(this->color != board->getActiveColor()) return false; 340 | std::vector moves = getPossibleMoves(); 341 | if(std::find(moves.begin(), moves.end(), destination) == moves.end()) return false; 342 | 343 | std::string origin = position; 344 | if(std::abs(origin[1] - destination[1]) > 1){ 345 | board->enPassantTarget = origin[0]; 346 | board->enPassantTarget += (origin[1] + destination[1]) / 2; 347 | } else if(destination == board->enPassantTarget){ 348 | board->setPieceAt( 349 | new Vacant(), 350 | board->getDestination(board->enPassantTarget, 0, -movingDirection) 351 | ); 352 | board->enPassantTarget = "-"; 353 | } else { 354 | board->enPassantTarget = "-"; 355 | } 356 | board->setPieceAt(this, destination); 357 | board->setPieceAt(new Vacant(), origin); 358 | 359 | board->changeActiveColor(); 360 | board->halfmoveClock = 0; 361 | if(this->color == 'b') board->fullmoveNumber += 1; 362 | board->board2FEN(); 363 | return true; 364 | } 365 | 366 | std::vector Knight::getPossibleMoves() 367 | { 368 | return removeUnableMoves(getAttackingSquares()); 369 | } 370 | 371 | std::vector Knight::getAttackingSquares() 372 | { 373 | std::vector squares; 374 | std::string candidate; 375 | static const int direction[8][2] = { 376 | {-1, 2}, {1, 2}, // front (from white's perspective) 377 | {2, 1}, {2, -1}, // right 378 | {-1, -2}, {1, -2}, // back 379 | {-2, 1}, {-2, -1} // left 380 | }; 381 | 382 | for(int i = 0; i < 8; i++){ 383 | candidate = getDestination(direction[i][0], direction[i][1]); 384 | if(candidate == "-") continue; 385 | if(board->getPieceColorAt(candidate) != this->color) 386 | squares.push_back(candidate); 387 | } 388 | 389 | return squares; 390 | } 391 | 392 | std::vector Bishop::getPossibleMoves() 393 | { 394 | return removeUnableMoves(getAttackingSquares()); 395 | } 396 | 397 | std::vector Bishop::getAttackingSquares() 398 | { 399 | std::vector squares; 400 | std::string candidate; 401 | static const int direction[4][2] = { 402 | {-1, 1}, {1, 1}, 403 | {-1, -1}, {1, -1} 404 | }; 405 | char targetColor; 406 | 407 | for(int i = 0; i < 4; i++){ 408 | candidate = position; 409 | while(true){ 410 | candidate = board->getDestination(candidate, direction[i][0], direction[i][1]); 411 | if(candidate == "-") break; 412 | targetColor = board->getPieceColorAt(candidate); 413 | if(targetColor == this->color){ 414 | break; 415 | } else if(targetColor == opponentColor){ 416 | squares.push_back(candidate); 417 | break; 418 | } else { 419 | squares.push_back(candidate); 420 | } 421 | } 422 | } 423 | 424 | return squares; 425 | } 426 | 427 | std::vector Rook::getPossibleMoves() 428 | { 429 | return removeUnableMoves(getAttackingSquares()); 430 | } 431 | 432 | std::vector Rook::getAttackingSquares() 433 | { 434 | std::vector squares; 435 | std::string candidate; 436 | static const int direction[4][2] = { 437 | {0, 1}, 438 | {-1, 0}, {1, 0}, 439 | {0, -1} 440 | }; 441 | char targetColor; 442 | 443 | for(int i = 0; i < 4; i++){ 444 | candidate = position; 445 | while(true){ 446 | candidate = board->getDestination(candidate, direction[i][0], direction[i][1]); 447 | if(candidate == "-") break; 448 | targetColor = board->getPieceColorAt(candidate); 449 | if(targetColor == this->color){ 450 | break; 451 | } else if(targetColor == opponentColor){ 452 | squares.push_back(candidate); 453 | break; 454 | } else { 455 | squares.push_back(candidate); 456 | } 457 | } 458 | } 459 | 460 | return squares; 461 | } 462 | 463 | bool Rook::moveTo(std::string destination) 464 | { 465 | if(this->color != board->getActiveColor()) return false; 466 | std::vector moves = getPossibleMoves(); 467 | if(std::find(moves.begin(), moves.end(), destination) == moves.end()) return false; 468 | 469 | bool captured = (board->getPieceCharAt(destination) != '-'); 470 | std::string origin = position; 471 | int pos; 472 | if(position == "a1"){ 473 | pos = board->castlability.find('Q'); 474 | if(pos != -1) board->castlability.erase(pos, 1); 475 | } else if(position == "h1"){ 476 | pos = board->castlability.find('K'); 477 | if(pos != -1) board->castlability.erase(pos, 1); 478 | } else if(position == "a8"){ 479 | pos = board->castlability.find('q'); 480 | if(pos != -1) board->castlability.erase(pos, 1); 481 | } else if(position == "h8"){ 482 | pos = board->castlability.find('k'); 483 | if(pos != -1) board->castlability.erase(pos, 1); 484 | } 485 | if(board->castlability.empty()) board->castlability = "-"; 486 | 487 | board->setPieceAt(this, destination); 488 | board->setPieceAt(new Vacant(), origin); 489 | 490 | board->changeActiveColor(); 491 | board->enPassantTarget = "-"; 492 | if(captured){ 493 | board->halfmoveClock = 0; 494 | } else { 495 | board->halfmoveClock += 1; 496 | } 497 | if(this->color == 'b') board->fullmoveNumber += 1; 498 | board->board2FEN(); 499 | return true; 500 | } 501 | 502 | std::vector Queen::getPossibleMoves() 503 | { 504 | return removeUnableMoves(getAttackingSquares()); 505 | } 506 | 507 | std::vector Queen::getAttackingSquares() 508 | { 509 | std::vector squares; 510 | std::string candidate; 511 | static const int direction[8][2] = { 512 | {-1, 1}, {0, 1}, {1, 1}, 513 | {-1, 0}, {1, 0}, 514 | {-1, -1}, {0, -1}, {1, -1} 515 | }; 516 | char targetColor; 517 | 518 | for(int i = 0; i < 8; i++){ 519 | candidate = position; 520 | while(true){ 521 | candidate = board->getDestination(candidate, direction[i][0], direction[i][1]); 522 | if(candidate == "-") break; 523 | targetColor = board->getPieceColorAt(candidate); 524 | if(targetColor == this->color){ 525 | break; 526 | } else if(targetColor == opponentColor){ 527 | squares.push_back(candidate); 528 | break; 529 | } else { 530 | squares.push_back(candidate); 531 | } 532 | } 533 | } 534 | 535 | return squares; 536 | } 537 | 538 | std::vector King::getPossibleMoves() 539 | { 540 | std::vector moves = getAttackingSquares(); 541 | std::vector attacked = board->getAttackingSquares(opponentColor); 542 | 543 | if(board->castlability != "-"){ 544 | if(color == 'w' && std::find(attacked.begin(), attacked.end(), "e1") == attacked.end()){ 545 | if(board->castlability.find('K') != -1 && board->getPieceCharAt("f1") == '-' && board->getPieceCharAt("g1") == '-' && std::find(attacked.begin(), attacked.end(), "f1") == attacked.end() && std::find(attacked.begin(), attacked.end(), "g1") == attacked.end()){ 546 | moves.push_back("g1"); 547 | } 548 | if(board->castlability.find('Q') != -1 && board->getPieceCharAt("b1") == '-' && board->getPieceCharAt("c1") == '-' && board->getPieceCharAt("d1") == '-' && std::find(attacked.begin(), attacked.end(), "c1") == attacked.end() && std::find(attacked.begin(), attacked.end(), "d1") == attacked.end()){ 549 | moves.push_back("c1"); 550 | } 551 | } else if(color == 'b' && std::find(attacked.begin(), attacked.end(), "e8") == attacked.end()){ 552 | if(board->castlability.find('k') != -1 && board->getPieceCharAt("f8") == '-' && board->getPieceCharAt("g8") == '-' && std::find(attacked.begin(), attacked.end(), "f8") == attacked.end() && std::find(attacked.begin(), attacked.end(), "g8") == attacked.end()){ 553 | moves.push_back("g8"); 554 | } 555 | if(board->castlability.find('q') != -1 && board->getPieceCharAt("b8") == '-' && board->getPieceCharAt("c8") == '-' && board->getPieceCharAt("d8") == '-' && std::find(attacked.begin(), attacked.end(), "c8") == attacked.end() && std::find(attacked.begin(), attacked.end(), "d8") == attacked.end()){ 556 | moves.push_back("c8"); 557 | } 558 | } 559 | } 560 | 561 | return removeUnableMoves(moves); 562 | } 563 | 564 | std::vector King::getAttackingSquares() 565 | { 566 | std::vector squares; 567 | std::string candidate; 568 | static const int direction[8][2] = { 569 | {-1, 1}, {0, 1}, {1, 1}, 570 | {-1, 0}, {1, 0}, 571 | {-1, -1}, {0, -1}, {1, -1} 572 | }; 573 | char targetColor; 574 | 575 | for(int i = 0; i < 8; i++){ 576 | candidate = getDestination(direction[i][0], direction[i][1]); 577 | if(candidate == "-") continue; 578 | targetColor = board->getPieceColorAt(candidate); 579 | if(targetColor == this->color){ 580 | continue; 581 | } else { 582 | squares.push_back(candidate); 583 | } 584 | } 585 | 586 | return squares; 587 | } 588 | 589 | bool King::moveTo(std::string destination) 590 | { 591 | if(this->color != board->getActiveColor()) return false; 592 | std::vector moves = getPossibleMoves(); 593 | if(std::find(moves.begin(), moves.end(), destination) == moves.end()) return false; 594 | 595 | bool captured = (board->getPieceCharAt(destination) != '-'); 596 | std::string origin = position; 597 | board->setPieceAt(this, destination); 598 | board->setPieceAt(new Vacant(), origin); 599 | 600 | board->changeActiveColor(); 601 | int pos; 602 | if(this->color == 'w'){ 603 | pos = board->castlability.find('K'); 604 | if(pos != -1){ 605 | if(destination == "g1"){ 606 | board->setPieceAt(board->getPieceAt("h1"), "f1"); 607 | board->setPieceAt(new Vacant(), "h1"); 608 | } 609 | board->castlability.erase(pos, 1); 610 | } 611 | pos = board->castlability.find('Q'); 612 | if(pos != -1){ 613 | if(destination == "c1"){ 614 | board->setPieceAt(board->getPieceAt("a1"), "d1"); 615 | board->setPieceAt(new Vacant(), "a1"); 616 | } 617 | board->castlability.erase(pos, 1); 618 | } 619 | } else { 620 | pos = board->castlability.find('k'); 621 | if(pos != -1){ 622 | if(destination == "g8"){ 623 | board->setPieceAt(board->getPieceAt("h8"), "f8"); 624 | board->setPieceAt(new Vacant(), "h8"); 625 | } 626 | board->castlability.erase(pos, 1); 627 | } 628 | pos = board->castlability.find('q'); 629 | if(pos != -1){ 630 | if(destination == "c8"){ 631 | board->setPieceAt(board->getPieceAt("a8"), "d8"); 632 | board->setPieceAt(new Vacant(), "a8"); 633 | } 634 | board->castlability.erase(pos, 1); 635 | } 636 | } 637 | board->enPassantTarget = "-"; 638 | if(captured){ 639 | board->halfmoveClock = 0; 640 | } else { 641 | board->halfmoveClock += 1; 642 | } 643 | if(this->color == 'b') board->fullmoveNumber += 1; 644 | board->board2FEN(); 645 | return true; 646 | } 647 | 648 | std::vector Vacant::getPossibleMoves() 649 | { 650 | std::vector moves; 651 | return moves; 652 | } 653 | 654 | // int main() 655 | // { 656 | // Board board = Board(); 657 | // board.print(); 658 | // board.move("e2", "e4"); 659 | // board.move("e7", "e5"); 660 | // board.move("d1", "h5"); 661 | // board.print(); 662 | // std::vector moves = board.getPieceAt("f7")->getPossibleMoves(); 663 | // for(int i = 0; i < moves.size(); i++){ 664 | // std::cout << moves[i] << std::endl; 665 | // } 666 | // } 667 | --------------------------------------------------------------------------------