[C++] DEMINEUR SOUS CONSOLE

cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 - 11 juil. 2012 à 11:23
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 - 12 juil. 2012 à 11:24
Cette discussion concerne un article du site. Pour la consulter dans son contexte d'origine, cliquez sur le lien ci-dessous.

https://codes-sources.commentcamarche.net/source/54437-c-demineur-sous-console

cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
12 juil. 2012 à 11:24
>> -Le struct cell : Je me doute que c'est un constructeur, et que ça initialise les cellules de la grille, mais je ne vois pas trop comment.
"struct Cell", ça équivaut à "class Cell + public". En gros je créer un type personnalisé qui contient deux variables. Le constructeur est là pour dire: par défaut les variables valent "ceci".
Lorsque je fais Cell tab[size][size], ça créer un tableau de "Cell". Chacune des cases appelent automatiquement le constructeur, lors de la création.

>> -le (std::numeric_limits<std::streamsize>::max(), '\n') que tu rajoutes après chaque cin.
Dans certains cas (par exemple lorsque tu rentres un texte à la place d'un chiffre) tu peux partir en boucle infinie. Pour éviter cela, il faut utiliser un "ignore" pour dire: "je veux ignorer certains caractères". Par défaut ce n'est pas un saut de ligne '\n' mais une fin de fichier "eof". J'ai donc spécifié que je voulais ignorer l'appui sur la touche entrée (puisqu'elle insère un \n). Le "std::numeric_limits<std::streamsize>::max()," sert à dire: donne moi la valeur maximale d'une valeur entière. Ici ça serait 2^32. Je veux éviter autant de \n que possible, je met donc la plus grande valeur qui existe. J'aurais aussi pu faire: ignore(10, '\n'). Je pense que ça aurait aussi fonctionné.

>> D'ailleurs, un cin.ignore() ne suffit-il pas, il faut aussi rajouter le cin.clear()?
Ce n'est pas parce que tu ignores certains caractères que ton flux ne peut pas être pourri. Lorsque tu lis une entrée et que celle-ci n'est pas correcte, il faut "nettoyer" le flux avant de le réutiliser.
C'est ce que fait le clear. Il remet les flags internes à leurs valeurs de base.

>> -Dans ton drawCell, ici : std::cout << static_cast<char>(positions[x][y].mineNumber + '0'), le cast est là pour passer d'un int à un char?
Je passe d'un entier vers son code ASCII. '0' équivaut à 32. Donc si j'ai 0 et que je veux écrire '0' il faut que j'ajoute 32 ou 0 pour que ça donne '0'. Si tu préfères, chaque caractères existant porte un numéro, et les chiffress sont numérotés à partir de 32.

Le cast est la pour forcer le type char. mineNumber étant de type "int", si je le passe dans un std::cout, il m'écrira 32 au lieu de '0'.
Exemple:
char c1 = 32;
int i1 = 32;
char c2 = '0';
int i2 = '0';

std::cout << c1 << " " << i1 << " " << c2 << " " << i2 << std::endl; // affichera 0 32 0 32

>> Je pense que le .mineNumber est une sorte de fonction sans paramètres (?).
Non, c'est une simple variable.

>> Mais le '0', je vois pas ce qu'il fait là ^^'
Cf plus haut. C'est une astuce pour obtenir la version textuelle du nombre.

>> for (unsigned int x = 0; x < GRID_SIZE; ++x)
>> std::cout << static_cast<char>('A' + x) << " ";
Génère l'affichage de l'alphabet.
'A' + 0 => 'A'
'A' + 1 => 'B'
etc...

>> std::cout << " * " << std::setfill(' ') << std::setw(2) << x + 1 << " ";
Pour ceci, voir le lien que j'ai mis. En gros setfill et setw sont des modificateurs de flux qui permettent de gérer l'affichage. Par exemple, tu peux faire un alignement à gauche ou à droite, forcer un nombre à tenir dans deux espaces, etc...

>> -Dans selectCell, que représente le 'a' ? (if (c < 'a' || c > GRID_SIZE + 'a' || i > GRID_SIZE))
Lorsque l'utilisateur rentre une coordonnée de type "a 7" je récupère le caractère et je vérifie qu'il est valide. Pour être valide le caractère doit être >= à 'a' (si c'est un caractère plus petit que 'a' il y a un problème, non :p ?) et doit être inférieur à la taille de la grille. Si la taille de ta grille vaut 26, alors le caractère doit être plus petit ou égale à 'z'.
GRID_SIZE + 'a', si GRID_SIZE vaut 0, alors => 'a'
GRID_SIZE + 'a', si GRID_SIZE vaut 1, alors => 'b'
...
GRID_SIZE + 'a', si GRID_SIZE vaut 26, alors => 'z'

>> Dernière petite chose : Les variables initialisées au tout début, ce sont des variables globales, qui fonctionnent dans toutes les fonctions?
Non, il ne faut jamais faire de variables globales. Ici, ce sont des constantes globales. Elles sont effectivement "visibles" partout dans le fichier.

>> -Dans expandSelection, tu appelles cette même fonction, pourquoi cela ne risque-t-il pas de provoquer une boule infinie? (Je m'explique, quand tu appelles pour la 1ere fois expandSelection, les
>> instructions s'effectuent, et au moment où celles-ci appellent expandSelection, la fonction devrait repartir au début et tout relire ... et ceci à l'infini, non?)
C'est une fonction récursive. Rappeler une fonction au sein d'elle même ne te fait pas revenir au début de celle-ci. La fonction "se gèle" jusqu'à ce que tu ais fini l'exécution de la fonction appelée. Qu'elle est le même nom n'est pas gênant, c'est tout de même considéré comme une autre fonction indépendante.
C'est une notion un peu difficile pour ton niveau actuel. C'est pour ça que je t'ai mis un commentaire expliquant que l'on peut retirer les appels récursifs pour simplifier le code (bien évidemment on perd la fonctionnalité qui découvre toutes les cases adjacentes qui sont de type 0).

Je te met un exemple de fonction récursive simple:
int facto(int nb)
{
if (nb == 0)
return 1;

return facto(nb - 1) * nb;
}

Si je fais facto(3), en gros ça donnerait quelque chose comme ceci:

int facto(3)
{
if (3 == 0)
return 1;

return facto(3 - 1) * 3;
// appel de facto(2)
int facto(2)
{
if (2 == 0)
return 1;
return facto(2 - 1) * 2;
// appel de facto(1)
int facto(1)
{
if (1 == 0)
return 1;
return facto(1 - 1) * 1;
// appel de facto(0)
int facto(0)
{
if (0 == 0)
return 1; // fin de facto(0), pas d'autre appel, alors on remonte juste avant le "gel" de facto(1)
}
}
}

Après le return 1, tu te retrouves alors avec facto(1 -1) * 1 qui est 1 * 1. Tu remontes alors avant le "gel" de facto(2).
Tu te retrouves alors avec "return facto(2 - 1) * 2;" qui devient "return 1 * 2;". Tu remontes alors avant le "gel" de facto(3).
Tu te retrouves alors avec "return facto(3 - 1) * 3;" qui devient "return 2 * 3;". Tu renvois alors 6.

Je t'invite à te renseigner sur ce qu'est la récursivité.
zenoobrider Messages postés 4 Date d'inscription mercredi 25 novembre 2009 Statut Membre Dernière intervention 11 juillet 2012
11 juil. 2012 à 20:16
Merci beaucoup ! :)

Seulement, il y a quelques parties de codes que je ne comprends pas trop :

-Le struct cell : Je me doute que c'est un constructeur, et que ça initialise les cellules de la grille, mais je ne vois pas trop comment.

-le (std::numeric_limits<std::streamsize>::max(), '\n') que tu rajoutes après chaque cin. D'ailleurs, un cin.ignore() ne suffit-il pas, il faut aussi rajouter le cin.clear()?

-Dans ton drawCell, ici : std::cout << static_cast<char>(positions[x][y].mineNumber + '0'), le cast est là pour passer d'un int à un char? Je pense que le .mineNumber est une sorte de fonction sans paramètres (?). Mais le '0', je vois pas ce qu'il fait là ^^'

-Tout cette partie du drawGrid :
for (unsigned int x = 0; x < GRID_SIZE; ++x)
std::cout << static_cast<char>('A' + x) << " ";
std::cout << " *" << std::endl;
for (unsigned int x = 0; x < GRID_SIZE; ++x)
{
std::cout << " * " << std::setfill(' ') << std::setw(2) << x + 1 << " ";
for (unsigned int y = 0; y < GRID_SIZE; ++y)
Je n'ai rien, mais alors rien compris x)

-Dans selectCell, que représente le 'a' ? (if (c < 'a' || c > GRID_SIZE + 'a' || i > GRID_SIZE))

-Dans expandSelection, tu appelles cette même fonction, pourquoi cela ne risque-t-il pas de provoquer une boule infinie? (Je m'explique, quand tu appelles pour la 1ere fois expandSelection, les instructions s'effectuent, et au moment où celles-ci appellent expandSelection, la fonction devrait repartir au début et tout relire ... et ceci à l'infini, non?)

Sinon, tu as oublié de trier le fichier scores.txt selon le score, mais je m'en occupe ^^

Dernière petite chose : Les variables initialisées au tout début, ce sont des variables globales, qui fonctionnent dans toutes les fonctions?

En tout cas, merci beaucoup :)
cs_nikau Messages postés 8 Date d'inscription dimanche 25 novembre 2007 Statut Membre Dernière intervention 30 juillet 2012
11 juil. 2012 à 19:29
Évidement que c'est différent !! Je n'ai jamais dis le contraire!!
Mais dans le cas de son programme, avec des class, il n'aurait pas besoin de variables globale pour faire des plus petite fonction comme tu le lui a conseillé.
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
11 juil. 2012 à 19:28
Une petite coquille s'est glissée dans mon code. Il faut remplacer cette ligne:
if (x > 0 && y > 0 && !positions[x - 1][y - 1].visible == 0)

Par celle-ci:
if (x > 0 && y > 0 && !positions[x - 1][y - 1].visible)

(Dans la fonction "expandSelection")
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
11 juil. 2012 à 19:24
@nikau: Pas tout à fait d'accord avec toi. Ce n'est pas parce que tu as de l'objet que tu évites les variables globales et inversement. Ce sont deux notions complètement différentes...

@zenoobrider:
Je te propose une version corrigé de ton travail. Voilà ce que j'ai fait:
- Allégement du code (tellement de trucs inutiles ou un peu lourd :p)
- Correction des bugs
- Vraie dimension variable (en changeant les constantes taille de la grille et de nombre de bombe, ça fonctionne. (Il est aisément possible de l'ajouter en option).
- Lorsqu'un utilisateur tombe sur une case vide (0), alors toutes les cases annexes sont rendues visibles (comme le vrai démineur).
- Pour gérer les alignements, je t'invite à lire cette source: http://www.cppfrance.com/codes/ALIGNER-TEXTE-CONSOLE_50571.aspx

J'ai esssayé de faire en sorte que le code ne soit pas trop difficile à comprendre. J'ai posé un commentaire sur les notions/astuces les plus difficiles.
Il reste surement un peu de travail à faire pour gérer l'affichage correctement ou rajouter des options. Je te laisse le faire :)

#include
#include <string>
#include <ctime>
#include <cstdlib>
#include <fstream>
#include <sstream>
#include
#include <limits>

static const unsigned int GRID_SIZE = 10;
static const unsigned int NB_BOMBS = 10;

struct Cell
{
Cell()
: mineNumber(0), visible(false)
{
}

unsigned char mineNumber;
bool visible;
};

void showHeader()
{
std::cout <<
"\t\t\t *************************\n"
"\t\t\t *\t Demineur *\n"
"\t\t\t *\t By *\n"
"\t\t\t *\t Zenoo *\n"
"\t\t\t *************************\n" << std::endl;
}

std::string getPseudo()
{
std::cout << std::endl <<
" ****************************************************************\n"
" * *\n"
" * *\n"
" * *\n"
" * *\n"
" * *\n"
" * *\n"
" * ** ** ** ** ** ** ** *\n"
" ** ** ** ** Entrez votre pseudo ** ** ** ***\n"
" * ** ** ** ** ** ** ** ** *\n"
" * *\n"
" * *\n"
" * *\n"
" * *\n"
" * *\n"
" * *\n"
" ****************************************************************\n" << std::endl;
std::string nomJoueur;
std::cin >> nomJoueur;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

return nomJoueur;
}

void menu()
{
std::cout <<
" ****************************************************************\n"
" * *\n"
" * ** ** ** ** ** ** ** *\n"
" ** ** ** ** ** ** 1. Nouvelle partie ** ** ** ** ***\n"
" * ** ** ** ** ** ** ** ** *\n"
" * *\n"
" * *\n"
" * ** ** ** ** ** ** ** *\n"
" ** ** ** ** ** ** 2. Regles du jeu** ** ** ** ** ***\n"
" * ** ** ** ** ** ** ** ** *\n"
" * *\n"
" * *\n"
" * ** ** ** ** ** ** ** *\n"
" ** ** ** ** ** ** 3. Scores* ** ** ** ** ** ** ***\n"
" * ** ** ** ** ** ** ** ** *\n"
" * *\n"
" ****************************************************************\n" << std::endl;
}

void saveScore(const std::string& playerName, unsigned int score)
{
const std::string filename = "scores.txt";
std::ofstream file(filename.c_str(), std::ios::app);

if (!file)
{
std::cout << "ERREUR: Impossible d'ouvrir le fichier: " << filename << std::endl;
return;
}

file << playerName << " " << score << std::endl;
}

void drawCell(Cell positions[GRID_SIZE][GRID_SIZE], int x, int y)
{
if (!positions[x][y].visible)
std::cout << "#";
else
{
if (positions[x][y].mineNumber == 9)
std::cout << '°';
else
std::cout << static_cast<char>(positions[x][y].mineNumber + '0');
}
}

void drawGrid(Cell positions[GRID_SIZE][GRID_SIZE])
{
std::cout << " ****************************************************************" << std::endl
<< " * *" << std::endl
<< " * ";
for (unsigned int x = 0; x < GRID_SIZE; ++x)
std::cout << static_cast<char>('A' + x) << " ";
std::cout << " *" << std::endl;
for (unsigned int x = 0; x < GRID_SIZE; ++x)
{
std::cout << " * " << std::setfill(' ') << std::setw(2) << x + 1 << " ";
for (unsigned int y = 0; y < GRID_SIZE; ++y)
{
drawCell(positions, x, y);
std::cout << " ";
}
std::cout << " *" << std::endl;
}
std::cout << " * *" << std::endl
<< " * *" << std::endl
<< " * *" << std::endl
<< " ****************************************************************" << std::endl;
}

void putBomb(Cell positions[GRID_SIZE][GRID_SIZE], unsigned int x, unsigned int y)
{
if (x > 0 && y > 0 && positions[x - 1][y - 1].mineNumber != 9)
++positions[x - 1][y - 1].mineNumber;
if (x > 0 && positions[x - 1][y].mineNumber != 9)
++positions[x - 1][y].mineNumber;
if (x > 0 && y < GRID_SIZE - 1 && positions[x - 1][y + 1].mineNumber != 9)
++positions[x - 1][y + 1].mineNumber;
if (y > 0 && positions[x][y - 1].mineNumber != 9)
++positions[x][y - 1].mineNumber;
positions[x][y].mineNumber = 9;
if (y < GRID_SIZE - 1 && positions[x][y + 1].mineNumber != 9)
++positions[x][y + 1].mineNumber;
if (x < GRID_SIZE - 1 && y > 0 && positions[x + 1][y - 1].mineNumber != 9)
++positions[x + 1][y - 1].mineNumber;
if (x < GRID_SIZE - 1 && positions[x + 1][y].mineNumber != 9)
++positions[x + 1][y].mineNumber;
if (x < GRID_SIZE - 1 && y < GRID_SIZE - 1 && positions[x + 1][y + 1].mineNumber != 9)
++positions[x + 1][y + 1].mineNumber;
}

void generateBomb(Cell positions[GRID_SIZE][GRID_SIZE])
{
int nbBombs = NB_BOMBS;
while (nbBombs > 0)
{
unsigned int x = rand() % GRID_SIZE;
unsigned int y = rand() % GRID_SIZE;
if (positions[x][y].mineNumber != 9)
{
putBomb(positions, x, y);
--nbBombs;
}
}
}

void selectCell(unsigned char& c, unsigned int& i)
{
bool finished = false;

while (!finished)
{
std::cout << "Jouez. (de la forme A 1, a 1, d 10 ou D 10)" << std::endl;
std::cin >> c >> i;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
--i; // Conversion numéro visuel vers numéro réel.
c |= 32; // Met en minuscule.

if (c < 'a' || c > GRID_SIZE + 'a' || i > GRID_SIZE)
std::cout << "Erreur de saisie" << std::endl;
else
finished = true;
}
}

unsigned int checkVisibleCell(Cell positions[GRID_SIZE][GRID_SIZE])
{
unsigned int nb = 0;
for (unsigned int x = 0; x < GRID_SIZE; ++x)
for (unsigned int y = 0; y < GRID_SIZE; ++y)
if (positions[x][y].visible)
++nb;

return nb;
}

void expandSelection(Cell positions[GRID_SIZE][GRID_SIZE], unsigned int x, unsigned int y)
{
positions[x][y].visible = true;

// Cette partie là peut être désactivée. Elle ne sert qu'à afficher tous les 0 qui sont
// proches de celui qu'on vient de trouver.
if (positions[x][y].mineNumber > 0)
return;

if (x > 0 && y > 0 && !positions[x - 1][y - 1].visible == 0)
expandSelection(positions, x - 1, y - 1);
if (x > 0 && !positions[x - 1][y].visible)
expandSelection(positions, x - 1, y);
if (x > 0 && y < GRID_SIZE - 1 && !positions[x - 1][y + 1].visible)
expandSelection(positions, x - 1, y + 1);
if (y > 0 && !positions[x][y - 1].visible )
expandSelection(positions, x, y - 1);
if (y < GRID_SIZE - 1 && !positions[x][y + 1].visible)
expandSelection(positions, x, y + 1);
if (x < GRID_SIZE - 1 && y > 0 && !positions[x + 1][y - 1].visible)
expandSelection(positions, x + 1, y - 1);
if (x < GRID_SIZE - 1 && !positions[x + 1][y].visible)
expandSelection(positions, x + 1, y);
if (x < GRID_SIZE - 1 && y < GRID_SIZE - 1 && !positions[x + 1][y + 1].visible)
expandSelection(positions, x + 1, y + 1);
}

void play(const std::string& playerName, Cell positions[GRID_SIZE][GRID_SIZE])
{
bool finished = false;
bool win = false;
unsigned int score = 0;

while (!finished)
{
drawGrid(positions);
unsigned char c;
unsigned int x;
selectCell(c, x);
unsigned int y = c - 'a';

std::cout << x << " " << y << std::endl;
expandSelection(positions, x, y);
score = checkVisibleCell(positions);
if (positions[x][y].mineNumber == 9)
{
win = false;
finished = true;
}
else if (score >= GRID_SIZE * GRID_SIZE - NB_BOMBS)
{
win = true;
finished = true;
}
}

drawGrid(positions);
std::cout << (win ? " Bravo" : "Perdu") << " ! Vous avez gagné " << score * 1000 << " points !";
saveScore(playerName, score * 1000);
}

void nouveauJeu()
{
Cell positions[GRID_SIZE][GRID_SIZE];
std::string nomJoueur = getPseudo();
showHeader();
generateBomb(positions);
play(nomJoueur, positions);
}

void rules()
{
showHeader();
std::cout << " ****************************************************************" << std::endl
<< " * REGLES *" << std::endl
<< " * *" << std::endl
<< " * Le principe est simple. Comme son nom l'indique, le but du *" << std::endl
<< " * jeu est de deminer le terrain, symbolise par une grille. *" << std::endl
<< " * Une fois qu'une zone est decouverte il est possible de *" << std::endl
<< " * deduire la position des mines par rapport aux indications *" << std::endl
<< " * chiffrees donnees par les cases mises a jour. En effet ces *" << std::endl
<< " * cases sont numerotees de 1 a 8. Ce chiffre indique le nombre *" << std::endl
<< " * de mines se trouvant dans les 8 cases entourant celle *" << std::endl
<< " * contentant le chiffre. De cette maniere on peut (en théorie *" << std::endl
<< " * du moins) finir le jeu. *" << std::endl
<< " * La partie est terminee lorque vous avez decouvert toutes les *" << std::endl
<< " * mines du terrain, ou si vous avez malencontreusement pose la *" << std::endl
<< " * souris sur une case minee ! *" << std::endl
<< " * *" << std::endl
<< " ****************************************************************" << std::endl;

}

void scores()
{
std::ifstream file("scores.txt");
if (!file)
{
std::cout << "ERREUR: Impossible d'ouvrir le fichier en lecture." << std::endl;
return;
}

showHeader();
std::cout << " ****************************************************************\n"
<< " * *\n"
<< " * ** ** ** ** ** ** ** *\n"
<< " ** ** ** ** ** ** ** *SCORES ** ** ** ** ** ** ***\n"
<< " * ** ** ** ** ** ** ** ** *\n";

unsigned int i = 0;
std::string line;
while (getline(file, line))
std::cout << " * " << ++i << ". " << line << " *\n";
std::cout << " * *\n"
<< " ****************************************************************\n" << std::endl;
}

int main()
{
srand(time(0));

bool finished = false;
char choix;

while (!finished)
{
std::cout << std::endl;
showHeader();
menu();
std::cin >> choix;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

switch (choix)
{
case '1':
nouveauJeu();
break;
case '2':
rules();
break;
case '3':
scores();
break;
default:
std::cout << "Choix inconnu" << std::endl;
}

std::cout << "Tapez 0 pour retourner au menu." << std::endl;
std::cin >> finished;
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

return 0;
}
cs_nikau Messages postés 8 Date d'inscription dimanche 25 novembre 2007 Statut Membre Dernière intervention 30 juillet 2012
11 juil. 2012 à 17:04
Salut,
Utiliser des class te permettrait de ne pas avoir à utiliser des variables globales et ça serait plus propre.
Les class en C++, au début, et si tu n'a jamais utilisé un langage objet auparavant, ça n'est pas facile de bien en comprendre le concept, mais il faudra bien que tu t'y mettes, sinon autant faire du C. Lance toi ! Même si tu n'as pas tout bien compris, force toi à en faire une dans ton programme, c'est le meilleur moyen d'apprendre. Tu verras que tu vas vite comprendre et te rendre compte que finalement c'est très facile.
Quand tu auras bien compris comment ça fonctionne, tu ne pourras plus t'en passer.
zenoobrider Messages postés 4 Date d'inscription mercredi 25 novembre 2009 Statut Membre Dernière intervention 11 juillet 2012
11 juil. 2012 à 15:33
Désolé pour le double post, mais je viens de me rendre compte que mon bout de code qui est censé associer le pseudo du joueur au résultat dans les scores ne fonctionne pas, Les pseudos ne vont pas du tout avec les scores, sauf que je ne vois pas pourquoi, mon code a l'air d'être bon ...
zenoobrider Messages postés 4 Date d'inscription mercredi 25 novembre 2009 Statut Membre Dernière intervention 11 juillet 2012
11 juil. 2012 à 15:32
Voilà, j'ai tout corrigé, merci pour ton aide :)

Le .zip et le code source sont actualisés.
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
11 juil. 2012 à 12:14
>> Et pour finir, je ne maitrise pas encore assez bien les classes pour me permettre de les utiliser ^^
Pas gênant, ce n'est pas obligatoire pour le projet que tu présentes.

>> Bref, je m'occupe de tout, sauf du découpage en fonctions, que je n'arrive pas à gérer sans savoir initialiser des variables globales :/
Ne fait jamais de variables globales, tout simplement :).
Pour le découpage des fonctions, je te laisse mettre à jour ton code, et j'essaierais de te proposer un découpage propre.
zenoobrider Messages postés 4 Date d'inscription mercredi 25 novembre 2009 Statut Membre Dernière intervention 11 juillet 2012
11 juil. 2012 à 11:57
Merci pour ton aide, je m'en vais de ce pas régler tous ces problèmes.
Comme tu as sans doute pu le constater, je débute en C++, ce qui fait que je suis loin de connaitre toutes les syntaxes des mots clefs du C++.
-Pour le using namespace, je me charge de tout modifier.
-De même pour les commentaires, je vais essayer de les éclaircir ^^
-Au niveau de la casse, je peux gérer aussi.
-J'ai changé Options en Règles du jeu.
-Le chemin absolu est remplacé par "scores.txt"
-J'ai changé l'extension du header en .hh
-Pour le cout, j'avoue que j'ignorais que l'on pouvait enchainer des strings sans "<< endl", je règle ça de suite

J'arrive maintenant au problème des fonctions.
Au départ, je voulais tout découper en petites fonctions, mais j'ai été confronté à un problème de variables.
Comme je débute, j'ai surement dû louper quelque chose, mais je n'arrive pas à réutiliser des variables utilisées dans les fonctions en dehors...
Vu que j'en utilise beaucoup, il faudrait que j'intègre touuuuuuutes les variables en paramètres des fonctions, et ça serait long, très long ...
J'ai entendu parler de variables globales, mais je ne sais pas comment les utiliser ni les mettre en place :/

-Je vais introduire des booléens à la place de mes entiers de vérification
-Je rassemble en ce moment même mon placement des bombes en plusieurs boucles for qui raccourcissent fortement le code
-Pour le std::sort, là encore, j'ignorais même jusqu'à son existence x)
-Je mets en place la double boucle au niveau de l'affichage de la grille

Et pour finir, je ne maitrise pas encore assez bien les classes pour me permettre de les utiliser ^^

Bref, je m'occupe de tout, sauf du découpage en fonctions, que je n'arrive pas à gérer sans savoir initialiser des variables globales :/
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
11 juil. 2012 à 11:24
Merci de ne pas joindre dans ton zip les fichiers inutiles (les *.o par exemple).
cptpingu Messages postés 3837 Date d'inscription dimanche 12 décembre 2004 Statut Modérateur Dernière intervention 28 mars 2023 123
11 juil. 2012 à 11:23
Bonjour.

Au niveau conptuel:
- Lorsque l'on tombe sur un 0, il est d'usage de dévoiler tous les 0 autour jusqu'à tomber sur des "chiffres".
- Titre/décoration trop grande. C'est plus pénible qu'autre chose.
- Ne pas prendre en compte la casse serait mieux (d10 et D10 accepté, par exemple).
- "Options" n'est pas un menu d'option. C'est une explication. Devrait s'appeler "Règle du jeu".

Au niveau technique (pleins de choses qui me choquent dans ton code):
- Évite les using namespace ! Voir: http://0217021.free.fr/portfolio/axel.berardino/articles/bon-usage-using-namespace
- Commenter, ce n'est pas "paraphraser". Voir: http://0217021.free.fr/portfolio/axel.berardino/articles/ecrire-de-bons-commentaires
- N'utilise pas de chemin absolu ! "C:/Users/Genevieve/Documents/C++/Projet/Perso/scores.txt" ne fonctionnera que chez toi...
- Un header en C++, c'est .hh ou .hpp, mais pas .h (réservé au C).
- Tu peux enchainer les écritures au lieu de refaire des std::cout de partout...

std::cout << "\t\t\t *************************" << std::endl;
std::cout << "\t\t\t *\t Demineur *" << std::endl;
std::cout << "\t\t\t *\t By *" << std::endl;
std::cout << "\t\t\t *\t Zenoo *" << std::endl;
std::cout << "\t\t\t *************************" << std::endl;
std::cout << std::endl;

devrait être:

std::cout << "\t\t\t *************************\n"
<< "\t\t\t *\t Demineur *\n"
<< "\t\t\t *\t By *\n"
<< "\t\t\t *\t Zenoo *\n"
<< "\t\t\t *************************\n" << std::endl;

Comme en plus, ce ne sont que des string, on pourrait même écrire:
std::cout << "\t\t\t *************************\n"
"\t\t\t *\t Demineur *\n"
"\t\t\t *\t By *\n"
"\t\t\t *\t Zenoo *\n"
"\t\t\t *************************\n" << std::endl;

- Une fonction dépasse rarement 50 lignes. Tu ne découpes pas assez ton code en petite fonction.
- Plutôt que de faire plein de tableau, fais un seul tableau contenant des structures. La structure contenant des informations sur la case en cours.
- Dans la fonction main, "boucleMenu" devrait être un booléen. Les tests sur cette variable devrait être factorisé en dehors des "if". Il y a redondance pour rien.
- Au lieu d'écrire chacun des membres du tableau, utilise une boucle (je pense notamment à l'affichage du score).
- Plutôt que de donner une taille fixe au std::vector, utilise la méthode ".push_back" (à la limite avec un ".reserve" pour l'optimisation). Si la taille est fixe, utilise un std::array (attention C++0x seulement).- pointsGagnes (pointsGagnes + 1000);> pointsGagnes += 1000;
- Trop de redondance dans la méthode qui pose les bombes. Tu as une série de "else if" qui peut aisément se factoriser... De plus, pas besoin de faire des tableaux pour les bombes (surtout que faire deux tableaux, je ne vois vraiment pas l'intérêt). Il suffit de poser ta bombe (si il n'y en a pas déjà une) et d'incrémenter de 1 tous les bords de celle-ci. Ça peut se faire dans une boucle, sans utiliser de tableaux.
- "void tri_bulles(vector& tab)" => Préfère un std::sort, bien plus performant que ton tri à bulles.
- L'affichage de la grille est perfectible. Tu utilises 10 fois la même boucle. Une double boucle aurait grandement facilité la chose.
- "int gain(1); // Si 0, perdu." C'est ici un booléen et non "int" qui devrait être utilisé.

Au niveau architectural:
- Une classe aurait été adaptée pour gérer la grille.

Y a beaucoup de chose à améliorer, et j'ai surement oublié des choses. Mais je te laisse déjà corriger tout cela.
Rejoignez-nous