// Ce corrigé n'utilise pas d'héritage car très peu vu en LFIAP4. Il serait peut-être pratique d'avoir une Class Piece et // des classes dérivées spécifique pour chaque type de pièce : pion, roi, reine, fou, tour, cavalier avec // simplement une surcharge de la fonction "coupValide" pour chaque type. Cela éviterait un switch dans le code de "coupValide" de Piece. // Dans ce corrigé, il y a 3 types IdPiece = TypePiece + Couleur. On pourrait en avoir qu'un en mettant 32 valeurs possibles dans le TypePiece. //========================================== NOYAU ============================================================== struct Vec2 // un struct suffit ici (mais une classe irait aussi) { int x,y; Vec2(int _x, int _y); static void testRegression() const; } // IMPORTANT: les operators Vec2 operator+(Vec2, vec2); Vec2 operator-(Vec2, vec2); bool operator==(Vec2 , Vec2 ); enum Couleur { BLANC, NOIR }; // IMPORTANT : ne surtout pas mettre R,G,B dans une structure car complètement inéfficace et poserait des problèmes de bugs. // Il faut contraindre le type à son minimum. Presque un bool serait possible mais un enum est mieux. // => typedef int IdPiece; // est le minimum à avoir : le numéro de pièce dans le tableau de pièce, -1 si la pièce n'existe pas // Il est sûrement plus efficace d'avoir un enum (+ un struct IdPiece défini juste avant ConfigurationJeu) // IMPORTANT : NE SURTOUT PAS METTRE DE STRING OU DE CHAINE DE CAR pour le type de pièce : ce ne serait pas très joli car complètement inéfficace enum TypePiece { VIDE=-1, PION1=0, PION2=1, PION3=2, PION4=3, PION5=4, PION6=5, PION7=6, PION8=7, ROI=8, REINE=9, FOU1=10, FOU2=11, CAVALIER1=12, CAVALIER=13, TOUR1=14, TOUR2=15 }; // --> Vec, TypePiece, Couleur, IdPiece class Piece { public: Piece( TypePiece typ, Couleur coul, const Vec2& pos); // Normalement ici, il n'y aura jamis VIDE dans le type void deplacement(Vec2 dep); // déplace la pièce du vecteur 'dep' : ne fait aucun comtrôle du damier void prise(); // change la pièce à prise bool coupValide(const ConfigrationJeu&, Vec2 depl); // un switch qui appelle une des fonctions coupValideXXX où XXX dépend du type de pièce, voir en private void ecriture(ofstream& f); // operator<< serait surement mieux void lecture(ofstream& f); // operator>> serait surement mieux ostream& operator<<(ostream& , const Piece& p) istream& operator<<(istream& , const Piece& p) static void testRegression() const; private: TypePiece m_type; // type de la pièce(un enum=un int, qui sert également pour accèder à la case du tableau de pièces blanche // ou noir de ConfigurationJeu Couleur m_couleur; // BLANC ou NOIR Vec2 m_pos; // Position sur le damier, valide uniquement si le bool m_enJeu est vrai bool m_enJeu; // Prise ou en jeu float m_importance; // une estimation empirique de la valeur d'une pièce, indépendament de sa position dans une partie bool coupValidePion(const ConfigrationJeu&, Vec2 depl); bool coupValideRoi(const ConfigrationJeu&, Vec2 depl); bool coupValideReine(const ConfigrationJeu&, Vec2 depl); bool coupValideFou(const ConfigrationJeu&, Vec2 depl); bool coupValideCavalier(const ConfigrationJeu&, Vec2 depl); bool coupValideTour(const ConfigrationJeu&, Vec2 depl); }; // Avec ce type ou peut représenter toutes les pièces du jeu. Un struct suffit ici (mais une classe irait aussi) // On aurait aussi pu avoir dans l'enum TypePiece, 32 valeurs avec toutes les pièces possibles 16 blanches+16 noires struct IdPiece { TypePiece type; Couleur coul; IdPiece(int t=-1, Couleur c=BLANC); }; // un coup=déplace la pièce de la case 'pos" vers 'pos+depl' struct Coup { Vec2 pos; Vec2 deplacement; Coup(const Vec2& p=Vec2(0,0), const Vec2& d=Vec2(0,0)); }; // --> Vec, TypePiece, Couleur, IdPiece, Piece class ConfigurationJeu { ConfigurationJeu(); // Appel init : void init(); // Place toutes les pièces et range toutes les infos dont identifiant. Appeler au début ou à chaque nouvelle partie // LES TROIS FONCTIONS IMPORTANTES DE CETTE CLASSE bool coupValide(const Coup& c) const; // indique si un déplacement 'depl' d'une pièce (repéré avec sa position 'pos') est possible std::vector calculTousLesCoupsPossibles(const Vec2& pos) const; // construit un tableau de tous les déplacement possible pour une pièce bool jouerCoup(const Coup& c); // joue un coup=déplace la pièce de la case 'pos" vers 'pos+depl' si le coup est possible. Renvoie faux si le coup n'est pas valide const Piece& getPiece(const Vec2& pos) const; // trouve la pièce à partir de sa position. const Piece& getPiece(const IdPiece& pos) const; // trouve la pièce à partir d'identifiant const IdPiece& getPiece(const Vec2& pos) const; // trouve l'ID de la pièce à partir de sa position. bool partieTerminee() const // renvoie vrai si un joueur a gagné Couleur vainqueur() const; // renvoie NOIR, BLANC. ATTENTION : valide seulement si la partie est terminée, // sinon renvoie la couleur qui a le score le plus élevé (voir f ci-dessus et ci-dessous) float score(Couleur col) const; // renvoie une estimation d'un score d'un joueur, calculé en fonction des pièces prises float distance(ConfigrationJeu cj); // renvoie un réel indiquant si les deux configurations sont proches : 0 indique indentique, un grand nombre=très différentes void ecriture(ofstream& f); void lecture(ofstream& f); // OU ostream& operator<<(ostream& , const ConfigrationJeu& p) istream& operator<<(istream& , const ConfigrationJeu& p) static void testRegression() const; private: // Avec juste ces 2 tableaux, on peut tout faire mais pour trouver une pièces à partir d'une position // il faut parcourir les 2 tableaux de pièces. Pas optimal mais ca marche. // Surement qu'un tableau de 32 pièces serait un peu plus optimal, ici Piece m_piecesB[16]; // toutes les pièces blanches Piece m_piecesN[16]; // toutes les pièces noires Piece m_vide; // un pièce vide (correspond à une case vide) // IMPORTANT : il faut ajouter un tableau comme ceci, qui fait double emploi avec les 2 tableaux de 2x16 pieces, // mais selon l'algo parfois on a besoin d'avoir la pièce et parfois on a besoin du damier ! IdPiece m_damier[8][8]; // le damier, chaque case sera soit un TypePiece associé à une couleur // remarque : dans ce code j'évite les pointeur et travaille avec des // identitifiants (plus pratique et moins sujet à bug), mais les pointeurs auraient été possible aussi Couleur m_joueurSuivant; // La couleur du joueur qui doit jouer const Piece& getPiece(IdPiece idp, Couleur col) const; // trouve la pièce à partir de son "nom", par exemple getPiece(FOU1, BLANC); }; // ================ NOYAU : gestion du dictionnaire de parties ================== // --> Vec, TypePiece, Couleur, IdPiece, Piece, ConfigrationJeu // Une unique partie avec toutes les étapes(=ConfigurationJeu) class Partie { public: Partie(); int nbEtape() const; const ConfigurationJeu& getIemeEtape(int ) const; void ajouterEtape(const ConfigrationJeu& cj); Coup coupJouerAEtapeN(int n); // renvoie le coup jouer à l'étape N de la partie. n doit être plus petit strictement que le nombre d'étape. void crop(int debut, int fin); // coupe tous les coups avant debut et après fin void ecriture(ofstream& f); void lecture(ofstream& f); // OU ostream& operator<<(ostream& , const Partie& p) istream& operator<<(istream& , const Partie& p) static void testRegression() const; private: std::vector m_etapes; // les N étapes d'une partie. Entre 2 étapes il s'est passé un "Coup", mais on stocke la configuration en entier. Stocker les coups auraient été possible aussi. }; // --> Vec, TypePiece, Couleur, IdPiece, Piece, ConfigrationJeu, Partie class DictionnaireParties { public: DictionnaireParties(); int nbPartie() const; void ajouterPartie(const Partie& part); void ajouterPartie(const string& fichier); const Partie& getIemePartie(int ) const; std::vector trouveLesConfigurationsEquivalentes(const ConfigrationJeu& cj); std::vector trouveLesConfigurationsEquivalentesMeilleursScores(const ConfigrationJeu& cj, float scoreMin); // LES DEUX FONCTIONS IMPORTANTES DE CETTE CLASSE std::vector suggererDesBonsCoups(const ConfigrationJeu& cj); Coup suggererMeilleurCoup(const ConfigrationJeu& cj); // appelle la fonction suggererDesBonsCoups et prend le max void ecriture(ofstream& f); void lecture(ofstream& f); // OU ostream& operator<<(ostream& , const ...& p) istream& operator<<(istream& , const ...& p) static void testRegression() const; private: std::vector m_dict; }; //========================================== AFFICHAGE ============================================================== // TRES IMPORTANT DE NE PAS OUBLIER CES 3 CLASSES et ELLES DOIVENT ETRE SEPAREES DU NOYAU (!!!). // --> Vec, TypePiece, Couleur, IdPiece, Piece, ConfigrationJeu class JeuTxt { public: JeuTxt(); void boucle(); private: ConfigurationJeu m_cj; void affichage() const; }; // --> Vec, TypePiece, Couleur, IdPiece, Piece, ConfigrationJeu class JeuSDL { public: JeuSDL(); void boucle(); // la boucle est IMPORTANTE private: ConfigurationJeu m_cj; Image m_damier; // les images sont IMPORTANTES Image m_pieces_blanc[TYPEPIECE_MAX]; Image m_pieces_noir[TYPEPIECE_MAX]; void affichage() const; Vec2 clicSourie(int mouseX, int mouseY); }; // --> Vec, TypePiece, Couleur, IdPiece, Piece, ConfigrationJeu, Partie, DictionnaireParties class GestionDictionnairePartiesSDL { public: GestionDictionnaireSDL(); void boucle(); private: JeuSDL m_jeu; DictionnaireParties m_dictParties; Widget m_ihm; void affichage() const; };