Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Exercices] Venez vous entraîner !

(v2)

    4 juillet 2011 à 18:22:26

    germino : correction de diverses coquilles, ajout d'une note sur les exo de nanoc.
    • Partager sur Facebook
    • Partager sur Twitter
      4 juillet 2011 à 18:56:13

      il faut vraiment mettre toute les solutions comme ca je coderait (et pas que moi j'espère :p ) les solutions manquantes
      • Partager sur Facebook
      • Partager sur Twitter
        4 juillet 2011 à 20:26:42

        Solution au triangle de Pascal



        [C++11] (même s'il est possible de modifier légèrement le code pour s'en passer)



        Vous pouvez trouver l'exercice ici.

        Si vous ne comprenez pas certains termes, je vous invite à chercher sur notre grand ami Google, et, si vous ne trouvez pas, à poster votre question à la suite de ce message.

        Bon ! Les présentations faites, je vais pouvoir vous expliquer une solution de cet exercice du triangle de pascal.

        Commençons par le plus "simple" (encore que ...) : l'interface avec l'utilisateur. Si vous voulez voir directement la partie du calcul du triangle de Pascal, vous pouvez utiliser votre molette de souris jusqu'au prochain titre en bleu. :)

        L'interface utilisateur



        Le main est très simple :
        int main(int, char**)
        {
            std::cout << "===== Programme de calcul du triangle de Pascal ====="
                      << std::endl;
            while (menu()); // Tant que l'utilisateur veut continuer, on continue.
            return EXIT_SUCCESS;
        }
        


        En fait, on va simplement continuer le programme (de la fonction menu()), tant que l'utilisateur ne souhaite pas quitter. Puis on arrêtera.
        Le define EXIT_SUCCESS est disponible dans <cstdlib>.

        Puis, voyons la fonction menu(). Elle se charge de l'affichage du menu, et de la gestion des réponses. Elle renverra true si l'utilisateur souhaite continuer à utiliser le programme, false sinon :
        bool menu()
        {
            if (!std::cin.good()) return false; // Il y a un souci d'entree-sortie.
        
            std::cout <<
                "Que vous souhaitez-vous ?" << std::endl <<
                "- Afficher les [p]remieres lignes du triangle" << std::endl <<
                "- Afficher une [l]igne" << std::endl <<
                "- Afficher une [c]ase" << std::endl <<
                "- Afficher [K] parmi N" << std::endl <<
                "- Afficher le binome de [N]ewton" << std::endl <<
                "- [Q]uitter" << std::endl <<
                std::endl <<
                "Que souhaitez-vous ? (p/l/c/k/n/q) "
                ;
        

        La condition à la première ligne est importante : si il y a un souci avec std::cin, qui est notre moyen d'interaction avec l'utilisateur, on arrête tout.
        Vous la verrez revenir plutôt souvent.
        Donc, d'abord on affiche le menu. Ensuite, on récupère le choix :
        char choix;
            std::cin >> choix;
            if (!std::cin.good()) return false; // Il y a un souci d'entree-sortie
        

        Et enfin on le traite.

        Pour éviter de surcharger la fonction, on va se contenter de reléguer tout le travail à des fonctions "subalternes", qui vont devoir récupérer les valeurs demandées, et gérer leur affichage.
        choix = std::tolower(choix); // Pour autoriser a entrer des majuscules
            switch (choix) {
                case 'p':
                    afficherLignes();
                    break;
                case 'l':
                    afficherLigne();
                    break;
                case 'c':
                    afficherCase();
                    break;
                case 'k':
                    afficherKParmiN();
                    break;
                case 'n':
                    binomeNewton();
                    break;
                case 'q':
                    return false; // On arrete
                default:
                    std::cout << "Merci de faire un choix dans la liste."
                              << std::endl;
                    // On continue, parce que l'utilisateur doit bien choisir
                    // quelque chose de coherent.
            }
        


        Pour terminer cette fonction, si on est arrivé là - c'est-à-dire si 'q' n'a pas été choisi, on va simplement réafficher le menu.

        return true; // L'utilisateur n'a pas demande a arreter.
        }
        


        Maintenant, les fonctions elles-mêmes. On va les voir de la plus simple à la plus complexe.

        D'abord, voyons donc afficherCase() et afficherKParmiN(). Elles sont très ressemblantes : d'abord on récupère les coordonnées de la case à afficher, et ensuite on l'affiche. Les deux différences sont dans les messages affichés et dans l'appel à la fonction de calcul de case, que nous verrons plus tard.
        void afficherCase()
        {
            std::cout <<
                "De quelle ligne souhaitez-vous afficher la case ? ";
            int ligne;
            std::cin >> ligne;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::cout <<
                "Quelle case souhaitez-vous afficher ? ";
            int num;
            std::cin >> num;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::cout << Pascal::uneCase(num - 1, ligne - 1) << std::endl;
        }
        
        void afficherKParmiN()
        {
            int k, n;
        
            std::cout << "K ? ";
            std::cin >> k;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::cout << "N ? ";
            std::cin >> n;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::cout << Pascal::uneCase(k, n) << std::endl;
        }
        


        Maintenant, voyons comment une ligne est affichée. D'abord on va récupérer le numéro de la ligne :
        void afficherLigne()
        {
            std::cout <<
                "Quelle ligne souhaitez-vous afficher ? ";
            int num;
            std::cin >> num;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        

        Ensuite, il va être nécessaire de calculer le contenu de la ligne. Ici aussi, on va reléguer le calcul à une fonction spécialisée :
        Pascal::ligne_type ligne;
            Pascal::ligne(num - 1, ligne);
            // -1 parce qu'il considere la premiere ligne comme etant 0
        


        Et enfin, on va afficher le résultat :
        for (Pascal::case_type c : ligne) {
                std::cout << c << " ";
            }
            std::cout << std::endl;
        }
        

        Qu'est-ce que c'est, cette boucle bizarre ?

        Cette boucle for mérite en effet une explication.
        C'est en fait l'une des particularités du c++11 particulièrement utile.
        En fait, elle va parcourir ligne, et mettre chaque valeur dans c, qui est de type Pascal::case_type.
        Et elle va simplement afficher la valeur de la case, suivie d'un espace pour éviter de coller les nombres.

        Maintenant, passons à l'affichage du binôme de Newton. Ce n'est à la base que peu de choses de plus que l'affichage d'une ligne, si ce n'est qu'il faut également afficher les A^X et B^Y.
        Le début est très similaire :
        void binomeNewton()
        {
            size_t n;
            std::cout << "N ? ";
            std::cin >> n;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::cout << "(A + B) ^ " << n << " =";
        
            Pascal::ligne_type ligne;
            Pascal::ligne(n, ligne);
        

        Vous noterez qu'on a déjà affiché le début, qui est identique pour toutes les valeurs de N choisies.

        Ensuite, on va parcourir chacune des valeurs, mais cette fois-ci on ne peut pas utiliser la nouvelle boucle foreach-like, mais il va falloir utiliser une bonne vieille boucle, car on aura besoin de l'indice aussi.
        On va donc commencer par calculer les coefficients qu'il faut mettre après A et B (leurs puissance, quoi) :
        for (size_t k = 0 ; k <= n ; ++k) {
                const size_t coeffA = n - k;
                const size_t coeffB = k;
        


        Maintenant, il va falloir afficher le coefficient global : la valeur du triangle de Pascal correspondant à cette position. On ne le fait que si elle n'est pas 1, sinon le coefficient est inutile :
        if (ligne[k] != 1) std::cout << " " << ligne[k];
        


        Par la suite, il faudra afficher A et sa puissance. Si A est à la puissance 0, il est strictement inutile de l'afficher. Si sa puissance est 1, on affiche simplement A.
        On procède de même pour B et sa puissance :
        if (coeffA != 0) {
                    std::cout << " A";
                    if (coeffA != 1) std::cout << " ^ " << coeffA;
                }
                if (coeffB != 0) {
                    std::cout << " B";
                    if (coeffB != 1) std::cout << " ^ " << coeffB;
                }
        


        Enfin, on va afficher un '+' si on n'en est pas encore au dernier caractère :
        if (k != n) std::cout << " +";
            }
            std::cout << std::endl;
        }
        


        Pour terminer cette partie, voyons la fonction qui affiche les premières lignes du triangle.
        C'est un début classique, on se contente de demander le nombre de lignes et de déléguer le calcul à une fonction particulière :
        void afficherLignes()
        {
            std::cout << "Combien de lignes souhaitez-vous afficher ? ";
            size_t nb;
            std::cin >> nb;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::vector<Pascal::ligne_type> lignes;
            Pascal::lignes(nb, lignes);
        

        Ensuite, il va falloir calculer les longueurs en caractères que vont devoir avoir chaque colonne de chiffres, pour avoir des colonnes aisément distingables (on verra la fonction calculerLongueur juste après, mais globalement elle calcule la longueur en chiffres d'un nombre.) :
        // On va calculer les longueurs pour chaque colonne, pour pouvoir
            // egaliser.
            std::vector<size_t> longueurs;
            for (size_t colonne = 0 ; colonne < nb ; ++colonne) {
                size_t longueurMax = 0;
                for (size_t ligne = colonne ; ligne < nb ; ++ligne) {
                    size_t longueur = calculerLongueur(lignes[ligne][colonne]);
                    if (longueur > longueurMax) longueurMax = longueur;
                }
                longueurs.push_back(longueurMax);
            }
        

        Maintenant, il va falloir calculer la longueur de chaque ligne, pour faire un triangle isocèle :
        // On calcule les longueurs de lignes
            std::vector<size_t> longueursLignes;
            for (size_t ligne = 0 ; ligne < nb ; ++ligne) {
                longueursLignes.push_back(std::accumulate(
                    std::begin(longueurs),
                    std::begin(longueurs) + ligne + 1,
                    lignes[ligne].size() - 2 // Le nombre d'espaces entre nombres.
                    ));
            }
        


        Ici, il est intéressant de regarder le calcul. Il utilise std::accumulate. Globalement, cette fonction va faire la somme de tous les éléments entre son premier paramètre inclus et son deuxième paramètre exclus, plus son troisième paramètre.
        Elle va donc commencer par faire la somme entre std::begin(longueurs) et std::begin(longueurs) + ligne + 1.
        Qu'est-ce que ça signifie ?

        En fait, std::begin(longueurs) va renvoyer une sorte de pointeur vers le début de longueurs (un itérateur, en fait).
        Le premier paramètre est donc aisé à comprendre.
        Le second est un peu plus complexe : en fait, il va dire que là où on s'arrête de calculer la somme est quand on a parcouru ligne valeurs.
        Pourquoi le +1, alors ? En fait, c'est simplement parce que le dernier élément est exclus, il va donc falloir donner l'élément suivant le dernier dont on veut calculer la longueur.

        On va donc avoir la somme des longueurs des ligne premières colonnes. Ce qui équivaut à la longueur de la ligne, excepté les espaces. Espaces que l'on va rajouter tout de suite.

        En effet, on va ajouter grâce au 3ème paramètre le nombre d'espaces entre les nombres, pour avoir, au final, la longueur totale de la ligne.

        On va avoir ensuite besoin de la longueur de la ligne la plus longue, pour pouvoir "isocéliser" notre triangle. Elle est simple : la dernière ligne du triangle de pascal est forcément la plus grande :
        const size_t longueurMax = longueursLignes.back();
        


        Enfin, on va en arriver à l'affichage.
        On va afficher les lignes une à une :
        // Et maintenant on affiche.
            for (size_t ligne = 0 ; ligne < nb ; ++ligne) {
        


        Puis on va voir si le triangle vaut la peine d'être "isocélisé". J'ai remarqué que, passé les 5 premières lignes du triangle, le fait qu'on en arrive aux nombres à deux chiffres fait que le triangle a l'air beaucoup moins joli. Vous pouvez envisager d'améliorer ceci.
        Du coup, si il y a cinq lignes ou moins, on va ajouter le nombre d'espaces nécessaires pour centrer la ligne, qui est égal à la moitié de la longueur qui manque par rapport à la ligne la plus longue.
        On en arrive donc à ceci :
        // D'abord, on "isocelise", seulement si on veut 5 lignes ou moins
                // car sinon l'affichage est tres mauvais, a cause des espaces
                // rajoutes entre les nombres :
                if (nb <= 5) {
                    const size_t longueurLigne = longueursLignes[ligne];
                    std::cout <<
                        std::string((longueurMax - longueurLigne) / 2, ' ')
                        ;
                }
        


        Enfin, on va afficher la ligne elle-même. En fait, on va récupérer chaque valeur de la ligne dans une std::string (grâce à std::ostingstream). Puis on va devoir égaliser la longueur de la colonne. Pour ce faire, on va ajouter le nombre nécessaire d'espaces à la fin du nombre. Enfin, on va afficher la valeur ainsi formatée :
        // Et on affiche la ligne
                for (size_t colonne = 0 ; colonne <= ligne ; ++colonne) {
                    // On recupere l'entier dans une string
                    std::ostringstream oss;
                    oss << lignes[ligne][colonne];
                    std::string valeur = oss.str();
                    // On egalise la longueur
                    valeur.resize(longueurs[colonne], ' ');
                    // Et on affiche.
                    std::cout << valeur << " ";
                }
        

        Voilà ! Cette fonction est terminée !
        std::cout << std::endl;
            }
        }
        


        Il ne nous reste plus qu'à voir la fonction calculerLongueur, et on pourra (enfin !) passer au calcul lui-même.
        size_t calculerLongueur(int v)
        {
            size_t longueur = 1;
            while (v >= 10) {
                v /= 10;
                ++longueur;
            }
            return longueur;
        }
        


        Cette fonction est plutôt simple : On a un de long par défaut. Tant qu'on est supérieur à 10, on va augmenter d'un caractère la longueur, et on va retirer un caractère de longueur (en divisant par 10).

        Le calcul proprement dit



        Commençons par voir le header, pour savoir ce qu'on va faire :
        #ifndef PASCAL_HPP_INCLUDED__
        #define PASCAL_HPP_INCLUDED__ 1
        
        #include <string>
        #include <vector>
        
        namespace Pascal
        {
            typedef unsigned long long case_type;
            typedef std::vector<case_type> ligne_type;
        
            void lignes(int nb, std::vector<ligne_type> & lignes);
        
            void ligne(int num, ligne_type & ligne);
        
            case_type uneCase(int num, int ligne);
        }
        
        #endif
        


        On va donc avoir un namespace (un tutoriel sur les namespaces doit traîner quelque part sur le SdZ, si la notion n'est pas présente dans le nouveau tutoriel officiel) Pascal, qui va contenir toutes nos fonctions ... Et nos types.

        On va ensuite définir ce qu'est une case, et ce qu'est une ligne :
        typedef unsigned long long case_type;
            typedef std::vector<case_type> ligne_type;
        

        Si vous ne le saviez pas, typedef permet de définir un nouveau type, ou plus précisément un nouveau nom de type qui veut dire exactement la même chose que le type de base.
        Ici, on définit donc deux types : ce qu'est une case, et ce qu'est une ligne.
        L'utilité de ces deux types peut sembler nulle, mais en fait non.
        Pendant que je développais ce programme, j'étais parti sur un int. Et, au moment de tester la case, quand j'essaie 14 parmi 120 (cf. énoncé), zut ! J'ai un nombre négatif ! C'était dû au simple fait que je dépassais la valeur maximum d'un int. Du coup, grâce à ces typedefs, il m'a suffit de modifier à un endroit pour que le bug soit supprimé. En effet, un unsigned long long peut contenir beaucoup plus de données qu'un int.
        Voilà comment les typedefs m'ont sauvé la vie !

        Ensuite viennent les fonctions de calcul, que l'on va analyser tout de suite.

        Commençons à nouveau par le calcul d'une case. J'ai pris la formule sur Wikipédia. Tant que l'on reste à des K raisonnables (disons, environ 10000), le programme devrait tourner sans problème. Si vous voulez essayer avec un K plus grand, vous devriez modifier le calcul, pour qu'il ne soit plus récursif mais itératif (pour ceux qui ne connaissent pas ces termes, Wikipédia est votre ami !) :
        case_type uneCase(int num, int ligne)
            {
                // Ces noms collent mieux aux formules Wikipedia
                const int n = ligne;
                const int k = num;
                // On peut arreter ici.
                if (k == 0) {
                    return 1;
                }
                // Et la formule wikipedia
                // la, comme on a un objet peu lourd, on peut utiliser return
                return uneCase(num - 1, ligne - 1) * n / k;
            }
        

        On peut noter qu'on arrête le calcul si k = 0, parce que sinon ça ferait une récursion infinie (un peu comme une boucle infinie, en pire !).

        Maintenant, observons le calcul d'une ligne donnée. Il faut que je l'avoue, ce calcul n'est pas du tout optimisé. Vous pourriez, pour aller plus loin, essayer de prendre avantage de la symétrie du triangle de Pascal pour éviter la moitié du calcul.
        Bref, le code :
        void ligne(int num, ligne_type & ligne)
            {
                ligne_type Resultat;
                Resultat.push_back(1);
                for (int i = 1 ; i <= num ; ++i) {
                    // Tant qu'il reste des elements a ajouter a la ligne
                    Resultat.push_back(uneCase(i, num));
                }
                std::swap(Resultat, ligne); // Maintenant vous savez tout.
            }
        

        Le commentaire de swap dit qu'on sait tout, mais en fait vous ne savez encore rien : le commentaire expliquant swap est plus haut dans le code, mais plus bas dans mon explication. :diable:
        En fait, on va simplement calculer l'ensemble des cases une par une, sans optimisation. Comblez ce manque !

        Enfin, voyons la formule de calcul des N premières lignes :

        void lignes(int nb, std::vector<ligne_type> & lignes)
            {
                std::vector<ligne_type> Resultat;
                Resultat.push_back(ligne_type(1, 1));
        

        D'abord, on sait que la première ligne contient uniquement un 1.

        while (--nb > 0) { // Tant qu'il reste des lignes a faire
        

        Ensuite, pour chaque ligne qu'il faut ajouter (le --n décrémente n avant de retourner sa valeur), on va calculer la ligne.

        // On initialise la nouvelle ligne a un chiffre de plus que
                    // celui de la ligne precedente
                    Resultat.push_back(ligne_type(Resultat.back().size() + 1, 0));
                    ligne_type & ligne = Resultat.back();
                    // On recuprer l'ancienne ligne
                    ligne_type & last = Resultat[Resultat.size() - 2];
        

        On va d'abord préparer le calcul : la nouvelle ligne fait un caractère de plus que la précédente, et on en profite aussi pour récupérer le ligne précédente dans last.
        On utilise des références pour éviter d'avoir à recopier l'ensemble du code de récupération des valeurs, tout en évitant aussi la recopie d'une lourde ligne.

        Ensuite, on va remplir la ligne :
        // Et on remplit la ligne
                    for (size_t i = 0 ; i < ligne.size() ; ++i) {
                        // Pour chacun des caracteres de la nouvelle chaine
                        if (i == 0 || i == last.size()) {
                            // Premiers et derniers sont a un
                            ligne[i] = 1;
                        } else {
                            // Sinon, c'est la somme entre le caractere du dessus
                            // et celui du dessus un cran a gauche.
                            ligne[i] = last[i - 1] + last[i];
                        }
                    }
        


        Si on est en train de chercher le premier ou le dernier caractère de la ligne, on est dans un cas spécial : ils sont toujours égaux à 1.

        Sinon, on va simplement appliquer la formule : caractère au-dessus à gauche + caractère au-dessus.

        Enfin, on va renvoyer le résultat de façon spéciale (expliquée dans les commentaires) :
        }
                std::swap(Resultat, lignes); // Et on renvoie le resultat.
                // Swap a ceci d'extraordinaire qu'il permet de renvoyer sans
                // avoir a faire une lourde copie de tout le tableau.
            }
        


        Améliorations possibles



        Vous pourriez par exemple permettre l'affichage sous forme de triangle isocèle même si le nombre de lignes est supérieur à 5.
        Ou bien rendre récursif l'algorithme de calcul de la valeur d'une case.
        Ou encore améliorer le calcul d'une ligne donnée, soit en usant de la symétie, soit en modifiant complètement la façon d'y arriver.

        Bref, il vous reste beaucoup de choses à améliorer !

        « Mais moi je suis impatient, je veux le code ! »



        D'accord, d'accord, voici le code.
        #include <algorithm>
        #include <cstdlib> // Pour EXIT_SUCCESS
        #include <iostream>
        #include <numeric>
        #include <sstream>
        #include <string>
        #include <vector>
        
        #include "Pascal.hpp"
        
        bool menu(); // Retourne "est-ce que l'utilisateur veut continuer ?"
        
        void afficherLignes();
        void afficherLigne();
        void afficherCase();
        void afficherKParmiN();
        void binomeNewton();
        
        size_t calculerLongueur(int valeur);
        
        int main(int, char**)
        {
            std::cout << "===== Programme de calcul du triangle de Pascal ====="
                      << std::endl;
            while (menu()); // Tant que l'utilisateur veut continuer, on continue.
            return EXIT_SUCCESS;
        }
        
        bool menu()
        {
            if (!std::cin.good()) return false; // Il y a un souci d'entree-sortie.
        
            std::cout <<
                "Que vous souhaitez-vous ?" << std::endl <<
                "- Afficher les [p]remieres lignes du triangle" << std::endl <<
                "- Afficher une [l]igne" << std::endl <<
                "- Afficher une [c]ase" << std::endl <<
                "- Afficher [K] parmi N" << std::endl <<
                "- Afficher le binome de [N]ewton" << std::endl <<
                "- [Q]uitter" << std::endl <<
                std::endl <<
                "Que souhaitez-vous ? (p/l/c/k/n/q) "
                ;
            char choix;
            std::cin >> choix;
            if (!std::cin.good()) return false; // Il y a un souci d'entree-sortie.
        
        
            choix = std::tolower(choix); // Pour autoriser a entrer des majuscules
            switch (choix) {
                case 'p':
                    afficherLignes();
                    break;
                case 'l':
                    afficherLigne();
                    break;
                case 'c':
                    afficherCase();
                    break;
                case 'k':
                    afficherKParmiN();
                    break;
                case 'n':
                    binomeNewton();
                    break;
                case 'q':
                    return false; // On arrete
                default:
                    std::cout << "Merci de faire un choix dans la liste."
                              << std::endl;
                    // On continue, parce que l'utilisateur doit bien choisir
                    // quelque chose de coherent.
            }
            return true; // L'utilisateur n'a pas demande a arreter.
        }
        
        void afficherLignes()
        {
            std::cout << "Combien de lignes souhaitez-vous afficher ? ";
            size_t nb;
            std::cin >> nb;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::vector<Pascal::ligne_type> lignes;
            Pascal::lignes(nb, lignes);
        
            // On va calculer les longueurs pour chaque colonne, pour pouvoir
            // egaliser.
            std::vector<size_t> longueurs;
            for (size_t colonne = 0 ; colonne < nb ; ++colonne) {
                size_t longueurMax = 0;
                for (size_t ligne = colonne ; ligne < nb ; ++ligne) {
                    size_t longueur = calculerLongueur(lignes[ligne][colonne]);
                    if (longueur > longueurMax) longueurMax = longueur;
                }
                longueurs.push_back(longueurMax);
            }
        
            // On calcule les longueurs de lignes
            std::vector<size_t> longueursLignes;
            for (size_t ligne = 0 ; ligne < nb ; ++ligne) {
                longueursLignes.push_back(std::accumulate(
                    std::begin(longueurs),
                    std::begin(longueurs) + ligne + 1,
                    lignes[ligne].size() - 2 // Le nombre d'espaces entre nombres.
                    ));
            }
        
            // On recherche la longueur la plus grande, qui est forcement celle de
            // la derniere
            const size_t longueurMax = longueursLignes.back();
        
            // Et maintenant on affiche.
            for (size_t ligne = 0 ; ligne < nb ; ++ligne) {
                // D'abord, on "isocelise", seulement si on veut 5 lignes ou moins
                // car sinon l'affichage est tres mauvais, a cause des espaces
                // rajoutes entre les nombres :
                if (nb <= 5) {
                    const size_t longueurLigne = longueursLignes[ligne];
                    std::cout <<
                        std::string((longueurMax - longueurLigne) / 2, ' ')
                        ;
                }
                // Et on affiche la ligne
                for (size_t colonne = 0 ; colonne <= ligne ; ++colonne) {
                    // On recupere l'entier dans une string
                    std::ostringstream oss;
                    oss << lignes[ligne][colonne];
                    std::string valeur = oss.str();
                    // On egalise la longueur
                    valeur.resize(longueurs[colonne], ' ');
                    // Et on affiche.
                    std::cout << valeur << " ";
                }
                std::cout << std::endl;
            }
        }
        
        void afficherLigne()
        {
            std::cout <<
                "Quelle ligne souhaitez-vous afficher ? ";
            int num;
            std::cin >> num;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            Pascal::ligne_type ligne;
            Pascal::ligne(num - 1, ligne);
            // -1 parce qu'il considere la premiere ligne comme etant 0
        
            for (Pascal::case_type c : ligne) {
                std::cout << c << " ";
            }
            std::cout << std::endl;
        }
        
        void afficherCase()
        {
            std::cout <<
                "De quelle ligne souhaitez-vous afficher la case ? ";
            int ligne;
            std::cin >> ligne;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::cout <<
                "Quelle case souhaitez-vous afficher ? ";
            int num;
            std::cin >> num;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::cout << Pascal::uneCase(num - 1, ligne - 1) << std::endl;
        }
        
        void afficherKParmiN()
        {
            int k, n;
        
            std::cout << "K ? ";
            std::cin >> k;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::cout << "N ? ";
            std::cin >> n;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::cout << Pascal::uneCase(k, n) << std::endl;
        }
        
        void binomeNewton()
        {
            size_t n;
            std::cout << "N ? ";
            std::cin >> n;
            if (!std::cin.good()) return; // Il y a un souci d'entree-sortie.
        
            std::cout << "(A + B) ^ " << n << " =";
        
            Pascal::ligne_type ligne;
            Pascal::ligne(n, ligne);
        
            for (size_t k = 0 ; k <= n ; ++k) {
                const size_t coeffA = n - k;
                const size_t coeffB = k;
                if (ligne[k] != 1) std::cout << " " << ligne[k];
                if (coeffA != 0) {
                    std::cout << " A";
                    if (coeffA != 1) std::cout << " ^ " << coeffA;
                }
                if (coeffB != 0) {
                    std::cout << " B";
                    if (coeffB != 1) std::cout << " ^ " << coeffB;
                }
                if (k != n) std::cout << " +";
            }
            std::cout << std::endl;
        }
        
        size_t calculerLongueur(int v)
        {
            size_t longueur = 1;
            while (v >= 10) {
                v /= 10;
                ++longueur;
            }
            return longueur;
        }
        

        #ifndef PASCAL_HPP_INCLUDED__
        #define PASCAL_HPP_INCLUDED__ 1
        
        #include <string>
        #include <vector>
        
        namespace Pascal
        {
            typedef unsigned long long case_type;
            typedef std::vector<case_type> ligne_type;
        
            void lignes(int nb, std::vector<ligne_type> & lignes);
        
            void ligne(int num, ligne_type & ligne);
        
            case_type uneCase(int num, int ligne);
        }
        
        #endif
        

        #include "Pascal.hpp"
        
        #include <vector>
        #include <string>
        
        namespace Pascal
        {
            void lignes(int nb, std::vector<ligne_type> & lignes)
            {
                std::vector<ligne_type> Resultat;
                Resultat.push_back(ligne_type(1, 1));
                while (--nb > 0) { // Tant qu'il reste des lignes a faire
                    // On initialise la nouvelle ligne a un chiffre de plus que
                    // celui de la ligne precedente
                    Resultat.push_back(ligne_type(Resultat.back().size() + 1, 0));
                    ligne_type & ligne = Resultat.back();
                    // On recuprer l'ancienne ligne
                    ligne_type & last = Resultat[Resultat.size() - 2];
                    // Et on remplit la ligne
                    for (size_t i = 0 ; i < ligne.size() ; ++i) {
                        // Pour chacun des caracteres de la nouvelle chaine
                        if (i == 0 || i == last.size()) {
                            // Premiers et derniers sont a un
                            ligne[i] = 1;
                        } else {
                            // Sinon, c'est la somme entre le caractere du dessus
                            // et celui du dessus un cran a gauche.
                            ligne[i] = last[i - 1] + last[i];
                        }
                    }
                }
                std::swap(Resultat, lignes); // Et on renvoie le resultat.
                // Swap a ceci d'extraordinaire qu'il permet de renvoyer sans
                // avoir a faire une lourde copie de tout le tableau.
            }
        
            void ligne(int num, ligne_type & ligne)
            {
                ligne_type Resultat;
                Resultat.push_back(1);
                for (int i = 1 ; i <= num ; ++i) {
                    // Tant qu'il reste des elements a ajouter a la ligne
                    Resultat.push_back(uneCase(i, num));
                }
                std::swap(Resultat, ligne); // Maintenant vous savez tout.
            }
        
            case_type uneCase(int num, int ligne)
            {
                // Ces noms collent mieux aux formules Wikipedia
                const int n = ligne;
                const int k = num;
                // On peut arreter ici.
                if (k == 0) {
                    return 1;
                }
                // Et la formule wikipedia
                // la, comme on a un objet peu lourd, on peut utiliser return
                return uneCase(num - 1, ligne - 1) * n / k;
            }
        }
        


        J'espère que vous aurez pris plaisir à lire ma correction !
        • Partager sur Facebook
        • Partager sur Twitter
          4 juillet 2011 à 20:39:09

          Equinoxe : Ajouté ma solution au triangle de Pascal.
          Equinoxe : Proposé un niveau de difficulté de 3 à ce même exercice, en attendant de voir si quelqu'un pense autrement.
          • Partager sur Facebook
          • Partager sur Twitter
            4 juillet 2011 à 23:43:13

            Bonne initiative de faire revivre ces exercices.

            Avec un peu de recul, je dirais que l'exercice sur l'opérateur virgule est tout sauf un bon exercice pour débutants.

            Au niveau des catégories, ce que tu appelles "mathématiques" serait plutôt à classer dans "Classes" puisque ce sont des exercices fait pour faire créer des classes à sémantique d'entité aux lecteurs.
            • Partager sur Facebook
            • Partager sur Twitter
            Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
              5 juillet 2011 à 0:00:12

              Citation : Nanoc

              Avec un peu de recul, je dirais que l'exercice sur l'opérateur virgule est tout sauf un bon exercice pour débutants.


              Pourquoi cela? Pourtant je l'ai retrouvé plusieurs fois sur le web, preuve qu'il est assez apprecié :D
              • Partager sur Facebook
              • Partager sur Twitter
                5 juillet 2011 à 0:09:31

                Après, avec le c++11, il n'y a plus besoin de ça, puisque les initalizer-lists existent. :)
                • Partager sur Facebook
                • Partager sur Twitter
                  5 juillet 2011 à 0:19:38

                  C'est surtout un exo qui exploite un détail obscur du langage pour lui faire faire des choses non prévues au départ.
                  Pas vraiment un exo pour un débutant. On joue avec la syntaxe au lieu de jouer avec les principes de la conception et du développement. Principes essentiels qui ne sont pas encore assimilés.
                  • Partager sur Facebook
                  • Partager sur Twitter
                  C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                    5 juillet 2011 à 1:54:40

                    Beaucoup trop long ce triangle de pascal, le triangle de pascal, c'est 30 lignes, pas plus :colere2:
                    Allez avec les options, 60 lignes. Je regarde ton code pour voir comment tu fais.

                    Edit: J'ai regardé ton code, tu t'embêtes beaucoup trop.

                    Je suis plus à mon aise en C, donc ces codes seront en C, mais si ça vous dérange, je les traduit en C++.

                    Affichage du triangle de pascale :
                    void PascalsTriangle(int x,)
                    {
                       int p = 1, j, i;
                       for(i=0;i<x;i++,puts(""),p=1)
                          for(j=0,printf("%d ", p);j<i;j++)
                             printf("%d ", p = p*(i-j)/(j+1));
                    }
                    


                    En plus lisible :
                    void trianglePascal(int x)
                    {
                       int p = 1, j, i;
                       for(i=0;i<x;++i)
                       {
                          printf("1 ");
                          for(j=0;j<i;j++)
                             printf("%d ", p = p*(i-j)/(j+1) );
                          puts("");
                          p = 1;
                       }   
                    }
                    

                    Il y a certainement plus court en récursif (j'imagine que en 1 ligne, ça doit être faisable, n'est-ce pas Pouet_forever ?!? :-) ).

                    Pour récupérer la case C de la ligne L : (Attention, on commence à la case 0 et à la ligne 0 ).
                    int caseTrianglePascal(int L, int C) // Il faut lui trouver un nom potable
                    {
                       int i = 0, p=1;
                       for(;i<C;++i)
                          p = p*(L-i)/(i+1);
                       return p;
                    }
                    

                    J'ai juste chercher un lien entre les cases, avec une feuille et un crayon, je suis tombé sur la formule :

                    <math>\(Precedent * (numLigne - numCase)/(numCase+1)\)</math>

                    Qui permet de calculer la case suivante à partir de la case courante. Au début de ligne, la case courante est toujours 1.

                    J'avais fais un code ce matin (oui car ces derniers temps, je fais justement les exercices que je trouve ) et j'avais fais un code, seulement il est en C et il est sale (par-contre, il marche :p ). Je le posterais demain comme une correction.

                    PS: Bonus obfuscation ^^" :
                    #include <stdio.h>
                    
                    int main(int __, int _s)
                    {
                       int L;
                       static int _ = 0;
                       return (__>0)?scanf("%d", &L)&&main(-L,1):(__<0)?(printf("%d ", _s),(_++ < -__)?(
                             main(__, _s*(-__-(_-0x01))/(_))):((_=!(_|7)),puts("")&&main(__+1, 1))):putchar('1'); 
                    }
                    

                    Il y a un Warning avec g++ mais bon, c'est pas très grave.
                    • Partager sur Facebook
                    • Partager sur Twitter
                    🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles  - ♡ Copying is an act of love.
                      5 juillet 2011 à 8:01:49

                      @ache: C'est bien de faire des programmes de 60 lignes (ou moins), mais ce que tu as fait est complètement illisible pour les débutants, voire même pour des personnes un peu plus expérimentées (moi par exemple :-° ).

                      Le ternaire permet de tout faire tenir en une ligne, certes (je ne connais pas les différences de perf avec les "tests classiques" par contre), mais cela reste imbuvable, pourtant je code depuis quelques années maintenant et j'en ai vu de nombreuses fois.

                      Le but de ces corrections n'est pas d'avoir un code le plus court possible, mais quelque chose de compréhensible pour les débutants.

                      Néanmoins cela reste une solution, et les Zéros verront une autre façon de penser et de faire ^^

                      @Equinoxe: Ca c'est de l'explication! :p
                      Il est vrai que c'est un peu long au niveau codage, mais ça reste clair.
                      Par contre tu aurais dû montrer tout de suite la structure Pascal, plutôt que de la mettre seulement à la fin, car certains risquent d'être perdus en route ^^
                      • Partager sur Facebook
                      • Partager sur Twitter
                        5 juillet 2011 à 8:29:44

                        Citation : TrackOut

                        Citation : Nanoc

                        Avec un peu de recul, je dirais que l'exercice sur l'opérateur virgule est tout sauf un bon exercice pour débutants.


                        Pourquoi cela? Pourtant je l'ai retrouvé plusieurs fois sur le web, preuve qu'il est assez apprecié :D



                        Parce qu'en implémentant un opérateur virgule de cette manière, tu triches un peu avec les règles implicites que se fixent les programmeurs. Tu essayes de faire faire quelque chose au langage qui n'est pas vraiment prévu. De plus, le code final n'a pas les bonnes priorités et n'est pas vraiment robuste.
                        Les gens qui débutent devraient se concentrer sur des exercices de conception et de mini-algorithmes plutôt que d'essayer de jouer avec les cas limites.
                        • Partager sur Facebook
                        • Partager sur Twitter
                        Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
                          5 juillet 2011 à 8:37:58

                          Sinon, pour proposer un exercice un peu différent :

                          Exercice de langage



                          Essayez d'écrire la plus longe phrase grammaticalement correcte et ayant du sens en anglais ne contenant que des mots-clés du C++ sans répétition.
                          :p

                          Voici quelques exemples :

                          long friend try this.
                          new friend try not.

                          Mais on peut faire beaucoup mieux !

                          Vous pouvez trouver une liste des mots-clés ici : http://en.cppreference.com/w/cpp/keywords
                          • Partager sur Facebook
                          • Partager sur Twitter
                          Co-auteur du cours de C++. ||| Posez vos questions sur le forum ||| Me contacter.
                            5 juillet 2011 à 10:17:19

                            du genre :
                            try throw and break virtual friend
                            
                            • Partager sur Facebook
                            • Partager sur Twitter
                              5 juillet 2011 à 11:01:17

                              @@ache : Sauf que tu ne respectes pas les contraintes de l'énoncé. Qui sont que les chiffres doivent être les uns sur les autres. Là, tu récupères les 14 premières lignes, et ton triangle aura une forme affreuse (14 est un chiffre au pif, à partir de 6 ça comencera). Par ailleurs, si tu pouvais mettre en secret la formule, ce serait très bien, j'ai fait exprès de ne pas mettre la formule idéale pour laisser des améliorations.

                              En fait, vous pouvez voir que le code pour le triangle de pascal en lui-même est très court, c'est toute la partie IU qui est lourde.

                              @nours95 : J'hésite à éditer, parce que c'est quand même le main. Du coup, je vais juste dire d'utiliser la molette de la souris, si on veut avoir directement la partie sur le triangle de pascal lui-même.

                              @Nanoc : (Je ne pense pas que ce soit un exercice à mettre sur la première page, si ? :D )

                              while friend float this double new union and delete volatile register

                              (Pas beaucoup de sens, mais ... Tant que l'ami flotte, ceci double la nouvelle union et supprime le registre volatile.)
                              On aurait pû faire encore "pire", je pense. :)
                              • Partager sur Facebook
                              • Partager sur Twitter
                                5 juillet 2011 à 11:04:00

                                if this volatile and short true is using (by :honte: )new friend, try long friend.

                                Avec un peu de triche. ^^

                                Si le nouvel ami utilise cette petite vérité fugace, essaye l'ami de longue date.
                                • Partager sur Facebook
                                • Partager sur Twitter
                                Bla bla bla
                                  5 juillet 2011 à 11:30:56

                                  if extern new friend is volatile, short, static, goto long true friend, try break this using explicit, (not virtual) protected mutable catch. else, continue.
                                  Ma phrase n'a pas beaucoup de sens :(
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    5 juillet 2011 à 12:13:13

                                    @@che : ton code du dessus est limite pour un programmeur qui s'y connait et celui du dessous est d'une imbuvabilité qui fait peur.
                                    @Equinoxe : ta correction est parfaite !

                                    sinon :
                                    if long friend try double private union, goto catch public operator for signed register, break this false union while this friend try explicit this new extern volatile union, try using new virtual union; else continue const union.
                                    

                                    edit: si on ne dois pas répéter, la phrase a du sens :
                                    if long friend try double private union, goto catch public operator for signed register.
                                    
                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      5 juillet 2011 à 13:19:02

                                      Citation : Nanoc

                                      Bonne initiative de faire revivre ces exercices.

                                      Avec un peu de recul, je dirais que l'exercice sur l'opérateur virgule est tout sauf un bon exercice pour débutants.

                                      Au niveau des catégories, ce que tu appelles "mathématiques" serait plutôt à classer dans "Classes" puisque ce sont des exercices fait pour faire créer des classes à sémantique d'entité aux lecteurs.



                                      Merci. En effet, je m'en vais reclasser ces exercices :)
                                      Pour l'exercice virgule, je dirais difficulté 4 au moins.
                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        5 juillet 2011 à 13:23:17

                                        germino : changement de nom de la partie "mathématique"-->"classes à sémantique de valeur"
                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          5 juillet 2011 à 13:24:12

                                          Au fait nanoc, c'est à sémantique de valeur, pas d'entité...
                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                            5 juillet 2011 à 13:39:15

                                            Exercice



                                            Nom : Simplifier les mots
                                            Sujet : Fichier, Table ASCII, Les Flux

                                            Introduction


                                            Vous connaissez tous ce problème : Vous voulez afficher école, mais la console affiche ùcole :o La première (et meilleure) solution est d'enlever cet accent. Donc école devient ecole.
                                            Nous allons mettre ça en pratique :

                                            L'exercice


                                            niveau 1


                                            Le programme va supprimer les accents dans un fichier texte.
                                            L'utilisateur rentre le chemin vers son fichier et le programme le modifie et le ré-écrit dans la console.
                                            Chemin du fichier : C:\Users\Documents\Text\textAModifier.txt
                                            Modification...
                                            Fini :
                                            Etoile a huit arretes n'est plus illumine dans ce ciel etincelant


                                            niveau 2


                                            Le programme ne se contentera pas de modifier ce texte, mais il va créer un autre fichier nommé "Nom_du_fichier_modifie.txt" et y écrira la version modifiée du fichier.
                                            Conseils?
                                            Un petit tour sur Google pour trouver les fonctions nécéssaire à la création d'un fichier, le renommer et ecrire dedans...


                                            niveau 3


                                            Le programme pourra faire le niveau 2 sur quelques plate-forme.
                                            ex : Windows, Mac et Linux.


                                            ----------------------------
                                            J'espère que cet exo vous plaira.
                                            Pour ma part, je part pour 3 semaines...
                                            Alors à Bientôt ;)
                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              5 juillet 2011 à 14:13:08

                                              Mauvais contournement de la problématique
                                              1- le problème est limité aux consoles ms-dos sous windows (i.e. pas le problème sous cygwin, ou dans les fichiers générés, ou dans la console intégrée de BC++B, ...)
                                              2- "chcp 1252" est la solution
                                              3- si vraiment on veut faire sauter les accents, il faut jouer avec les locales ; je ne sais plus si boost.locale va adresser cela, je dirais que oui a priori. (c'est le genre de sujet avec lequel j'évite de perdre du temps)
                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                              C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                                                5 juillet 2011 à 15:40:55

                                                Bonjour,
                                                Que l'on soit claire, je n'ai encore pas donner de solution ;) , le premier code est limite, je l'avoue, c'est pour cela que j'ai réécrie en plus lisible rien qu'en dessou. Le dernier code est une boutade, il est totalement imbuvable. C'est de l'obfuscation :p Ce genre de code est juste un jeu. Demain ou se soir, je posterais une correction.
                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                🍊 - Étudiant - Codeur en C | Zeste de Savoir apprenez avec une communauté | Articles  - ♡ Copying is an act of love.
                                                  6 juillet 2011 à 9:16:58

                                                  @germino, met ma correction pour le Tic-Tac-Toe stp :p (celle de la page 4).
                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                    6 juillet 2011 à 9:33:36

                                                    Citation : PITETRE

                                                    voila j'ai fini la correction de l'exo "Tic-Tac-Toe", avec les corrections. Voici ;) :

                                                    main.cpp :
                                                    <secret>

                                                    #include <iostream>
                                                    #include <cstdlib>
                                                    #include "grille.h"
                                                    
                                                    using namespace std;
                                                    
                                                    int main()
                                                    {
                                                        Grille g;
                                                        int choix = 0;
                                                        cout << "bienvenu dans ce Tix-Tac-Toe, voici la grille numérotée" << endl;
                                                        g.afficher();
                                                        g.init();
                                                        while(1)
                                                        {
                                                            cout << "c'est à '" << g.next() << "' de jouer" << endl;
                                                            do
                                                            {
                                                                choix = 0;
                                                                cout << "quel est votre choix '" << g.next() << "' ?" << endl;
                                                                cin >> choix;
                                                            }while(!g.playAt(choix));
                                                            #ifdef _WIN32
                                                                system("cls");
                                                            #else
                                                                system("clear");
                                                            #endif
                                                    
                                                            g.afficher();
                                                    
                                                            if(g.won() == 'e' || g.won() == 'x' || g.won() == 'o')
                                                                break;
                                                        }
                                                        if(g.won() == 'e')
                                                            cout << "egalité !!!" << endl;
                                                        else
                                                            cout << g.won() << " a gagné la partie" << endl;
                                                    
                                                        return 0;
                                                    }
                                                    

                                                    </secret>

                                                    grille.h :

                                                    <secret>

                                                    #ifndef GRILLE_H
                                                    #define GRILLE_H
                                                    
                                                    #include <iostream>
                                                    
                                                    class Grille
                                                    {
                                                        public:
                                                            Grille(char starting = 'o');// génère la grille remplit par défaut par les nombre 1, 2, 3, ..., 7, 8, 9
                                                            void afficher();// affiche la grille
                                                            void init();// initialise toute les cases de m_grille à ' '
                                                            bool playAt(int x, int y = -1);// soit les coordonnées de m_grille, soit un nombre entre 1 et 9
                                                            char won();// 0 si personne, 1 si o, 2 si x
                                                            char next();
                                                        private:
                                                            char m_grille[3][3];// la grille
                                                            char m_next;// le prochain "joueur" à jouer : 'o' ou 'x'
                                                    };
                                                    
                                                    #endif
                                                    

                                                    </secret>

                                                    grille.cpp :

                                                    <secret>

                                                    #include "grille.h"
                                                    
                                                    using namespace std;
                                                    
                                                    Grille::Grille(char starting) : m_next(starting)
                                                    {
                                                        for(int i = 0 ; i < 9 ; i++)
                                                            m_grille[i/3][i%3] = i+'1';
                                                    }
                                                    
                                                    void Grille::afficher()
                                                    {
                                                        cout << "     |     |" << endl;
                                                        cout << "  " << m_grille[2][0] << "  |  " << m_grille[2][1] << "  |  " << m_grille[2][2] << endl;
                                                        cout << "_____|_____|_____" << endl;
                                                        cout << "     |     |" << endl;
                                                        cout << "  " << m_grille[1][0] << "  |  " << m_grille[1][1] << "  |  " << m_grille[1][2] << endl;
                                                        cout << "_____|_____|_____" << endl;
                                                        cout << "     |     |" << endl;
                                                        cout << "  " << m_grille[0][0] << "  |  " << m_grille[0][1] << "  |  " << m_grille[0][2] << endl;
                                                        cout << "     |     |" << endl;
                                                    }
                                                    
                                                    void Grille::init()
                                                    {
                                                        for(int i = 0 ; i < 9 ; i++)
                                                            m_grille[i/3][i%3] = ' ';
                                                    }
                                                    
                                                    char Grille::next()
                                                    {
                                                        return m_next;
                                                    }
                                                    
                                                    bool Grille::playAt(int x, int y)
                                                    {
                                                        if(y < 0)
                                                        {
                                                            x--;
                                                            y = x/3;
                                                            x %= 3;
                                                        }
                                                    
                                                        if(m_grille[y][x] == 'o' || m_grille[y][x] == 'x' || x < 0 || x > 2 || y < 0 || y > 2)
                                                            return false;
                                                    
                                                        m_grille[y][x] = m_next;
                                                        m_next = (m_next == 'o')?'x':'o';
                                                        return true;
                                                    }
                                                    
                                                    char Grille::won()
                                                    {
                                                        if(((m_grille[0][0] == m_grille[1][1] && m_grille[1][1] == m_grille[2][2]) || (m_grille[0][2] == m_grille[1][1] && m_grille[1][1] == m_grille[2][0])) && (m_grille[1][1] == 'o' || m_grille[1][1] == 'x'))
                                                        {
                                                            return m_next;
                                                        }
                                                        else
                                                        {
                                                            for(int i = 0 ; i < 3 ; i++)
                                                            {
                                                                if(m_grille[i][0] == m_grille[i][1] && m_grille[i][1] == m_grille[i][2] && (m_grille[i][1] == 'o' || m_grille[i][1] == 'x'))
                                                                    return m_next;
                                                                else if(m_grille[0][i] == m_grille[1][i] && m_grille[1][i] == m_grille[2][i] && (m_grille[1][i] == 'o' || m_grille[1][i] == 'x'))
                                                                    return m_next;
                                                            }
                                                            bool b = true;
                                                            for(int i = 0 ; i < 9 ; i++)
                                                            {
                                                                if(m_grille[i/3][i%3] != 'o' && m_grille[i/3][i%3] != 'x')
                                                                    b = false;
                                                            }
                                                            if(b)
                                                                return 'e';
                                                        }
                                                        return ' ';
                                                    }
                                                    

                                                    </secret>



                                                    Relis le premier post stp :)
                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      6 juillet 2011 à 10:01:21

                                                      ok ca changé depuis la premiere fois ;) , je le ferai se soir
                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                        6 juillet 2011 à 10:06:21

                                                        a- Pourquoi ce mastodonte de iostream dans un .h (cherches iosfwd sur le site)
                                                        b- init() c'est mal, en OO on utilise des constructeurs
                                                        c- won() est compliquée : mélange SESE/SEME ; sémantique zarb' (conjugaison du verbe, valeurs retournées (les enums c'est plus clair))
                                                        d- grille fait trop de choses : la grille et le jeu => next n'est pas de la responsabilité de la grille
                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                        C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
                                                          6 juillet 2011 à 10:24:58

                                                          pouquoi pas ? le joueur lui dit juste ou joué, après elle choisit quelle pion mettre et ou, c'est privé ... init c'est pour initialiser à ' ' et donc ca marche puisque le constructeur initialise a 1, 2 ... 8, 9 . J'aurais pus faire un constructeur surcharger avec une variable qui change en cour, mais c'était plus simple comme ca ... si c'est les noms des fonction qui te gène je peut les changer, mais ca change rien une fois compilé ca reste des 0 et des 1 .
                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                            6 juillet 2011 à 10:36:43

                                                            b- Je ne vois pas l'intérêt de passer par un état inutilisable => merge init dans le ctr
                                                            c- won est très zarbi je t'assure.
                                                            • Partager sur Facebook
                                                            • Partager sur Twitter
                                                            C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.

                                                            [Exercices] Venez vous entraîner !

                                                            × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                                                            × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                                                            • Editeur
                                                            • Markdown