Partage

Morpion

Mon premier programme en C++

Sujet résolu
Le 6 février 2011 à 23:48:04

Bonjour les zéros !
Je débute en C++ (j'ai de (bonnes) bases en C) et comme premier programme j'ai choisi le morpion.
Il est primitif et je sais que je pourrais encore l'améliorer (ce que je compte faire d'ailleurs car il en a besoin) mais je voudrais d'abord avoir votre avis sur mon code et ma façon de programmer objet.
Je m'excuse à l'avance de ne pas avoir créé de prototypes et tout ce qu'il y a autour.

PS: Je ne suis pas sur d'avoir implanté correctement le constructeur mais il marche.


#include <iostream>

using namespace std;

class Game
{
    public:

    Game()
    {
        choix = 0;
        joueur = 1;
        fin = 0;
        coups = 0;
        for(int i = 0; i < 9; i++)
        {
            p[i] = i + 65;
        }
    }

    int menu()
    {
        int choix;

        cout << "MENU" << endl;
        cout << "1. Jouer une partie" << endl;
        cout << "2. Quitter le programme" << endl;
        cout << "Votre choix ? : ";
        cin >> choix;

        return choix;
    }

    void principal()
    {
        cout << "C'est au tour du " << joueur << "joueur" << endl;
        Game::check();
        if(joueur == 1)
            p[choix-1] = 'X';
        else
            p[choix-1] = 'O';
        coups++;
        Game::gagne();
        Game::afficher();
        joueur = 3 - joueur;

    }

    void check()
    {
        for(;choix < 0 || choix > 9 || p[choix-1] == 'X' || p[choix-1] == 'O';)
        {
            cout << "Soit le numero entre est incorrect soit la case est déja occuppée. Entrez un autre nombre (entre 1 et 9):";
            cin >> choix;
        }
    }

    void afficher()
    {
        cout << " ____ ____ ____" << endl;
        cout << "|    |    |    |" << endl;
        cout << "| " << p[0] << "  | " << p[1] << "  | " << p[2] << "  |" << endl;
        cout << "|____|____|____|" << endl;
        cout << "| " << p[3] << "  | " << p[4] << "  | " << p[5] << "  |" << endl;
        cout << "|____|____|____|" << endl;
        cout << "| " << p[6] << "  | " << p[7] << "  | " << p[8] << "  |" << endl;
        cout << "|    |    |    |" << endl;
        cout << " ____ ____ ____" << endl;
    }

    int gagne ()
    {
        if ((p[0] == p[1] && p[1] == p[2]) || (p[3] == p[4] && p[4] == p[5]) || (p[6] == p[7] && p[7] == p[8]) || (p[0] == p[4] && p[4] == p[8]) || (p[6] == p[4] && p[4] == p[2]) || (p[0] == p[3] && p[3] == p[6]) || (p[1] == p[4] && p[4] == p[7]) || p[2] == p[5] && p[5] == p[8])
        {
            cout << "Le joueur" << joueur << " a gagne" << endl;
            return 1;
        }
        else if(coups == 9)
        {
            cout << "Match nul" << endl;
            return 1;
        }

        return 0;
    }

    private:

    int choix;
    int joueur;
    int fin;
    int coups;
    char p[9];

};

int main()
{
    Game morpion;

    if(morpion.menu() == 1)
    {
        while(morpion.gagne() != 1)
        {
            morpion.principal();
        }
    }
    else
        return 0;

    return 0;
}
Publicité
Le 6 février 2011 à 23:48:04
Le 7 février 2011 à 0:07:33

Salut,

Pourquoi n'utilises-tu pas la compilation séparée ? Si tu définis une méthode dans le corps de ta classe cette méthode sera inline. Ici c'est le cas pour toutes tes méthodes et elles n'ont pas du tout le profil pour.

L'autre chose c'est qu'il est en général plus lisible de créer un fichier séparé contenant uniquement ta méthode main. Ça évite, à toi et à ceux qui liront ton code, de chercher où tu as bien pu la cacher ;)

Pourquoi appelles-tu toutes tes méthodes de classes comme si elles étaient statiques ? (ex Game::gagne() dans principal). De ce que je vois, ce n'est pas le cas.

Concernant ta façon de "programmer objet", c'est un peu dur à juger sur si peu de code mais n'hésite pas si tu as des questions plus précises sur le sujet.

Enfin, pour le constructeur, essaye d'utiliser au maximum la liste d'initialisation (ex Game() : choix(0), joueur(1))

++
Le 7 février 2011 à 3:03:13

À regarder ce code, on dirait que tu as fait du Java avant de faire du C++. Je ne trouve pas l'utilisation de la POO utile dans ce cas (enfin, peut-être, mais pas comme tu l'utilises).
Le 7 février 2011 à 14:34:31

Merci pour ces réponses élogieuses :p.

@BZ
Je sais que c'est beaucoup mieux d'utiliser une compilation séparée mais pour les petits projets je préfère d'abord tout écrire dans un seul fichier et d'organiser tout ca plus tard (quand j'ai déja qq lignes de code) ici j'ai eu la flemme de le faire (il était tard) mais je m'excuse si cela vous a gêné dans la lecture.

&Ice_keese
Je ne me suis pas encore sérieusement mis au java mais c'est vrai que je m'en suis vaguement inspiré,pour avoir une idée générale sur la façon d'utiliser les classes.
Donc, en fait, les classes c'est un truc utile à utiliser avec modération ? Et mon code en C++ devrait être identique(ou presque) à un morpion en C ?

Ne perdez pas votre temps à corrigez tout ce qui est #include et la mauvaise implémentation de la compilation séparée, c'est juste un exemple pour illustrer mes propos

le main.c
int main()
{
    int joueur = 1;
    int fin;
    int coups = 0;
    int choix;
    char plateau[9] = {'1', '2', '3', '4', '5', '6', '7', '8', '9'};

    printf("MENU\n\n");
    printf("1.Nouvelle partie\n\n");
    printf("2.Quitter\n\n");
    printf("Votre choix ?: ");
    scanf("%d", &choix);

    if(choix == 1)
    {
        afficher(plateau);
        fin = 1;

        do
        {
            printf("C'est au tour de joueur%d :", joueur);
            scanf("%d", &choix);
            check(plateau, &choix);
            if(joueur == 1)
                plateau[choix-1] = 'X';
            else
                plateau[choix-1] = 'O';

            coups++;
            gagne(plateau, joueur, &fin, coups);

            afficher(plateau);
            joueur = 3 - joueur;

        }while(fin != 0);
    }
    else if(choix == 2)
        return 0;

    return 0;
}


le morpion.h
void check(char p[9], int* choix);
void afficher(char p[9]);
int gagne (char p[9], int joueur, int* fin, int coups);


le morpion.c

void afficher(char p[9])
{
    printf(" ____ ____ ____\n");
    printf("|    |    |    |\n");
    printf("| %c  | %c  | %c  |\n", p[0], p[1], p[2]);
    printf("|____|____|____|\n");
    printf("|    |    |    |\n");
    printf("| %c  | %c  | %c  |\n", p[3], p[4], p[5]);
    printf("|____|____|____|\n");
    printf("|    |    |    |\n");
    printf("| %c  | %c  | %c  |\n", p[6], p[7], p[8]);
    printf("|____|____|____|\n");
}

int gagne (char p[9], int joueur, int* fin, int coups)
{
    if ((p[0] == p[1] && p[1] == p[2]) || (p[3] == p[4] && p[4] == p[5]) || (p[6] == p[7] && p[7] == p[8]) || (p[0] == p[4] && p[4] == p[8]) || (p[6] == p[4] && p[4] == p[2]) || p[0] == p[3] && p[3] == p[6] || p[1] == p[4] && p[4] == p[7] || p[2] == p[5] && p[5] == p[8])
    {
        printf("\n\nLe joueur%d a gagne\n\n", joueur);
        *fin = 0;
    }
    else if(coups == 9)
    {
        printf("\n\nMatch nul\n\n");
        *fin = 0;
    }

    return 0;
}

void check(char p[9], int* choix)
{
    for(;*choix < 0 || *choix > 9 || p[*choix-1] == 'X' || p[*choix-1] == 'O';)
    {
        printf("Soit le numero entre est incorrect soit la case est déja occuppée. Entrez un autre nombre (entre 1 et 9):");
        scanf("%d", choix);
    }
}
Le 7 février 2011 à 16:10:46

Pas à utiliser à modération, mais...

Une classe sert à représenter un concept, une idée, et un objet est une forme concrète de cette idée. Quand ta classe n'a aucun attributs, c'est probablement un signe que tu devrais en faire un namespace.

Il y a moyen de rentrer la POO là dedans. Tu pourrais faire une classe plateau, par exemple.
Le 7 février 2011 à 22:26:00

Bon voila, j'ai un peu remanié un code, j'ai surtout implémenter la compilation séparée et ca donne ca :

plateau.h
#ifndef DEF_Plateau
#define DEF_Plateau

#include <string>
#include <iostream>
#include "player.h"

class Plateau
{
    public:

    Plateau();
    void placement(int j, int choix);
    void check(int &choix);
    void afficher();
    int gagne();

    private:

    Player m_name;
    int coups;
    char p[9];

};

#endif


plateau.cpp
#include <iostream>
#include "plateau.h"

using namespace std;

Plateau::Plateau()
{
    for(int i = 0; i < 9; i++)
    {
        p[i] = i + 65;
    }
}

void Plateau::placement(int j, int choix)
{
    if(j == 1)
        p[choix-1] = 'X';
    else
        p[choix-1] = 'O';
    coups++;
}

void Plateau::check(int &choix)
{
    while(choix < 0 || choix > 9 || p[choix-1] == 'X' || p[choix-1] == 'O')
    {
        cout << "Soit le numero entre est incorrect soit la case est déja occuppée. Entrez un autre nombre (entre 1 et 9):";
        cin >> choix;
    }
}

void Plateau::afficher()
{
    cout << " ____ ____ ____" << endl;
    cout << "|    |    |    |" << endl;
    cout << "| " << p[0] << "  | " << p[1] << "  | " << p[2] << "  |" << endl;
    cout << "|____|____|____|" << endl;
    cout << "|    |    |    |" << endl;
    cout << "| " << p[3] << "  | " << p[4] << "  | " << p[5] << "  |" << endl;
    cout << "|____|____|____|" << endl;
    cout << "|    |    |    |" << endl;
    cout << "| " << p[6] << "  | " << p[7] << "  | " << p[8] << "  |" << endl;
    cout << "|____|____|____|" << endl;
}

int Plateau::gagne()
{
    if ((p[0] == p[1] && p[1] == p[2]) || (p[3] == p[4] && p[4] == p[5]) || (p[6] == p[7] && p[7] == p[8]) || (p[0] == p[4] && p[4] == p[8]) || (p[6] == p[4] && p[4] == p[2]) || (p[0] == p[3] && p[3] == p[6]) || (p[1] == p[4] && p[4] == p[7]) || (p[2] == p[5] && p[5] == p[8]))
    {
        return 1;
    }
    else if(coups == 9)
    {
        cout << "Match nul" << endl;
        return -1;
    }

    return 0;
}


player.h
#ifndef DEF_Player
#define DEF_Player

#include <string>

class Player
{
    public:

    Player();
    Player(int number, std::string name);
    void rename(std::string name);
    std::string getname() const;
    int getnumber() const;

    private:

    std::string m_name;
    int m_number;
};

#endif


player.cpp
#include <iostream>
#include "player.h"

using namespace std;

Player::Player()
{
    m_number = 0;
    m_name = "Noob";
}

Player::Player(int number, string name)
{
    m_number = number;
    m_name = name;
}

void Player::rename(string name)
{
    m_name = name;
}

string Player::getname() const
{
    return m_name;
}

int Player::getnumber() const
{
    return m_number;
}


main.cpp
#include <iostream>
#include <string>
#include "plateau.h"
#include "player.h"

void menu(int *choix);
using namespace std;

int main()
{
    int choix;
    int joue =1;
    string name;
    menu(&choix);

    if(choix == 1)
    {
        cout << "Veuillez entrer un nom pour le joueur 1" << endl;
        cin >> name;

        Player j1(1, name);

        cout << "Veuillez entrer un nom pour le joueur 2" << endl;
        cin >> name;

        Player j2(2, name);

        cout << "Les regles du jeu sont simples, le premier joueur qui alligne 3 de ses pions gagne la partie" << endl;

        Plateau morpion;

        while(morpion.gagne() != 1 && morpion.gagne() != -1)
        {
            cout << "A votre tour, joueur " << joue << ":" << endl;
            cin >> choix;
            morpion.placement(joue, choix);
            morpion.afficher();
            if(morpion.gagne() == 1 && joue == 1)
                cout << endl << j1.getname() << " a gagne" << endl;
            else if(morpion.gagne() == 1 && joue == 2)
                cout << endl << j2.getname() << " a gagne" << endl;
            joue = 3 - joue;
        }
    }
    else
        return 0;

    return 0;
}

void menu(int *choix)
{
    cout << "MENU" << endl;
    cout << "1. Jouer une partie" << endl;
    cout << "2. Quitter le programme" << endl;
    cout << "Votre choix ? : ";
    cin >> *choix;
}


Les critiques éventuelles sont bien entendues les bienvenues :)
Le 7 février 2011 à 23:13:46

Salut.

J'ai pas regardé dans le détail mais quelques suggestions quand même.

Pourquoi Plateau à un membre Player ? Ça parait assez étrange. En plus j'ai pas l'impression que tu l'utilises.

Dans ton main tu boucles jusqu'à ce que quelqu'un gagne ou que le plateau soit rempli, ce qui semble logique. Par contre à chaque tour tu vérifies s'il y a un gagnant. Tu devrais le faire un seule fois après la boucle, puisque que tant que tu es dans la boucle tu sais qu'il n'y a pas de gagnant.

Sinon plutôt que de demander à la fonction gagne si le plateau est rempli, je ferai plutôt une fonction "est_rempli" qui renvoie un bool (tout comme gagne). La condition de ta boucle serait (!morpion.gagne() && !morpion.est_rempli()), c'est un peu plus parlant.

Morpion

× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
  • Editeur
  • Markdown