J'implémente un module d'échecs pendant que j'apprends le C ++. Dans le module j'ai (partiellement) implémenté:
Board
(grille, pièces, méthodes de déplacement, etc.) Couleur
(NOIR / BLANC) et Rang
(KING, QUEEN, ..., PAWN) et
Piece
regroupant la couleur et le rang (BLACK KING, ..., WHITE PAWN). Je décide maintenant comment représenter le contenu d'un carré sur le plateau (grille). Un carré doit contenir un Piece
(BLACK KING), ou rien. Options envisagées:
créer un struct Square
contenant deux membres de données, {bool occupé; Piece piece;}
, (éventuellement étendre la classe Piece
).
MON OBJECTION: La solution semble un peu trop lourde, et dans le cas où le carré n'est pas occupé alors le membre piece
devrait être vide. Je pense que cela peut entraîner la nécessité d'exceptions pour certaines méthodes de classe, ce qui ne devrait pas être nécessaire.
étendre l'énumération de Rank
ou Color
pour inclure une option vide.
MON OBJECTION: semble sémantiquement inélégant, hacky et artificiel.
recherche dans d'autres packages comme std::optional
MON OBJECTION: Pour le style de codage et la simplicité, j'aimerais éviter l'utilisation de machines supplémentaires. Est-ce que std :: optional
conviendrait même?
en utilisant NULL
ou nullptr
pour représenter l'état d'un carré vide
MON OBJECTION: Encore une fois, cela semble piraté. Le contenu d'un carré vide n'est PAS NULL
, ni le nombre 0
, ne devrait pas être comparable au nombre 26 ... mais devrait être une nouvelle constante EMPTY_SQUARE.
Aucune de ces options ne semble convenir. Existe-t-il un moyen plus élégant d'étendre une classe (comme Piece
) avec d'autres membres (comme EMPTY_SQUARE
) qui n'a aucun des membres de données de la classe d'origine? (Y a-t-il d'autres options?)
4 Réponses :
Une case sur votre échiquier peut contenir une pièce ou être vide. En termes plus abstraits, vous voulez avoir quelque chose de spécifique ou rien du tout. C'est exactement le cas d'utilisation pour lequel std :: optional
est fait. Vous ne pouvez pas éviter les machines supplémentaires de toute façon car vous avez besoin de quelque chose pour représenter un carré vide. Dans le C ++ de style 2019, un type facultatif est une solution parfaitement simple et idiomatique, en particulier lorsqu'il provient de la bibliothèque standard.
Concernant vos autres alternatives:
struct Square
: Je suis d'accord avec vos objections, en particulier la question sur ce qu'il faut mettre dans le membre piece
lorsque le carré est vide. En bref: struct Square
est une version naïve et super simple d'un optionnel. nullptr
: Parfois, un pointeur nul peut être une représentation appropriée de «rien». Habituellement, vous rencontrez le problème lorsque votre implémentation fonctionne avec des pointeurs de toute façon et que vous ne voulez pas introduire std::optional
- cela introduirait un état de rien-ish supplémentaire avec son ambiguïté . En général, je suis d'accord pour éviter les pointeurs nuls dans la mesure du possible. Que diriez-vous de conserver une collection de carrés non vides?
typedef std::map<square,piece,chessboard_order,chessboard_allocator> chess_position;
Un allocateur personnalisé est nécessaire car pas plus de 64 carrés sont possibles. De plus, la collection se rétrécit au fur et à mesure que le jeu avance, car des pièces sont prises.
Que diriez-vous que chaque personnage ait une position? Et enregistrez une liste de toutes les figures sur le tableau.
Le tableau aurait la logique associée à la vérification des règles de mouvement.
Vous voulez dire quelque chose comme stocker 16 figurines blanches et 16 noires, chacune avec 8 pions (comme
D'après moi, vous rencontrez un problème avec la représentation d'un état vide. De cette façon, le problème est inexistant.
Une autre méthode consiste à introduire un Null_Piece
( Null Object Pattern a >)
Il vous faudra ensuite créer la classe de base Piece
et les classes qui implémenteront les comportements des pièces Pawn
, Knight
, Bishop
, Rook
, Queen
et King
.
NullPiece
implémentera les mêmes comportements, mais à la place, il n'y aura aucune action.
De cette façon, chaque Carré
du tableau peut avoir un morceau.
Connexes: stackoverflow.com/questions/2537942/nullable-values-in-c et stackoverflow.com/questions/ 7645578 /…
Pourquoi ne pas créer une classe d'interface pour représenter le contenu carré et dériver une classe
Piece
ouEmpty
pour définir le contenu. Ensuite, votre structureSquare
doit contenir un pointeur vers l'interface. Vous pouvez vérifier plus tard quelle classe "enfant" existe dans le carréUtilisez
std :: optional
, c'est exactement le bon outil. Je ne comprends pas votre objection à propos des "mécanismes supplémentaires" à cette approche.En relation: une autre question sur l'application d'échecs essayant de résoudre un problème similaire.
Cela semble assez basé sur l'opinion. Personnellement, j'opterais pour
nullptr
parce que soit un carré contient un morceau, soit il n'en contient pas. Donc soit il pointe vers un objet pièce, soit il ne le fait pas. C'est un test facile et ne semble pas surprenant. Je ne considère pas du tout cela comme un "hackey".@NirMH suggérez-vous d'avoir une classe Square {...} avec un membre de données (public?) Comme bool contains_piece, et une classe dérivée Occupied_square: Square {...}? Cela créera-t-il des problèmes de typage? c'est à dire. Lorsqu'une pièce est déplacée d'un carré, la classe de ce carré (
Occupied_square
) sera rétrogradée à un ancienSquare
.