Implémentation d’un jeu de cartes en C ++

J’essaie donc de créer un jeu de cartes et je suis encore un peu novice en C ++ et en indicateurs, alors j’ai quelques questions

J’ai donc la classe Card avec getRank , getSuit et la comparaison de deux objects Card . De la même manière, je fixe le numéro de dossier 1-13 pour le grade et 1-4 pour le costume. C’est donc une simple comparaison en comparant le rang + la couleur d’une carte avec le rang + la couleur d’une autre carte.

Donc, j’initialise et déclare les deux tableaux comme ceci:

 char Rank[13] = {1,...13}; char Suit[4] = ... 

et mes méthodes sont comme ça:

 char* getSuit(){ return Suit; } char* getRank(){ return Rank; } int Comp(Card *card) { if (rank+suit rank+card->suit) return 1; else if... else ... } 

Ma question est la suivante: est-ce une mauvaise idée d’avoir un tableau de caractères car je ne stocke que des entiers? Plus tard, je prévois de convertir ces nombres en sortie “Three Spade”, c’est pourquoi j’ai utilisé char.

Mon autre question est, est-ce que mes méthodes get ont l’air correct? Les méthodes get renverraient le tableau entier ou un index du tableau, ce que je veux. Et est-ce que j’utilise correctement ‘->’?

Je suis encore en train de dessiner, c’est pourquoi je n’ai que des bribes de code

Voici un brouillon à considérer et sur lequel vous construirez probablement votre classe:

 class Card { public: // TODO: provide suitable constructor... enum Suit {Hearts, Diamonds, Clubs, Spades}; enum Rank {A, K, Q, J, _10, _9, _8, _7, _6, _5, _4, _3, _2}; Suit getSuit() { return suit; } Rank getRank() { return rank; } // TODO: implement setters, etc... bool operator==(const Card& c) const { return rank == c.rank && suit == c.suit; } private: Suit suit; Rank rank; }; 

Je vous suggère donc d’utiliser des énumérations et d’utiliser un critère de comparaison exact pour comparer deux cartes.
De même, vous pouvez implémenter les opérateurs> et

Vous avez eu l’idée …

Le -> obtiendra la valeur de l’élément RHS de l’object référencé par le LHS. Donc oui, c’est correct tant que vous traitez avec des pointeurs sur le LHS.

Vous pourriez envisager d’utiliser Enums pour le costume.

Je vous recommande de réexaminer votre logique d’évaluation, comme @littleadv l’a dit.

C’est une mauvaise idée car c’est un gaspillage d’espace. Vous n’avez besoin de stocker qu’un seul numéro, pas tous.

En outre, vous pouvez implémenter l’opérateur == au lieu d’écrire un ensemble de méthodes, ce qui semblerait plus naturel.

BTW: Re votre logique – rang + costume est un critère de comparaison? Le rang 9 du costume 4 est meilleur que le rang 10 du costume 2? Je ne suis pas sûr du jeu que vous simulez, mais d’habitude, le classement précède les combinaisons … Mais cela dépend des besoins réels, bien sûr, il suffit de dire …

Re vos questions sur l’utilisation des méthodes -> et get – regardez les erreurs du compilateur quand vous les recevez. Oui, dans les méthodes get , vous renvoyez le tableau. Mais si vous arrêtez d’utiliser des tableaux et ne stockez que ce dont vous avez besoin, les méthodes seront efficaces.

Si vous souhaitez renvoyer une chaîne pour une raison quelconque – vous pouvez mapper l’index entier que vous stockez sur la chaîne correcte dans un tableau statique, afin de ne pas stocker de chaînes redondantes dans différentes instances des classes.

Grâce à ce processus de comparaison, vous obtiendrez éventuellement des cartes identiques … ce qui, je suppose, est erroné … Edit: il existe également un kit de départ pour le blackjack dans Visual Studio que vous pouvez vérifier.

Vous commencez à construire votre classe, étape par étape, avec du code fonctionnel. Commencez avec une classe simple qui ne possède qu’une getSuit et une méthode getRank . Écrivez une petite application qui le teste, par exemple comme ceci:

 int main() { Card card(10, 4); if (card.getSuit() != 4) { std::cout << "Wrong suit!" << std::endl; } return 0; } 

En écrivant un petit test comme celui-ci pour chaque fonctionnalité que vous implémentez, vous vous assurez que tout fonctionne correctement. En outre, si vous réfléchissez d'abord à la façon dont vous souhaitez utiliser une classe, vous comprendrez mieux la mise en œuvre.

En ce qui concerne la mise en œuvre réelle, cela dépend en grande partie de ce qui est enseigné dans votre classe. Une chose simple à considérer est de représenter les rangs et les costumes en utilisant un enum .

Ce type de syndicat discriminé est mieux géré dans les langues de la famille ML.

Par exemple dans OCaml:

 type suit = Hearts | Diamonds | Clubs | Spades type rank = Ace | King | Queen | Jack | Num of int type card = rank * suit 

Oui je sais, l’affiche a demandé une réponse C ++ …

Une carte est un mauvais candidat pour une classe – elle n’a aucun comportement, donc aucune méthode. C’est donc mieux modélisé comme une structure. Les cartes sont immuables (sauf si vous sortingchez), les données relatives aux membres const sont donc indiquées:

 enum Suit {Hearts, Diamonds, Clubs, Spades}; enum Rank {A, K, Q, J, _10, _9, _8, _7, _6, _5, _4, _3, _2}; struct Card { Card(Suit suit, Rank rank) : suit(suit), rank(rank) {} const Suit suit; const Rank rank; }; 

Si vous pouvez vivre avec la syntaxe card.first au lieu de card.suit, vous l’obtenez gratuitement en utilisant la paire standard:

 #include  typedef const std::pair Card; Card aceOfSpades(Spades, A); 

Bonus: Vous obtenez également des opérateurs raisonnables == et

Utiliser une interface

Il fournit aux étudiants un moyen simple d’utiliser les cartes dans le code qu’ils écrivent sans avoir access aux éléments internes d’une carte, sans pouvoir créer une carte spécifique et sans savoir comment les cartes sont mises en œuvre. Ce processus commence par le code pour une interface de carte, une interface appelée ICard.

 public interface ICard extends Comparable { public static final int SPADES = 0; public static final int HEARTS = 1; public static final int DIAMONDS = 2; public static final int CLUBS = 3; public int getSuit(); public int getRank(); } 

L’interface spécifie le comportement d’une carte sans fournir d’informations sur la manière dont les cartes sont implémentées. Une fois qu’ils savent que getSuit() renvoie une valeur telle que ICard.HEARTS et que getRank() renvoie une valeur comprise entre 1 (ace) et 13 (roi), les étudiants peuvent écrire du code à partir de cette spécification. Par exemple, voici le code pour vérifier si un tableau de cartes est sortingé. Nous ne soaps pas comment il a été sortingé (par exemple, tous les as arrivent-ils avant les deux ou tous les piques viennent-ils avant les cœurs?), Mais nous pouvons déterminer qu’un tableau est sortingé. 1 Commencer les noms d’interface avec un I majuscule, suivi d’un nom en majuscule, est une convention de dénomination commune dans la programmation orientée object dans de nombreux langages, pas seulement Java.

 public boolean isSorted(ICard[] list){ for(int k=1; k < list.length; k++){ if (list[k-1].compareTo(list[k]) > 0){ return false; } } return true; } 

À partir de cette interface ICard simple, nous pouvons poser aux étudiants de nombreux types de questions pour tester et réviser des concepts allant de la syntaxe Java à la résolution de problèmes concernant une, deux ou plusieurs cartes. Quelques exemples simples sont inclus ici, et d’autres sont disponibles sur le site Web. En répondant à ces questions, les étudiants doivent comprendre l’interface car il n’y a aucune implémentation. Les étudiants se concentrent sur le comportement plutôt que sur les variables d’instance et d’autres détails d’implémentation, tels que la création d’une chaîne pour représenter l’as de pique. Étude ICard / Questions sur le code

  1. Ecrivez la fonction isRed qui renvoie true si son paramètre ICard est rouge (coeurs ou diamants) et renvoie false sinon. public boolean isRed (Carte ICard) {…}

  2. Une paire est constituée de deux cartes du même rang (par exemple, deux rois ou deux huitièmes). Ecrivez la fonction isPair qui renvoie true si ses deux parameters ICard représentent une paire et renvoie false sinon. public boolean isPair (ICard a, ICard b) {…}

  3. Une couleur est une main, disons au poker, dans laquelle toutes les cartes ont la même couleur (par exemple, cinq cœurs ou cinq clubs pour une main de cinq cartes). Ecrivez la fonction isFlush qui renvoie true si le tableau de cartes est un flush et renvoie false sinon. public boolean isFlush (ICard [] main) {…}

  4. Au blackjack ou 21, la valeur d’une main est le total des cartes, où les valets, les reines et les rois (11, 12 et 13, respectivement, tels qu’ils sont retournés par getRank ()) comptent chacun pour 10 et qu’un as compte comme 1 ou 10, le meilleur des deux. Un total de plus de 21 est un buste; ce n’est pas bon de faire faillite. Écrivez la fonction handTotal, qui renvoie la valeur totale d’une main. public int handTotal (ICard [] hand) {…} De l’interface à l’implémentation L’interface ICard fournit suffisamment d’informations pour écrire du code sur les cartes, mais il n’existe aucun moyen de créer un tableau de cartes, par exemple, ou même une seule carte pour tester la fonctions écrites ci-dessus (comme isPair et handTotal). D’où viennent les cartes? Dans la plupart des exemples du monde réel, les cartes proviennent d’un deck. Nous allons concevoir une classe qui modélise un Deck, qui est essentiellement une fabrique permettant de créer et d’obtenir des cartes. Pour restr simple et encourager l’étude de certaines interfaces Java standard, la classe Deck implémentera l’interface java.util.Iterator. Par exemple, pour stocker toutes les cartes d’un jeu dans une variable ArrayList, nous pouvons utiliser le code suivant:

    Deck d = nouveau Deck (); Cartes ArrayList = new ArrayList (); while (d.hasNext ()) {Carte ICard = (ICard) d.next (); System.out.println (carte); cartes.add (carte); } System.out.println (“# de cartes dissortingbuées =” + cards.size ());

Les dernières lignes générées par cet extrait de code peuvent être comme indiqué ci-dessous. Ils seront différents à chaque fois car la classe Deck développée ici mélange les cartes qu’elle dissortingbue par itération. … As de pique valet de trèfle six de pique dix de cœur dix de pique nombre de cartes dissortingbuées = 52 Si nous changeons les lignes après la boucle comme suit, la sortie change également.

 Collections.sort(cards); for(int k=0; k < cards.size(); k++){ System.out.println(cards.get(k)); } 

System.out.println ("# de cartes dissortingbuées =" + cards.size ()); La sortie montre comment les cartes renvoyées par la classe Deck implémentent l'interface Comparable. … Neuf des clubs dix des clubs valet des clubs reine des clubs roi des clubs nombre de cartes dissortingbuées = 52 Le code complet de la classe Deck est donné ci-dessous. Les méthodes hasNext (), next () et remove () sont requirejses pour les classes qui implémentent l'interface Iterator. Le code ci-dessous montre comment les objects de type Carte sont construits.

 public class Deck implements Iterator{ private ArrayList myCardList; private int myIndex; public Deck(){ myCardList = new ArrayList(); for(int suit = ICard.SPADES; suit <= ICard.CLUBS; suit++){ for (int rank = 1; rank <= 13; rank++){ myCardList.add(new Card(suit,rank)); } } shuffle(); } private void shuffle(){ Collections.shuffle(myCardList); myIndex = 0; } public boolean hasNext() { return myIndex < myCardList.size(); } public Object next() { ICard card = (ICard) myCardList.get(myIndex); myIndex++; return card; } public void remove() { throw new UnsupportedOperationException(); } } 

Un object Deck stocke 52 cartes - ces cartes peuvent être obtenues à partir d'un object Deck via une itération, mais un object Deck ne peut pas être redissortingbué et réutilisé. Au lieu de cela, un nouvel object Deck doit être créé pour traiter de nouvelles cartes. Cela simplifie les choses et fournit un exemple facile à suivre d'une classe qui implémente l'interface Iterator. La méthode remove () est facultative --- pour la classe Deck appelant cette méthode lève une exception. Deck Study / Code Questions

  1. Juste avant l'appel de la méthode shuffle dans le constructeur, décrivez l'ordre des objects stockés dans myCardList.

  2. Décrivez comment chaque méthode Deck change si la variable d'instance myCardList est remplacée par un tableau d'objects Card, par exemple, private ICard [] myCardList; Quel choix pour myCardList est le meilleur? Pourquoi?

  3. Ecrivez un code client qui définit un object Deck et crée un tableau de 13 objects ICard représentant les bêches dissortingbuées à partir de la Deck. Faites cela en examinant chaque object dissortingbué et en ne stockant que les cartes de pique.

  4. Ecrivez le corps du constructeur hypothétique de la classe Hand spécifié ci-dessous

    private ArrayList myCards; / **

    • traiter les cartes numCards de d, stocker dans myCards
    • (supposons qu'il rest au moins des cartes numCards dans d) * / main publique (Deck d, int numCards) {}

Des decks aux cartes Notre préoccupation initiale était d'utiliser l'interface ICard plutôt que de nous préoccuper de la façon dont les cartes sont mises en œuvre. Néanmoins, à un moment donné, il doit y avoir une mise en œuvre. Il n'est pas difficile de dire que les objects Card devraient être créés par la classe Deck. C'est l'approche que nous avons utilisée ici. La classe Card est une classe privée déclarée dans la classe Deck. Il n’ya aucune raison de le déclarer dans Deck (le fichier Deck.java). Cependant, en le déclarant privé, nous le rendons impossible pour tout code class2; elle pourrait tout aussi bien être déclarée en tant que classe non publique au sein de méthodes autres que la classe Deck pour construire des objects Card. Cela aide à atteindre notre objective initial. Les programmes clients peuvent obtenir des cartes d'un deck, mais ne peuvent pas créer de cartes. Étant donné que la Deck fournit des objects ICard, il est impossible de modifier une carte une fois obtenue depuis la Deck, car la carte ICard interfacée ne prend pas en charge la modification d’une carte.

Tel qu'écrit, la classe Card privée définie dans la classe Deck ne prend pas non plus en charge la modification car ses variables d'instance d'état privé sont finales, mais il s'agit d'une protection supplémentaire qui n'est probablement pas nécessaire, car aucun code client n'a access à la classe Card privée. 2 Typiquement, les classes déclarées dans une autre classe font souvent référence à l'état de l'object englobant. Dans ce cas, la classe nestede Card est déclarée en tant que classe statique privée et ne peut donc pas faire référence à un état non statique privé dans un object Deck. La classe Card pourrait faire référence à l’état statique de Deck, mais il n’y en a pas dans ce code. La classe de cartes est disponible sur le site Web; nous ne l'incluons pas ici car son implémentation n'est pas directement liée à notre discussion sur le design. Un lecteur attentif pourrait prétendre que notre objective initial n'a pas été atteint. Le code client peut sortingcher, par exemple, en créant un object Deck, puis en dissortingbuant des cartes à cet object jusqu'à ce qu'un as d'espaces (ou toute autre carte) soit dissortingbué. Ceci est vrai dans la conception actuelle de la classe Deck. Toutefois, nous pourrions créer un object Deck singleton, de la même manière qu’une seule instance de la classe Random est utilisée dans l’étude de cas de biologie marine. Les objects Singleton sont généralement créés en déclarant des constructeurs afin qu'ils soient privés. Dans ce cas, le constructeur de Deck passerait de public à privé. Le code client obtient une Deck en appelant une méthode publique getInstance (), qui renvoie un object Deck statique privé stocké dans la classe Deck. La méthode getInstance crée cet object privé lors du premier appel de getInstance.