IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Patience, un jeu de cartes

Patience, un jeu de cartes


Ce programme n'a rien d'original. On en trouve des versions sur tous les ordinateurs mais la particularité la version présente est de comporter plusieurs jeux différents et de permettre d'en créer facilement de nouveaux.

Il est réalisé en Visual CPP avec l'atelier logiciel Microsoft Visual Studio Community 2019. Il est conçu avec la bibliothèque MFC standard sans utiliser l'architecture Document/Vue.

Le code présenté est largement documenté, ce qui permet, ajouté aux possibilités de l'atelier logiciel, de comprendre assez aisément les fonctions assurées par ce programme.

La nouvelle version est enrichie d'un programme de résolution permettant de résoudre certains jeux. Cette possibilité est un peu gadget dans ce genre de jeu mais les programmeurs peuvent aussi s'amuser !

Architecture de base

Le classique de l'architecture MFC permet de retrouver les classes principales d'une application de ce type :

  • CPatient, la classe d'application : C'est cette classe qui démarre l'application (fichiers Patience.cpp et Patience.h).

  • CMainFrame, la classe principale : C'est la fenêtre principale qui supporte la fenêtre de l'application (fichiers MainFrm.cpp et MainFrm.h).

  • CChildView, la classe de vue : C'est cette classe qui supporte la fenêtre de l'application et gère l’interface avec le joueur (fichiers ChildView.cpp et ChildView.h). Elle contient les différentes classes qui gèrent le jeu et l'appel des différentes fonctions offertes par le menu.


Classes qui gèrent le jeu

Le jeu est construit autours d'objets : 1- les Cartes, 2- les Piles de cartes et 3- la gestion du jeu.

  • Classe CCard : Supporte les cartes, l'objet CCard définit une carte avec tous ses attributs, le champ fig définit l'image présentée soit de dos soit de face suivant le champ hide (fichiers Card.cpp et Card.h). Cet objet comporte aussi des pointeurs de chainage permettant de relier ensemble les différentes cartes appartenant à la même pile ainsi qu'un champ CPoint ofs indiquant à l'affichage la position relative de la carte dans la pile.

  • Classe CPile : Supporte les piles de cartes. L'objet CPile décrit les différentes piles de cartes et les fonctionnalités associées (fichiers Pile.cpp et Pile.h). Des noms de types qualifient les différentes piles (BTYPE_ENUM : Tableau, Fondation, Stock, Waste pour les principaux). C'est aussi à ce niveau que sont traitées les règles d'échange de cartes entre les différentes piles (possibilité de prendre une carte ou un groupe sur une pile et de les déposer sur une autre pile). D'autres champs permettent de définir la position de la pile sur le tapis, le nombre et la disposition des cartes.

  • Classe CPlayer : Supporte toute l'organisation du jeu. Les interactions entre les objets et le joueur sont traitées à ce niveau comme le transport de cartes d'une pile à l'autre sous le contrôle de la souris, ainsi que les différentes phases du jeu avec des déplacements de cartes plus complexes comme par exemple dans les phases de distribution, de fin de jeu ou de rejeu (fichiers Player.cpp et Player.h).

  • Pile de transport : Une pile supplémentaire non cataloguée dans les piles du jeu CPile CPlayer::m_WorkPile est utilisée pour contenir la ou les cartes qui bougent. Elle est animée par la procédure CRect CPlayer::MoveOneStep() qui la déplace d'un pas à chaque cycle d'horloge. Cette procédure assure aussi l'enchainement des mouvements complexes toujours grâce à cette pile de transport (distribution, rejeu...).

    La procédure CRect CPlayer::MoveInit(...) initialise un mouvement. Cette pile est aussi déplacée par la souris grâce à la procédure CRect CPlayer::DragMove(...). La souris commande aussi la capture et de déposition avec les procédures CRect CPlayer::DragDown(...) et CRect CPlayer::DragUp().



Fonctions auxiliaires

  • Descripteur des jeux : Les fichiers CardsDesk.cpp et CardsDesk.h, contiennent la description des 7 jeux actuels du programme. Pour chacun un texte ASCII décrit la composition du jeu, le nombre de cartes utilisées, la disposition des piles et les règles d'usage. Les dimensions s'adaptent à la taille des cartes affichées. Les commentaires présents dans ces fichiers indiquent comment créer un nouveau jeu et pour les règles d'accès il faut se reporter aux fonctions CPile::RdCardsTest() et CPile::WrCardsTest() qui définissent les règles pour la capture et la déposition d'un paquet de cartes avec la souris dans les fonctions Drag/Drop.

  • Barre de choix des cartes : Les fichiers CardsToolBar.cpp et CardsToolBar.h donnent la description de la barre d'outils présentant les différents sets de cartes disponibles.

  • fonctions complémentaires : Les fichiers Utilities.cpp et Utilities.h décrivent des fonctions accessoires utilisées dans les différents programmes.

  • Des commandes accessoires de débugge complètent ce programme pour commander à partir du clavier le cyclage des opérations ou la génération d’un jeu de cartes non mélangées.



Résolution

C'est une fonction nouvelle, un peu gadget permettant de résoudre seulement certains jeux. Cette fonction est assurée par les classes CSolver(x), fichiers Solver(x).cpp et Solver(x).h.

Un principe assez simple est utilisé, consistant à construire un arbre décrivant tous les états possibles du jeu. Chaque nœud fixant un état du jeu et le passage au nœud suivant étant conditionné à un mouvement unique de cartes. Cette construction permet de découvrir parmi ces états une solution possible ou non. Ensuite il suffit de re parcourir cet arbre de l'état résolu jusqu'à la racine pour disposer de la liste des opérations nécessaires pour obtenir cette solution.

La classe CRPlay est utilisée pour décrire un nœud. Elle contient :

  • CRPlay *pNext, *pPrev lier bi directionnellement tous les nœuds entre eux et retrouver un nœud quelconque en parcourant cette liste,

  • CRPlay *pOrg permet de lier un nœud enfant à son père, celui dont est issu ce nouveau nœud après un seul déplacement de cartes. Suivant l'état du jeu un père peut avoir plusieurs enfants et chaque enfant peut devenir père à son tour et matérialiser ainsi une branche allant de la racine à la feuille. Cette branche parcourue en sens inverse, de la feuille à la racine grâce à ce pointeur, indique les actions nécessaires pour reproduire ce parcourt.

  • BYTE pCards[], une table de 59 à 119 octets (suivant le jeu), mémorise l'état courant du jeu en donnant la positions des cartes dans les différentes piles. C'est une copie compactée du jeu géré par CPlayer. Elle est conçue pour déterminer rapidement les opérations à exécuter pour progresser dans la résolution avec une occupation mémoire optimisée permettant de créer un grand nombre de nœuds.

  • DWORD cmd indique le mouvement de cartes caractérisant le nœud suivant



Les principales fonctions pour la création et la gestion de l'arbre sont :
  • void MakeSolution() : Génération de l'arbre avec ses différentes branches.

  • BYTE Analyze(CRPlay* pOrg, BYTE rndCmd) : Détermination des mouvements de carte à effectuer.

  • BYTE Solution((CRPlay* pOrg, DWORD cmd, bool frozen) : Réalisation d'un mouvement avec génération d'un nouveau (CRPlay avec sa nouvelle table pCards[]).

  • void CardsConvert(CPlayer *pPlayer, CRplay* pPlay) : Conversion d'un jeu affiché en BYTE pCards[] d'un noeud.

  • void Display(const CRPlay* pPlay) : Affichage d'une étape d'un jeu calculé, fonction inverse de la précédente.

  • DWORD setCmd(BYTE level, BYTE src, BYTE dst, BYTE lg) const, codant la commande avec :
    • level constitue le niveau du nœud dans l'arborescence,
    • src la position (dans la table pCards) de la pile source,
    • dst la position (dans la table pCards) de la pile destination,
    • lg le nombre de cartes échangées, toujours à partir de la fin de la pile.


  • Ce sont ces dernières informations qui seront placées dans l'History grâce à un programme de conversion qui adapte le format à celui de l'History :
    CRPlay* CSolver::toStep(CRPlay* pPlay, DWORD* History) const.

  • BOOL CSolver::DosesItAlreadyExist(const CRPlay* pNew, const CRPlay* pPlayEnd) const
    Détecte l'égalité de deux records pour éviter doublons et les bouclages dans les opérations.

  • Enfin quelques programmes de mise au point et de débugge utiles pour le développement : Par exemple en vérifiant l'intégrité des informations de la table pCards (présence du code de toutes les cartes et bon codage des différentes piles).



Temps de résolution

Le principe utilisé est théorique car très vite l'arbre réalisé comporte de nombreuses branches avec de nombreuses ramifications et entraine un temps exorbitant pour atteindre une profondeur suffisante pour arriver à une solution. Plusieurs méthodes sont utilisées pour rendre ce temps acceptable, la première consiste à déterminer des priorités dans les critères de construction, la deuxième est de générer des branches avec les extrémités gelées, c'est à dire qui dans un premier temps n'entraineront qu'un nombre limité de ramifications.

Dans la majorité des cas ce dispositif réduit considérablement le temps de résolution mais avec l'inconvénient de ne pas de trouver forcément la solution optimum ou même parfois d'ignorer une solution possible. Donc il ne faut donc pas voir ce programme comme un juge absolu, un joueur expérimenté et astucieux fait mieux. C'est une occasion de se mesurer à lui ! Et si on s'impatiente devant la lenteur du processus, il est possible de l'interrompre (clavier touche 'Echap')...
Developpez.com décline toute responsabilité quant à l'utilisation des différents éléments téléchargés.