chessmcu/inc/Piece.hpp
A.M. Rowsell a56fb4d60f
dev: refactoring things to be more OOP! whoop whoop
Still struggling to understand all these concepts, and I will admit
to using ChatGPT to try and explain where I was going wrong which
did help quite a bit. But if I get this right it will be much
more robust and less "fragile" as they say.
2025-09-13 11:18:27 -04:00

235 lines
6.3 KiB
C++

// © 2025 A.M. Rowsell <amr@frzn.dev>
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as
// defined by the Mozilla Public License, v. 2.0.
#ifndef PIECE_HPP
#define PIECE_HPP
/** @file Piece.hpp
* @brief The header files for the Piece class.
*
* This file contains not only the definition of the Piece class itself, but also all
* of the derived classes (King, Queen, Rook, etc.) I was considering having them be in
* separate files, but each derived class does not contain that much code, so keeping them
* all in one place seemed easier to deal with. This may change in the future!
*
* Also defined here are important enums that help abstract chess terms, for example the
* PieceType, PieceColour, Rank, File, etc. These allow the code to be more readable and
* it helps make it obvious what certain bits of code are doing.
*/
#include <cstdint>
#include <algorithm>
#include <utility>
#include <string>
#include <memory>
#include <vector>
class Board;
enum PieceType {
PAWN, BISHOP, KNIGHT, ROOK, QUEEN, KING, EMPTY
};
enum PieceColour {
PIECE_WHITE, PIECE_BLACK, PIECE_EMPTY
};
enum Rank {
R1 = 0, R2 = 1, R3 = 2, R4 = 3, R5 = 4, R6 = 5, R7 = 6, R8 = 7, INVALID_RANK = 255
};
enum File {
A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7, INVALID_FILE = 255
};
enum CastleSide {
KINGSIDE = 1, QUEENSIDE = 2
};
/** @struct Square
* A struct that represents a square on the chess board.
*/
struct Square {
Rank rank; /**< enum Rank rank represents the rank of the Square */
File file; /**< enum File file represents the file of the Square */
/**
* A function to test if the square is a valid square on the chessboard
* @return True if the square is within the chessboard, False otherwise
*/
bool isValid() const {
return rank >= 0 && rank < 8 && file >= 0 && file < 8;
}
};
/**
* A struct representing the start and end squares of a Move.
*/
struct Move {
Square from;
Square to;
};
/**
* @class Piece Piece.hpp "inc/Piece.hpp"
* @brief A class abstracting chess pieces
*
* This is a polymorphic base class containing the basic properties all chess pieces have.
* It is intended to be extended by a child class for each piece Type, i.e. King, Queen, etc.
*/
class Piece {
private:
friend class Board;
protected:
PieceColour colour;
PieceType pieceType;
std::string pieceName; /**< std::string pieceName is a string showing the full name of the piece, i.e. "PAWN" */
std::string
pieceSymbol; /**< std::string pieceSymbol is a single character, using the standard algebraic chess notation, ie N for Knight */
bool hasMoved = false;
public:
Piece(PieceColour pColour) : colour(pColour) {
}
virtual ~Piece();
PieceColour getColour() const {
return colour;
}
std::string getPieceName() const {
return pieceName;
}
std::string getPieceSymbol() const {
return pieceSymbol;
}
PieceType getPieceType() const {
return pieceType;
}
bool checkIfMoved() const {
return hasMoved;
}
/**
* A virtual const function that should get all legal moves for a piece, but it may
* include some moves that need to be pruned, like those exposing the king to check.
* Still working out how to handle this.
*
* @param from the Square the Piece is currently on
* @param board A reference to the Board we are analyzing
* @return std::vector of Move: a list of all potentially legal moves
*/
virtual std::vector<Move> getLegalMoves(const Square &from, Board &board) const;
bool finalMoveChecks(std::vector<Move> *moveList, Board &board);
};
class King : public Piece {
private:
friend class Board;
public:
King(PieceColour colour) : Piece(colour) {
pieceName = "King";
pieceSymbol = "K";
pieceType = KING;
}
virtual std::vector<Move> getLegalMoves(const Square &from, Board &board) const override;
bool checkForCheck(Board &board) const;
bool checkForCastle(enum CastleSide side) const {
if(side == KINGSIDE)
return canCastleKS;
else if(side == QUEENSIDE)
return canCastleQS;
else
return false;
}
void setCastleQS(bool canCastle) {
canCastleQS = canCastle;
}
void setCastleKS(bool canCastle) {
canCastleKS = canCastle;
}
protected:
bool canCastleQS = true;
bool canCastleKS = true;
bool inCheck = false;
};
class Rook : public Piece {
friend class Board;
public:
Rook(PieceColour colour) : Piece(colour) {
pieceName = "Rook";
pieceSymbol = "R";
pieceType = ROOK;
}
virtual std::vector<Move> getLegalMoves(const Square &from, Board &board) const override;
};
class Queen : public Piece {
friend class Board;
public:
Queen(PieceColour colour) : Piece(colour) {
pieceName = "Queen";
pieceSymbol = "Q";
pieceType = QUEEN;
}
virtual std::vector<Move> getLegalMoves(const Square &from, Board &board) const override;
};
class Knight : public Piece {
friend class Board;
public:
Knight(PieceColour colour) : Piece(colour) {
pieceName = "Knight";
pieceSymbol = "N";
pieceType = KNIGHT;
}
virtual std::vector<Move> getLegalMoves(const Square &from, Board &board) const override;
};
class Bishop : public Piece {
friend class Board;
public:
Bishop(PieceColour colour) : Piece(colour) {
pieceName = "Bishop";
pieceSymbol = "B";
pieceType = BISHOP;
}
virtual std::vector<Move> getLegalMoves(const Square &from, Board &board) const override;
};
class Pawn : public Piece {
friend class Board;
public:
Pawn(PieceColour colour) : Piece(colour) {
pieceName = "Pawn";
pieceSymbol = "P";
pieceType = PAWN;
}
virtual std::vector<Move> getLegalMoves(const Square &from, Board &board) const override;
void promote(const Square &promotionSquare, Board &board, PieceType promoteTo);
protected:
bool vulnEnPassant = false;
bool firstMove = true;
};
#endif // PIECE_HPP