I have a parent class Piece that has several child classes including Queen, Rook and Bishop. However, a Queen can move like a Rook and a Bishop combined so it is conceptually the fusion of the two. I want to call functions from the Rook and Bishop class in the Queen class. I have tried making Queen inherit from both Rook and Bishop instead of inheriting from Piece however, then it inherits two copies of attributes from Piece (one in Bishop and one in Rook) which means I can't refer to either.
Is there a way to do this or do I just have to rewrite the functions for Queen?
EDIT: Sorry, I think my question is a bit hard to understand with just words. Since this is for university, I am unable to provide code as that may be against their plagiarism policies but here are the header files for each class.
I basically want to call the canMoveTo
and squaresInBetween
functions of Bishop and Rook from Queen since Bishop can move diagonally, Rook can move horizontally and vertically, and Queen can move diagonally, horizontally and vertically.
Piece.h
#ifndef PIECE_H
#define PIECE_H
#include <string>
#include <vector>
using namespace std;
class Piece // abstract
{
public:
Piece(string currentPos); // constructor - sets currentposition
virtual bool canMoveTo(string square) = 0; // able to move to that square on an empty board (movement rules)
virtual vector<string> squaresInBetween(string square) = 0; // squares in between current position and square (including square)
virtual ~Piece(); // virtual destructor to call correct destructor for each piece
string getPosition();
protected:
string currentPosition; // board coordinates (e.g. "d4") // change to char [2] later?
private:
friend class Player;
void setPosition(string pos);
};
#endif
Rook.h
#ifndef ROOK_H
#define ROOK_H
#include "Piece.h"
class Rook : public Piece
{
public:
Rook(string currentPos);
virtual bool canMoveTo(string square);
virtual vector<string> squaresInBetween(string square);
};
#endif
Bishop.h
#ifndef BISHOP_H
#define BISHOP_H
#include "Piece.h"
class Bishop : public Piece
{
public:
Bishop(string currentPos);
virtual bool canMoveTo(string square);
virtual vector<string> squaresInBetween(string square);
};
#endif
Queen.h
#ifndef QUEEN_H
#define QUEEN_H
#include "Piece.h"
class Queen : public Piece
{
public:
Queen(string currentPos);
virtual bool canMoveTo(string square);
virtual vector<string> squaresInBetween(string square);
};
#endif
CodePudding user response:
Well, if you are dead set on an OOP solution -- i.e. a solution involving inheritance -- you could use "mix-in" type classes MovesLikeRook
and MovesLikeBishop
and then make Rook and Bishop inherit from the appropriate class and have Queen inherit from both.
But as others have said, you don't need to solve every program with OOP. In this case if you don't want to write the same rook move and bishop move code multiple times you can just put that logic in free or static functions that can be called by Rook, Bishop, and Queen. I mean, that is one idea. But there are lots of ways to not repeat yourself and also not use a complicated object hierarchy, which is generally seen as a good thing in modern programming.
CodePudding user response:
I have tried making Queen inherit from both Rook and Bishop instead of inheriting from Piece however, then it inherits two copies of attributes from Piece (one in Bishop and one in Rook) which means I can't refer to either.
This case is known as diamond inheritance. C has a way of solving the problem of "two copies of attributes from Piece": virtual inheritance. It will look like:
class Piece {/*...*/};
class Rook : virtual public Piece {/*...*/};
class Bishop : virtual public Piece {/*...*/};
class Queen : public Rook, public Bishop {/*...*/};
This might work for you, this is rarely a good design.
CodePudding user response:
This is how I would check moves without virtual canMoveTo and or multiple inheritance:
#include <vector>
#include <string>
//-------------------------------------------------------------------------------------------------
// A move is described as steps a piece can make
// all moves only have to desribe the move to top-right as one step
// range will describe the range of a piece in one turn.
// moves will be combined with directions to cover all allowed moves in a turn
struct Move
{
Move(const int x, const int y, const int r) :
x_step{ x },
y_step{ y },
range{ r }
{
}
int x_step;
int y_step;
int range;
};
//-------------------------------------------------------------------------------------------------
// struct to contain directions of a move in a turn
struct Direction
{
int x_direction;
int y_direction;
};
//-------------------------------------------------------------------------------------------------
class Piece
{
public:
explicit Piece(const std::string& start_position, std::vector<Move>&& moves) :
m_position{ start_position },
m_moves{ moves }
{
};
// no need to make this virtual a piece will have all the information it needs
// without inheritance
bool can_move_to(const std::string& position)
{
// use vector of allowed moves
for (const auto& direction : m_directions)
{
for (const auto& move : m_moves)
{
for (int range = 0; range < move.range; range)
{
// calculate if piece can reach position using
// the step sizes from the piece, direction and range
std::string calculated_position;
// ... todo calculation here
if (calculated_position == position) return true;
}
}
}
return false;
}
protected:
Piece() = default;
virtual ~Piece() = default;
Piece(const Piece&) = delete;
Piece& operator=(const Piece&) = delete;
Piece(Piece&&) = delete;
private:
std::string m_position;
std::vector<Move> m_moves;
static std::vector<Direction> m_directions;
};
//-------------------------------------------------------------------------------------------------
// all information needed to check all directions in which a piece can move
std::vector<Direction> Piece::m_directions{ {1,1},{-1,1},{1,-1},{-1,-1} };
//-------------------------------------------------------------------------------------------------
class Rook :
public Piece
{
public:
explicit Rook(const std::string& position) :
Piece(position, {{1,0,8}, {0,1,8}}) // a rook can move one horizontal, or one vertical up to a range of 8 (board size)
{
}
};
//-------------------------------------------------------------------------------------------------
class Bishop :
public Piece
{
public:
explicit Bishop(const std::string& position) :
Piece(position, {{1,1,8}}) // a rook can move diagonally,up to a range of 8 (board size)
{
}
};
//-------------------------------------------------------------------------------------------------
class Knight :
public Piece
{
public:
explicit Knight(const std::string& position) :
Piece(position, { {1,2,1}, {2,1,1} }) // a knight can move 1 horizontally and 2 vertically, or 2 horizontally and 1 vertically
{
}
};
//-------------------------------------------------------------------------------------------------
class Queen :
public Piece
{
public:
explicit Queen(const std::string& position) :
Piece(position, { {1,0,8}, {0,1,8}, {1,1,8} }) // a queen can move horizontally, vertically and diagonally up to a range of 8
// IMO the extra complexity of multiple inheritance or looking up this information
// in Rook & Bishop is not worth the extra code.
{
}
};
int main()
{
Queen queen{ "e7" };
auto can_move = queen.can_move_to("a1");
}