Search code examples
c++chess

How do I structure my program to avoid having to query for the types of objects?


I'm new to programming, and as one of my first projects that is unguided by a tutorial, I'm implementing a chess program. The way I have it structured now, there is a base class called Piece, and each kind of piece is derived from that base class. That way, I can implement the board as a two-dimensional array of Pieces, with the base class representing the empty squares. This was working well until I considered what I would need to do to determine whether a king is in check. The way to do this that seemed least wasteful to me was to give the King class a function that, for example, checks whether the king has lines of sight with any enemy rooks or bishops, checks whether there are enemy pawns in either of the two squares from which they could reach the king, etc. But to do this, the function would need to know which specific derived classes are where. I suppose I could make it work by giving each piece class an ID member for the function to check, but that seems redundant, and besides, I gather that a program that needs such information is considered badly structured. Have I in fact made a bad choice so far, and if so, how might I clean it up?

Apologies that this isn't a very general question. I couldn't think of a way to generalize it that would still be helpful to me.


Solution

  • From what I can glean from your description, polymorphism seems like what you're looking for

    int abs(int x) { return x < 0 ? -x : x; }
    
    class Piece
    {
    public:
        Piece(int x, int y) : x{x}, y{y} {}
        virtual bool reachable(const Piece&) const = 0;
    
        // other methods
    
    protected:
        int x, y;
        std::pair<int, int> delta(const Piece& p) const
        {
            return {abs(x - p.x), abs(y - p.y)};
        }
    };
    
    class Knight : public Piece
    {
    public:
        using Piece::Piece;
        bool reachable(const Piece& p) const override
        {
            auto [dx, dy] = delta(p);
            if((dx == 1 && dy == 2) || (dx == 2 && dy == 1))
                return true;
            return false;
        }
    };
    
    class King : public Piece
    {
    public:
        using Piece::Piece;
        bool reachable(const Piece& p) const override
        {
            auto [dx, dy] = delta(p);
            if(dx <= 1 && dy <= 1)
                return true;
            return false;
        }
    };
    
    // other pieces
    
    class Board
    {
    public:
        bool check(const King& k) const
        {
            for(auto& p : pieces)
                if(p->reachable(k))
                    return true;
            return false;
        }
    private:
        std::vector<std::unique_ptr<Piece>> pieces;
    };
    

    A Piece has some common interface including reachable, indicating whether the piece can reach the specified piece.

    To check whether there is a check, iterate through the collection of pieces and see if they can reach the specified king.