1)Un bool... c'est pas un truc pour dire oui ou non... J'opterai pour l'enum, même si je les ai très très rarement manipulé.
2)C'est fait par obligation. Comme c'est un tableau de char, et i est un int, j'ai beau cherché une fonction qui convertit int en char mais j'ai pas trouvé ce qu'il me fallait. Si tu connais une fonction qui fait le boulot dis le moi
3)Ok.
1) Le bool aurait pu être utilisé pour dire modeMulti = true par exemple pour dire que tu joue en multi-joueur. Cependant, le type enum est aussi bien voir mieux (lisibilité, possibilité de plus de mode ...)
2) Si, c'est possible, la preuve :
1) C'est pas logique ce que tu dis, sinon je laisserai les int Par contre j'ai opté pour les enum.
2) Ce n'est pas possible warning: conversion to 'char' from 'int' may alter its value|
Au pire je peux faire comme ça mais ce n'est pas clean ...
Edit: j'ai essayé avec static_cast mais ça m'affiche des symboles et mon pc bip !
itoa est le même nom qu'une fonction de la libc, mais ça ne devrait pas gêner puisque, de mémoire, le prototype n'est pas le même. On utilise 'i % 10' pour être sûr qu'on a un chiffre qui tient dans un caractère : si jamais on envoyait 1353, ça ferait tout bugger, sinon. Là, on a le dernier caractère qui est transcrit : itoa(1353) == '3'.
Puis, on peut l'utiliser comme ça :
for (int i = 0 ; i < 9 ; ++i) grid[i] = itoa(i + 1);
En fait, la solution la plus simple est la première.
Après modification, voilà à quoi ressemble mon code du Puissance 4:
#include "Menu.h"
int main()
{
Menu m;
m.show();
return 0;
}
#ifndef Menu_H_INCLUDED
#define Menu_H_INCLUDED
class Menu
{
public:
Menu();
void show(); // affiche le menu
void play(); // créer la grille puis lance la fonction play de la classe Grid
void options(); // modification des dimensions de la grille
enum CHOICE{PLAY = 1, OPTIONS = 2, EXIT = 3}; // choix du menu
private:
int m_height, m_width;
CHOICE m_choice;
};
#endif // Menu_H_INCLUDED
#include <iostream>
#include <fstream>
#include <string>
#include "Menu.h"
#include "Grid.h"
using namespace std;
Menu::Menu() : m_height(6), m_width(7)
{
}
void Menu::show()
{
do
{
cout << "*** Connect Four ***\n\n";
cout << "1 : Play" << endl;
cout << "2 : Options" << endl;
cout << "3 : Exit" << endl;
cout << "Your choice : ";
int choice;
cin >> choice;
m_choice = CHOICE(choice);
switch(m_choice)
{
case PLAY:
play();
break;
case OPTIONS:
options();
break;
default:
break;
}
}while(m_choice != EXIT);
}
void Menu::play()
{
Grid g(m_height, m_width);
cout << g.play() << " has won !" << endl;
}
void Menu::options()
{
cout << "Enter the height of the grid (4 - 10): ";
do
{
cin >> m_height;
}while(m_height < 4 || m_height > 10);
cout << "Enter the width of the grid (4 - 10): ";
do
{
cin >> m_width;
}while(m_width < 4 || m_width > 10);
}
#ifndef Grid_H_INCLUDED
#define Grid_H_INCLUDED
#include <string>
#include <vector>
class Grid
{
public:
Grid(int height = 6, int width = 7);
std::string play(); // fonction principal renvoyant le vainqueur à la fin
void show() const; // affiche la grille
bool height(int &x,int y); // remplace x par la hauteur de la colonne choisis et renvoie "true" si tout est OK
bool victory() const; // vérifie si un joueur à gagné
bool full() const; // vérifie si la grille est remplie
void reset(); // initialise la grille
enum STATE {EMPTY, CROSS, CIRCLE}; // Etat d'une case de la grille
private:
int m_height, m_width; // taille réspectivement en hauteur et largeur de la grille
bool m_equality; // contient "true" si la grille est remplie et que personne n'a gagné
STATE m_player; // Etat du joueur actuel
std::vector <std::vector<STATE> >m_grid; // La grille
};
#endif // Grid_H_INCLUDED
Oui, en effet, car l'dée est bien trouvée, beaucoup d'ameliorations sont trouvable et realisables, c'est découpé en pas mal de niveaux, c'est facile, c'est marrant, ect...
Mais aussi la description est precise.
Après, pour la difficulté, je mettrait pas plus de 2
germino:
1) ajout des derniers exercices (création de la section logiciels)
2) ajout des dernières solutions
3) ajout de difficultés (rehaussement de la difficulté des jeux qui sont en fait l'une des branches les plus ardues de la programmation)
4) ouf ! ça c'est fait !
#ifndef JOUEUR_H
#define JOUEUR_H
#include <QtCore/QString>
#include <QtGui/QColor>
/**
* Cette classe représente un joueur, celui-ci est représenté par un nom et une couleur
*/
class Joueur
{
QColor _couleur;
QString _nom;
public:
Joueur(const QColor& couleur);
QColor getCouleur() const{return _couleur;}
QString getStringCouleur();
QString getNom() const;
void setNom(QString nom);
};
#endif // JOUEUR_H
#include "Joueur.h"
Joueur::Joueur(const QColor& couleur) :
_couleur(couleur)
{
}
QString Joueur::getStringCouleur()
{
int red = _couleur.red();
int green = _couleur.green();
int blue = _couleur.blue();
QString res;
if(red < 16)
res.append('0');
res.append(QString::number(red,16));
if(green < 16)
res.append('0');
res.append(QString::number(green,16));
if(blue < 16)
res.append('0');
res.append(QString::number(blue,16));
return res;
}
QString Joueur::getNom() const
{
return _nom;
}
void Joueur::setNom(QString nom)
{
_nom = nom;
}
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui/QMainWindow>
class Joueur;
class Plateau;
class QTextEdit;
class QPushButton;
/**
* Classe principale de l'application, c'est elle qui gère le jeu
*/
class MainWindow : public QMainWindow
{
Q_OBJECT
Joueur* _joueur1;
Joueur* _joueur2;
Joueur* _joueurCourant;
Plateau* _plateau;
QTextEdit* _console;
QPushButton* _btnJouer;
bool _commencee; //dit si la partie a commencée ou non (ou si elle est terminée)
unsigned int _nbCoupsAJouer; //donne le nombre total de coups à jouer
unsigned int _nbCoupsJoues; //donne le nombre de coups actuellement joués
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
Joueur* getJoueurCourant();
void passerTourSuivant();
bool partieEnCours(){return _commencee;}
void joueurGagne(int nbAllignes); //méthode appelée lorsqu'un joueur gagne
protected:
//on quitte le programme lorsque échap est appuyé
void keyReleaseEvent(QKeyEvent* event);
public slots:
void nomJoueur1Change(QString text);
void nomJoueur2Change(QString text);
void jouer();
};
#endif // MAINWINDOW_H
#include "MainWindow.h"
#include "Plateau.h"
#include "Joueur.h"
#include "math.h"
#include <QtGui/QGridLayout>
#include <QtGui/QLineEdit>
#include <QtGui/QPushButton>
#include <QtGui/QTextEdit>
#include <QtGui/qevent.h>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
_joueur1(new Joueur(Qt::blue)),
_joueur2(new Joueur(Qt::red)),
_joueurCourant(NULL),
_nbCoupsJoues(0)
{
//initialisation des joueurs
_joueur1->setNom(QString("Joueur1"));
_joueur2->setNom(QString("Joueur2"));
//initialisation de la fenêtre
_plateau = new Plateau(this);
_nbCoupsAJouer = 6*7; //initialisation du nombre de coups à jouer en fonction des dimmensions demandées au plateau
QGridLayout* layout = new QGridLayout();
QLineEdit* lineEditTemp = new QLineEdit("Joueur1");
connect(lineEditTemp, SIGNAL(textChanged(QString)), this, SLOT(nomJoueur1Change(QString)));
layout->addWidget(lineEditTemp, 0,0);
lineEditTemp = new QLineEdit("Joueur2");
connect(lineEditTemp, SIGNAL(textChanged(QString)), this, SLOT(nomJoueur2Change(QString)));
layout->addWidget(lineEditTemp, 0,1);
_btnJouer = new QPushButton("Jouer");
_btnJouer->setFixedSize(125,125);
connect(_btnJouer, SIGNAL(clicked()), this, SLOT(jouer()));
layout->addWidget(_btnJouer, 0, 2, 1, 1, Qt::AlignRight);
_console = new QTextEdit();
_console->setReadOnly(true);
layout->addWidget(_console, 0, 3, 2, 1);
layout->addWidget(_plateau, 1, 0, 1, 3);
layout->setColumnStretch(3, 2); //on demande à ce que la console prenne le reste de la place disponible
QWidget* tempWidget = new QWidget();
setCentralWidget(tempWidget);
tempWidget->setLayout(layout);
}
MainWindow::~MainWindow()
{
}
Joueur* MainWindow::getJoueurCourant()
{
return _joueurCourant;
}
void MainWindow::passerTourSuivant()
{
_joueurCourant = _joueurCourant == _joueur1 ? _joueur2 : _joueur1;
_console->append("c'est maintenant à <FONT COLOR=\"#" + _joueurCourant->getStringCouleur() + "\"><strong>" + _joueurCourant->getNom() + "</strong></FONT> de jouer");
++_nbCoupsJoues;
//si la partie est finie, on met à jour _commencee, on réactive le bouton _jouer
if(_nbCoupsJoues >= _nbCoupsAJouer)
{
_commencee = false;
_btnJouer->setEnabled(true);
_console->append("<h3>La partie se termine par un match nul</h3>");
}
}
void MainWindow::joueurGagne(int nbAllignes)
{
unsigned int nbCoups = ceil((_nbCoupsJoues+1)/2.0f); //+1 car on commence à 0
_console->append("<h3>Le joueur <FONT COLOR=\"#" + _joueurCourant->getStringCouleur() + "\"><strong>" + _joueurCourant->getNom() + "</strong></FONT> remporte la partie avec " + QString::number(nbAllignes) + " pièces allignées en " + QString::number(nbCoups) + " coups</h3>");
_console->append("Fin de la partie");
_commencee = false;
_btnJouer->setEnabled(true);
}
void MainWindow::keyReleaseEvent(QKeyEvent* event)
{
if(event->key() == Qt::Key_Escape)
close();
}
void MainWindow::nomJoueur1Change(QString text)
{
_joueur1->setNom(text);
}
void MainWindow::nomJoueur2Change(QString text)
{
_joueur2->setNom(text);
}
void MainWindow::jouer()
{
//tirage du joueur qui doit commencer.
_joueurCourant = (unsigned int)qrand()%2 == 0 ? _joueur1 : _joueur2;
_console->append("<FONT COLOR=\"#" + _joueurCourant->getStringCouleur() + "\"><strong>" + _joueurCourant->getNom() + "</strong></FONT> commence à jouer");
_commencee = true;
//on désactive le bouton _jouer
_btnJouer->setEnabled(false);
//on nettoie le plateau (même si c'est la première partie)
_plateau->nettoyer();
_nbCoupsJoues = 0;
}
#ifndef PIECE_H
#define PIECE_H
#include <QtGui/QGraphicsEllipseItem>
class Joueur;
/**
* Cette classe représente un pièce, celle-ci mémorise ses voisins afin d'être en mesure de dire si c'est une pièce qui va faire gagner
* Une pièce est caractérisé par le joueur qui l'a joué
* Elle stock aussi l'élément la représentant à l'écran
*/
class Piece
{
friend class Plateau; //cela nous évite de faire 8 accesseurs
Piece* _voisinHaut;
Piece* _voisinHautDroite;
Piece* _voisinDroite;
Piece* _voisinBasDroite;
Piece* _voisinBas;
Piece* _voisinBasGauche;
Piece* _voisinGauche;
Piece* _voisinHautGauche;
Joueur* _joueur;
QGraphicsEllipseItem _elementGraphique;
public:
Piece();
void setJoueur(Joueur* joueur);
/** les paramètres sont des variables de sortie correspondant aux élipses positionée aux extrémités de la ligne gagnante (si il y a une ligne gagnante, sinon, le résultat est incohérent */
unsigned int gagnant(QGraphicsEllipseItem*& pointDepart, QGraphicsEllipseItem*& pointArrivee); //retourne le nombre de pieces allignés avec celle-ci
QGraphicsEllipseItem* getElementGraphique();
void reinitialiser();
};
#endif // PIECE_H
#ifndef PLATEAU_H
#define PLATEAU_H
#include <QtCore/QList>
#include <QtGui/QGraphicsView>
class Piece;
class QGraphicsRectItem;
class QGraphicsLineItem;
class MainWindow;
/**
* Le plateau représente l'environnement de jeux, c'est lui qui gère l'ajout des pièce au jeux, c'est aussi lui qui dira au mainwindow lorsqu'une pièce gagnante a été posée
*/
class Plateau : public QGraphicsView
{
QList<Piece*> _pieces; //toutes les pièces disponibles
QList<QList<Piece*> > _grille; //les pièces dans la grilles
QGraphicsRectItem* _rectangleGris; //le rectangle qui indiquera ou apparaitra la pièce lorsqu'on déplace la souris
QGraphicsLineItem* _ligneGagnant; //la ligne mettant en valeur la combinaison gagnante
unsigned int _largeur;
unsigned int _hauteur;
MainWindow* _parent;
void chainerPieceAvecVoisins(unsigned int x, unsigned int y);
public:
Plateau(MainWindow* parent, unsigned int tailleX = 7, unsigned int tailleY = 6);
void nettoyer(); //réinitialise le plateau
protected:
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
};
#endif // PLATEAU_H
#include "Plateau.h"
#include "Piece.h"
#include "MainWindow.h"
#include "Joueur.h"
#include <QtGui/QGraphicsScene>
#include <QtGui/QGraphicsRectItem>
#include <QtGui/QGraphicsLineItem>
#include <QtGui/QPen>
#include <QtGui/QMouseEvent>
Plateau::Plateau(MainWindow* parent, unsigned int tailleX, unsigned int tailleY) :
_largeur(tailleX),
_hauteur(tailleY),
_parent(parent)
{
//initialisation de la grille
for(unsigned int x=0; x<tailleX; ++x)
{
_grille.append(QList<Piece*>());
for(unsigned int y=0; y<tailleY; ++y)
{
_grille.last().append(NULL);
}
}
//initialisation des pieces
for(unsigned int i = tailleX * tailleY; i > 0; --i)
{
_pieces.append(new Piece());
}
setFixedSize(825, 825);
//gestion de la scène
setScene(new QGraphicsScene());
scene()->setSceneRect(0,0,800,800);
scene()->setBackgroundBrush(QBrush(Qt::black));
//traçage de la grille
QPen pen(Qt::yellow);
float deltaX = 800.0f/tailleX;
for(unsigned int x=0; x<=tailleX; ++x)
{
scene()->addLine(x*deltaX, 0, x*deltaX, 800, pen);
}
float deltaY = 800.0f/tailleY;
for(unsigned int y=0; y<=tailleY; ++y)
{
scene()->addLine(0, y*deltaY, 800, y*deltaY, pen);
}
//initialisation du carré gris
_rectangleGris = new QGraphicsRectItem();
_rectangleGris->setBrush(QBrush(Qt::lightGray));
_rectangleGris->setRect(1,1, deltaX-2, deltaY-2);
//initialisation de la ligne
_ligneGagnant = new QGraphicsLineItem();
_ligneGagnant->setZValue(1); //on veut être sûr qu'elle soit au dessus de tout le monde
QPen penLine(Qt::magenta);
penLine.setCapStyle(Qt::RoundCap);
penLine.setWidth(5);
_ligneGagnant->setPen(penLine);
//la ligne suivante permet de déclencer mouseMouveEvent sans qu'on bouton ne soit appuyé
setMouseTracking(true);
}
void Plateau::nettoyer()
{
for(unsigned int i=0; i<_grille.size(); ++i)
{
for(unsigned int j=0; j<_grille[i].size(); ++j)
{
if(_grille[i][j] != NULL)
{
//on enlève l'élément de la scène
scene()->removeItem(_grille[i][j]->getElementGraphique());
//on remet la pièce dans le container
_pieces.append(_grille[i][j]);
//on réinitialise la pièce
_pieces.last()->reinitialiser();
//on réinitialise la grille
_grille[i][j] = NULL;
}
}
}
scene()->removeItem(_ligneGagnant);
}
void Plateau::mouseReleaseEvent(QMouseEvent* event)
{
if(!_parent->partieEnCours())
return;
if(!scene()->items().contains(_rectangleGris)) //si le rectangle gris ne fait pas parti de la scène, il n'y a pas leiu d'ajouter une pièce
return;
float deltaX = 800.0f/_largeur;
float deltaY = 800.0f/_hauteur;
//on ajoute une pièce à l'écran
Piece* pieceTemp = _pieces.takeFirst();
pieceTemp->getElementGraphique()->setPos(_rectangleGris->pos());
pieceTemp->getElementGraphique()->setRect(1, 1, deltaX-2, deltaY-2);
pieceTemp->setJoueur(_parent->getJoueurCourant());
scene()->addItem(pieceTemp->getElementGraphique());
//on ajoute la pièce à la grille
unsigned int positionX = (unsigned int)(_rectangleGris->x()/deltaX);
//si la souris sort de la zone du plateau, fait comme si elle était sur la dernière colone
positionX = positionX >= _largeur ? positionX-1 : positionX;
unsigned int positionY = (unsigned int)(_rectangleGris->y()/deltaY);
positionY = positionY >= _hauteur ? positionY-1 : positionY;
_grille[positionX][positionY] = pieceTemp;
//on la chaine avec ses voisins
chainerPieceAvecVoisins(positionX, positionY);
//on enlève le rectangle gris de la scène
scene()->removeItem(_rectangleGris);
//on regarde si le pièce déclence une victoire
QGraphicsEllipseItem* depart;
QGraphicsEllipseItem* arrivee;
unsigned int nbAllignes = pieceTemp->gagnant(depart, arrivee);
if(nbAllignes>3)
{
//on trace la ligne
float x1 = depart->pos().x() + depart->rect().x() + depart->rect().width()/2;
float x2 = arrivee->pos().x() + arrivee->rect().x() + arrivee->rect().width()/2;
float y1 = depart->pos().y() + depart->rect().y() + depart->rect().height()/2;
float y2 = arrivee->pos().y() + arrivee->rect().y() + arrivee->rect().height()/2;
_ligneGagnant->setLine(x1, y1, x2, y2);
scene()->addItem(_ligneGagnant);
_parent->joueurGagne(nbAllignes);
}
else
{
//on passe au tour suivant
_parent->passerTourSuivant();
}
}
void Plateau::mouseMoveEvent(QMouseEvent* event)
{
if(!_parent->partieEnCours())
return;
float deltaX = 800.0f/_largeur;
float deltaY = 800.0f/_hauteur;
unsigned int positionX = (unsigned int)(event->x()/deltaX);
//si la souris sort de la zone du plateau, fait comme si elle était sur la dernière colone
positionX = positionX >= _largeur ? positionX-1 : positionX;
// recherche de la place la plus haute non prise
unsigned int i = 0;
while(i<_grille[positionX].size() && _grille[positionX][i] == NULL)
++i;
//on en a trouvé une
if(i<_grille[positionX].size() && i>0 && _grille[positionX][i] != NULL)
{
_rectangleGris->setPos(positionX * deltaX, (i-1)*deltaY);
if(!scene()->items().contains(_rectangleGris)) //si le rectangle gris n'est pas déjà dans la scène, on l'ajoute
scene()->addItem(_rectangleGris);
}
//on en a pas trouvé, deux possibilités: la grille est vide, la grille est pleine
else if(i>=_grille[positionX].size()) //la colone est vide
{
_rectangleGris->setPos(positionX * deltaX, (i-1)*deltaY);
if(!scene()->items().contains(_rectangleGris)) //si le rectangle gris n'est pas déjà dans la scène, on l'ajoute
scene()->addItem(_rectangleGris);
}
//sinon, on ne déplace pas le carré gris
}
void Plateau::chainerPieceAvecVoisins(unsigned int x, unsigned int y)
{
//la pièce vien d'être ajoutée, elle ne peut donc avoir personne directement au dessus d'elle
//bas droite
if(x+1 < _largeur && y+1 < _hauteur)
{
_grille[x][y]->_voisinBasDroite = _grille[x+1][y+1];
if(_grille[x+1][y+1] != NULL)
_grille[x+1][y+1]->_voisinHautGauche = _grille[x][y];
}
//droite
if(x+1 < _largeur)
{
_grille[x][y]->_voisinDroite = _grille[x+1][y];
if(_grille[x+1][y] != NULL)
_grille[x+1][y]->_voisinGauche = _grille[x][y];
}
//haut droite
if(x+1 < _largeur && ((int)y)-1 >= 0)
{
_grille[x][y]->_voisinHautDroite = _grille[x+1][y-1];
if( _grille[x+1][y-1] != NULL)
_grille[x+1][y-1]->_voisinBasGauche = _grille[x][y];
}
//bas
if(y+1 < _hauteur)
{
_grille[x][y]->_voisinBas = _grille[x][y+1];
if(_grille[x][y+1] != NULL)
_grille[x][y+1]->_voisinHaut = _grille[x][y];
}
//haut gauche
if(((int)y)-1 >= 0 && ((int)x)-1 >= 0)
{
_grille[x][y]->_voisinHautGauche = _grille[x-1][y-1];
if(_grille[x-1][y-1] != NULL)
_grille[x-1][y-1]->_voisinBasDroite = _grille[x][y];
}
//gauche
if(((int)x)-1 >= 0)
{
_grille[x][y]->_voisinGauche = _grille[x-1][y];
if(_grille[x-1][y] != NULL)
_grille[x-1][y]->_voisinDroite = _grille[x][y];
}
//bas gauche
if(y+1 < _hauteur && ((int)x)-1 >= 0)
{
_grille[x][y]->_voisinBasGauche = _grille[x-1][y+1];
if(_grille[x-1][y+1] != NULL)
_grille[x-1][y+1]->_voisinHautDroite = _grille[x][y];
}
}
Puissance 4 - Niveau 2
je remet tous les fichiers, car certains ont subit des modifications
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui/QMainWindow>
#include "TableauScores.h"
class Joueur;
class Plateau;
class QTextEdit;
class QPushButton;
/**
* Classe principale de l'application, c'est elle qui gère le jeu
*/
class MainWindow : public QMainWindow
{
Q_OBJECT
Joueur* _joueur1;
Joueur* _joueur2;
Joueur* _joueurCourant;
Plateau* _plateau;
QTextEdit* _console;
QPushButton* _btnJouer;
bool _commencee; //dit si la partie a commencée ou non (ou si elle est terminée)
unsigned int _nbCoupsAJouer; //donne le nombre total de coups à jouer
unsigned int _nbCoupsJoues; //donne le nombre de coups actuellement joués
TableauScores _scores;
void createMenu();
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
Joueur* getJoueurCourant();
void passerTourSuivant();
bool partieEnCours(){return _commencee;}
void joueurGagne(int nbAllignes); //méthode appelée lorsqu'un joueur gagne
protected:
//on quitte le programme lorsque échap est appuyé
void keyReleaseEvent(QKeyEvent* event);
public slots:
void nomJoueur1Change(QString text);
void nomJoueur2Change(QString text);
void jouer();
void annuler();
void score();
};
#endif // MAINWINDOW_H
#include "MainWindow.h"
#include "Plateau.h"
#include "Joueur.h"
#include "ResumePartie.h"
#include "math.h"
#include <QtGui/QGridLayout>
#include <QtGui/QLineEdit>
#include <QtGui/QPushButton>
#include <QtGui/QTextEdit>
#include <QtGui/qevent.h>
#include <QtGui/QMenuBar>
#include <QtGui/QMessageBox>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
_joueur1(new Joueur(Qt::blue)),
_joueur2(new Joueur(Qt::red)),
_joueurCourant(NULL),
_nbCoupsJoues(0)
{
//initialisation des joueurs
_joueur1->setNom(QString("Joueur1"));
_joueur2->setNom(QString("Joueur2"));
//initialisation de la fenêtre
_plateau = new Plateau(this);
_nbCoupsAJouer = 6*7; //initialisation du nombre de coups à jouer en fonction des dimmensions demandées au plateau
QGridLayout* layout = new QGridLayout();
QLineEdit* lineEditTemp = new QLineEdit("Joueur1");
connect(lineEditTemp, SIGNAL(textChanged(QString)), this, SLOT(nomJoueur1Change(QString)));
layout->addWidget(lineEditTemp, 0,0);
lineEditTemp = new QLineEdit("Joueur2");
connect(lineEditTemp, SIGNAL(textChanged(QString)), this, SLOT(nomJoueur2Change(QString)));
layout->addWidget(lineEditTemp, 0,1);
_btnJouer = new QPushButton("Jouer");
_btnJouer->setFixedSize(80,80);
connect(_btnJouer, SIGNAL(clicked()), this, SLOT(jouer()));
layout->addWidget(_btnJouer, 0, 2, 1, 1, Qt::AlignRight);
_console = new QTextEdit();
_console->setReadOnly(true);
layout->addWidget(_console, 0, 3, 2, 1);
layout->addWidget(_plateau, 1, 0, 1, 3);
layout->setColumnStretch(3, 2); //on demande à ce que la console prenne le reste de la place disponible
QWidget* tempWidget = new QWidget();
setCentralWidget(tempWidget);
tempWidget->setLayout(layout);
//création du menu
createMenu();
}
MainWindow::~MainWindow()
{
}
void MainWindow::createMenu()
{
QMenuBar* menu = menuBar();
QAction* annuler = new QAction("Annuler", NULL);
connect(annuler, SIGNAL(triggered()), this, SLOT(annuler()));
menu->addAction(annuler);
QAction* score = new QAction("Score", NULL);
connect(score, SIGNAL(triggered()), this, SLOT(score()));
menu->addAction(score);
}
Joueur* MainWindow::getJoueurCourant()
{
return _joueurCourant;
}
void MainWindow::passerTourSuivant()
{
_joueurCourant = _joueurCourant == _joueur1 ? _joueur2 : _joueur1;
_console->append("c'est maintenant à <FONT COLOR=\"#" + _joueurCourant->getStringCouleur() + "\"><strong>" + _joueurCourant->getNom() + "</strong></FONT> de jouer");
++_nbCoupsJoues;
//si la partie est finie, on met à jour _commencee, on réactive le bouton _jouer
if(_nbCoupsJoues >= _nbCoupsAJouer)
{
_commencee = false;
_btnJouer->setEnabled(true);
_console->append("<h3>La partie se termine par un match nul</h3>");
_scores.ajouterScore(ResumePartie(_joueur1->getNom(), _joueur2->getNom(), _nbCoupsAJouer/2, "")); //on mémorise la partie
}
}
void MainWindow::joueurGagne(int nbAllignes)
{
unsigned int nbCoups = ceil((_nbCoupsJoues+1)/2.0f); //+1 car on commence à 0
_console->append("<h3>Le joueur <FONT COLOR=\"#" + _joueurCourant->getStringCouleur() + "\"><strong>" + _joueurCourant->getNom() + "</strong></FONT> remporte la partie avec " + QString::number(nbAllignes) + " pièces allignées en " + QString::number(nbCoups) + " coups</h3>");
_console->append("Fin de la partie");
_commencee = false;
_scores.ajouterScore(ResumePartie(_joueur1->getNom(), _joueur2->getNom(), nbCoups, _joueurCourant->getNom())); //on mémorise la partie
_btnJouer->setEnabled(true);
}
void MainWindow::keyReleaseEvent(QKeyEvent* event)
{
if(event->key() == Qt::Key_Escape)
close();
}
void MainWindow::nomJoueur1Change(QString text)
{
if(text == _joueur2->getNom()) //on ne prend pas en compte la modification
return;
//si le nom est vide, on met "Joueur1" comme nom
//si le nom contient des espaces, on le coupe
if(text.isEmpty())
text = "Joueur1";
_joueur1->setNom(text = text.split(' ')[0]); //on prend la première partie pour enlever l'espace
}
void MainWindow::nomJoueur2Change(QString text)
{
if(text == _joueur1->getNom()) //on ne prend pas en compte la modification
return;
//si le nom est vide, on met "Joueur2" comme nom
//si le nom contient des espaces, on le coupe
if(text.isEmpty())
text = "Joueur2";
_joueur2->setNom(text = text.split(' ')[0]); //on prend la première partie pour enlever l'espace
}
void MainWindow::jouer()
{
//tirage du joueur qui doit commencer.
_joueurCourant = (unsigned int)qrand()%2 == 0 ? _joueur1 : _joueur2;
_console->append("<FONT COLOR=\"#" + _joueurCourant->getStringCouleur() + "\"><strong>" + _joueurCourant->getNom() + "</strong></FONT> commence à jouer");
_commencee = true;
//on désactive le bouton _jouer
_btnJouer->setEnabled(false);
//on nettoie le plateau (même si c'est la première partie)
_plateau->nettoyer();
_nbCoupsJoues = 0;
}
void MainWindow::annuler()
{
QMessageBox msgBox;
if(_nbCoupsJoues == 0 )
msgBox.setText("Aucun coup n'a encore été joué");
else
msgBox.setText("Joué, c'est joué");
msgBox.exec();
}
void MainWindow::score()
{
_scores.exec();
}
#ifndef JOUEUR_H
#define JOUEUR_H
#include <QtCore/QString>
#include <QtGui/QColor>
/**
* Cette classe représente un joueur, celui-ci est représenté par un nom et une couleur
*/
class Joueur
{
QColor _couleur;
QString _nom;
public:
Joueur(const QColor& couleur);
QColor getCouleur() const{return _couleur;}
QString getStringCouleur();
QString getNom() const;
void setNom(QString nom);
};
#endif // JOUEUR_H
#include "Joueur.h"
Joueur::Joueur(const QColor& couleur) :
_couleur(couleur)
{
}
QString Joueur::getStringCouleur()
{
int red = _couleur.red();
int green = _couleur.green();
int blue = _couleur.blue();
QString res;
if(red < 16)
res.append('0');
res.append(QString::number(red,16));
if(green < 16)
res.append('0');
res.append(QString::number(green,16));
if(blue < 16)
res.append('0');
res.append(QString::number(blue,16));
return res;
}
QString Joueur::getNom() const
{
return _nom;
}
void Joueur::setNom(QString nom)
{
_nom = nom;
}
#ifndef PIECE_H
#define PIECE_H
#include <QtGui/QGraphicsEllipseItem>
class Joueur;
/**
* Cette classe représente une pièce, celle-ci mémorise ses voisins afin d'être en mesure de dire si c'est une pièce qui va faire gagner
* Une pièce est caractérisé par le joueur qui l'a joué
* Elle stock aussi l'élément la représentant à l'écran
*/
class Piece
{
friend class Plateau; //cela nous évite de faire 8 accesseurs
Piece* _voisinHaut;
Piece* _voisinHautDroite;
Piece* _voisinDroite;
Piece* _voisinBasDroite;
Piece* _voisinBas;
Piece* _voisinBasGauche;
Piece* _voisinGauche;
Piece* _voisinHautGauche;
Joueur* _joueur;
QGraphicsEllipseItem _elementGraphique;
public:
Piece();
void setJoueur(Joueur* joueur);
/** les paramètres sont des variables de sortie correspondant aux élipses positionées aux extrémités de la ligne gagnante (si il y a une ligne gagnante, sinon, le résultat est incohérent) */
unsigned int gagnant(QGraphicsEllipseItem*& pointDepart, QGraphicsEllipseItem*& pointArrivee); //retourne le nombre de pieces allignés avec celle-ci
QGraphicsEllipseItem* getElementGraphique();
void reinitialiser();
};
#endif // PIECE_H
#ifndef PLATEAU_H
#define PLATEAU_H
#include <QtCore/QList>
#include <QtGui/QGraphicsView>
class Piece;
class QGraphicsRectItem;
class QGraphicsLineItem;
class MainWindow;
/**
* Le plateau représente l'environnement de jeux, c'est lui qui gère l'ajout des pièce au jeux, c'est aussi lui qui dira au mainwindow lorsqu'une pièce gagnante a été posée
*/
class Plateau : public QGraphicsView
{
QList<Piece*> _pieces; //toutes les pièces disponibles
QList<QList<Piece*> > _grille; //les pièces dans la grilles
QGraphicsRectItem* _rectangleGris; //le rectangle qui indiquera ou apparaitra la pièce lorsqu'on déplace la souris
QGraphicsLineItem* _ligneGagnant; //la ligne mettant en valeur la combinaison gagnante
unsigned int _largeur;
unsigned int _hauteur;
MainWindow* _parent;
void chainerPieceAvecVoisins(unsigned int x, unsigned int y);
public:
Plateau(MainWindow* parent, unsigned int tailleX = 7, unsigned int tailleY = 6);
void nettoyer(); //réinitialise le plateau
protected:
void mouseReleaseEvent(QMouseEvent* event);
void mouseMoveEvent(QMouseEvent* event);
};
#endif // PLATEAU_H
#include "Plateau.h"
#include "Piece.h"
#include "MainWindow.h"
#include "Joueur.h"
#include <QtGui/QGraphicsScene>
#include <QtGui/QGraphicsRectItem>
#include <QtGui/QGraphicsLineItem>
#include <QtGui/QPen>
#include <QtGui/QMouseEvent>
Plateau::Plateau(MainWindow* parent, unsigned int tailleX, unsigned int tailleY) :
_largeur(tailleX),
_hauteur(tailleY),
_parent(parent)
{
//initialisation de la grille
for(unsigned int x=0; x<tailleX; ++x)
{
_grille.append(QList<Piece*>());
for(unsigned int y=0; y<tailleY; ++y)
{
_grille.last().append(NULL);
}
}
//initialisation des pieces
for(unsigned int i = tailleX * tailleY; i > 0; --i)
{
_pieces.append(new Piece());
}
setFixedSize(825, 825);
//gestion de la scène
setScene(new QGraphicsScene());
scene()->setSceneRect(0,0,800,800);
scene()->setBackgroundBrush(QBrush(Qt::black));
//traçage de la grille
QPen pen(Qt::yellow);
float deltaX = 800.0f/tailleX;
for(unsigned int x=0; x<=tailleX; ++x)
{
scene()->addLine(x*deltaX, 0, x*deltaX, 800, pen);
}
float deltaY = 800.0f/tailleY;
for(unsigned int y=0; y<=tailleY; ++y)
{
scene()->addLine(0, y*deltaY, 800, y*deltaY, pen);
}
//initialisation du carré gris
_rectangleGris = new QGraphicsRectItem();
_rectangleGris->setBrush(QBrush(Qt::lightGray));
_rectangleGris->setRect(1,1, deltaX-2, deltaY-2);
//initialisation de la ligne
_ligneGagnant = new QGraphicsLineItem();
_ligneGagnant->setZValue(1); //on veut être sûr qu'elle soit au dessus de tout le monde
QPen penLine(Qt::magenta);
penLine.setCapStyle(Qt::RoundCap);
penLine.setWidth(5);
_ligneGagnant->setPen(penLine);
//la ligne suivante permet de déclencher mouseMouveEvent sans qu'on bouton ne soit appuyé
setMouseTracking(true);
}
void Plateau::nettoyer()
{
for(unsigned int i=0; i<_grille.size(); ++i)
{
for(unsigned int j=0; j<_grille[i].size(); ++j)
{
if(_grille[i][j] != NULL)
{
//on enlève l'élément de la scène
scene()->removeItem(_grille[i][j]->getElementGraphique());
//on remet la pièce dans le container
_pieces.append(_grille[i][j]);
//on réinitialise la pièce
_pieces.last()->reinitialiser();
//on réinitialise la grille
_grille[i][j] = NULL;
}
}
}
scene()->removeItem(_ligneGagnant);
}
void Plateau::mouseReleaseEvent(QMouseEvent* event)
{
if(!_parent->partieEnCours())
return;
if(!scene()->items().contains(_rectangleGris)) //si le rectangle gris ne fait pas parti de la scène, il n'y a pas leiu d'ajouter une pièce
return;
float deltaX = 800.0f/_largeur;
float deltaY = 800.0f/_hauteur;
//on ajoute une pièce à l'écran
Piece* pieceTemp = _pieces.takeFirst();
pieceTemp->getElementGraphique()->setPos(_rectangleGris->pos());
pieceTemp->getElementGraphique()->setRect(1, 1, deltaX-2, deltaY-2);
pieceTemp->setJoueur(_parent->getJoueurCourant());
scene()->addItem(pieceTemp->getElementGraphique());
//on ajoute la pièce à la grille
unsigned int positionX = (unsigned int)(_rectangleGris->x()/deltaX);
//si la souris sort de la zone du plateau, fait comme si elle était sur la dernière colone
positionX = positionX >= _largeur ? positionX-1 : positionX;
unsigned int positionY = (unsigned int)(_rectangleGris->y()/deltaY);
positionY = positionY >= _hauteur ? positionY-1 : positionY;
_grille[positionX][positionY] = pieceTemp;
//on la chaine avec ses voisins
chainerPieceAvecVoisins(positionX, positionY);
//on enlève le rectangle gris de la scène
scene()->removeItem(_rectangleGris);
//on regarde si le pièce déclence une victoire
QGraphicsEllipseItem* depart;
QGraphicsEllipseItem* arrivee;
unsigned int nbAllignes = pieceTemp->gagnant(depart, arrivee);
if(nbAllignes>3)
{
//on trace la ligne
float x1 = depart->pos().x() + depart->rect().x() + depart->rect().width()/2;
float x2 = arrivee->pos().x() + arrivee->rect().x() + arrivee->rect().width()/2;
float y1 = depart->pos().y() + depart->rect().y() + depart->rect().height()/2;
float y2 = arrivee->pos().y() + arrivee->rect().y() + arrivee->rect().height()/2;
_ligneGagnant->setLine(x1, y1, x2, y2);
scene()->addItem(_ligneGagnant);
_parent->joueurGagne(nbAllignes);
}
else
{
//on passe au tour suivant
_parent->passerTourSuivant();
}
}
void Plateau::mouseMoveEvent(QMouseEvent* event)
{
if(!_parent->partieEnCours())
return;
float deltaX = 800.0f/_largeur;
float deltaY = 800.0f/_hauteur;
unsigned int positionX = (unsigned int)(event->x()/deltaX);
//si la souris sort de la zone du plateau, fait comme si elle était sur la dernière colone
positionX = positionX >= _largeur ? positionX-1 : positionX;
// recherche de la place la plus haute non prise
unsigned int i = 0;
while(i<_grille[positionX].size() && _grille[positionX][i] == NULL)
++i;
//on en a trouvé une
if(i<_grille[positionX].size() && i>0 && _grille[positionX][i] != NULL)
{
_rectangleGris->setPos(positionX * deltaX, (i-1)*deltaY);
if(!scene()->items().contains(_rectangleGris)) //si le rectangle gris n'est pas déjà dans la scène, on l'ajoute
scene()->addItem(_rectangleGris);
}
//on en a pas trouvé, deux possibilités: la grille est vide, la grille est pleine
else if(i>=_grille[positionX].size()) //la colone est vide
{
_rectangleGris->setPos(positionX * deltaX, (i-1)*deltaY);
if(!scene()->items().contains(_rectangleGris)) //si le rectangle gris n'est pas déjà dans la scène, on l'ajoute
scene()->addItem(_rectangleGris);
}
//sinon, on ne déplace pas le carré gris
}
void Plateau::chainerPieceAvecVoisins(unsigned int x, unsigned int y)
{
//la pièce vient d'être ajoutée, elle ne peut donc avoir personne directement au dessus d'elle
//bas droite
if(x+1 < _largeur && y+1 < _hauteur)
{
_grille[x][y]->_voisinBasDroite = _grille[x+1][y+1];
if(_grille[x+1][y+1] != NULL)
_grille[x+1][y+1]->_voisinHautGauche = _grille[x][y];
}
//droite
if(x+1 < _largeur)
{
_grille[x][y]->_voisinDroite = _grille[x+1][y];
if(_grille[x+1][y] != NULL)
_grille[x+1][y]->_voisinGauche = _grille[x][y];
}
//haut droite
if(x+1 < _largeur && ((int)y)-1 >= 0)
{
_grille[x][y]->_voisinHautDroite = _grille[x+1][y-1];
if( _grille[x+1][y-1] != NULL)
_grille[x+1][y-1]->_voisinBasGauche = _grille[x][y];
}
//bas
if(y+1 < _hauteur)
{
_grille[x][y]->_voisinBas = _grille[x][y+1];
if(_grille[x][y+1] != NULL)
_grille[x][y+1]->_voisinHaut = _grille[x][y];
}
//haut gauche
if(((int)y)-1 >= 0 && ((int)x)-1 >= 0)
{
_grille[x][y]->_voisinHautGauche = _grille[x-1][y-1];
if(_grille[x-1][y-1] != NULL)
_grille[x-1][y-1]->_voisinBasDroite = _grille[x][y];
}
//gauche
if(((int)x)-1 >= 0)
{
_grille[x][y]->_voisinGauche = _grille[x-1][y];
if(_grille[x-1][y] != NULL)
_grille[x-1][y]->_voisinDroite = _grille[x][y];
}
//bas gauche
if(y+1 < _hauteur && ((int)x)-1 >= 0)
{
_grille[x][y]->_voisinBasGauche = _grille[x-1][y+1];
if(_grille[x-1][y+1] != NULL)
_grille[x-1][y+1]->_voisinHautDroite = _grille[x][y];
}
}
#ifndef RESUMEPARTIE_H
#define RESUMEPARTIE_H
#include <QtCore/QString>
class QTextStream;
class TableauScores;
/**
* Cette classe stoque un compte rendu de partie et donne le soutils pour l'écrire/lire dans/depuis un fichier
*/
class ResumePartie
{
friend class TableauScores; //pour éviter de se taper 4 accesseurs
QString _joueur1;
QString _joueur2;
QString _vainqueur;
unsigned int _nbCoups;
public:
ResumePartie();
ResumePartie(QString joueur1, QString joueur2, unsigned int nbCoups, QString vainqueur = "");
friend QTextStream& operator<<(QTextStream& flux, const ResumePartie& partie);
friend QTextStream& operator>>(QTextStream& flux, ResumePartie& partie);
};
#endif // RESUMEPARTIE_H
#ifndef TABLEAUSCORES_H
#define TABLEAUSCORES_H
#include <QtGui/QDialog>
#include <QtGui/QStandardItemModel>
#include <QtGui/QTableView>
#include <QtGui/QLabel>
#include <QtCore/QMultiMap>
#include <QtCore/QFile>
#include "ResumePartie.h"
/**
* Cette classe est la boite de dialogue montrant les scores
* Elle s'occupe aussi d'aller lire les anciens scores dans un fichier, ainsi que d'écrire les nouveaux
* Les données sont présentées dans un QTableView. (et donc utilisation du modele/view de Qt)
*/
class TableauScores : public QDialog
{
Q_OBJECT
QMultiMap<QString, ResumePartie> _parties; //chaque partie impliquant un joueur est stoquée ici, QString étant le nom du joueur
QFile _fichier;
QStandardItemModel _modele;
QTableView _vue;
QLabel _statistiques; //label donnant les statistique d'un joueur
void initDonnees();
void creerWidget();
public:
explicit TableauScores(QWidget *parent = 0);
void ajouterScore(ResumePartie partie);
public slots:
/** Affiche les statistiques du joueur sélectionné dans le label */
void afficherStatistiques(const QModelIndex& index);
protected:
void closeEvent(QCloseEvent* event);
};
#endif // TABLEAUSCORES_H
Je n'ai géré aucun destructeur car toutes les allocations dynamiques sont effectuées durant la construction de la MainWindow. (sauf cas particulier dans le TableauScore)
si vous trouvez bugs ou améliorations, je suis prenneur :=)
Oui grâce aux commentaires, on peut comprendre tout ce que tu fais (et d'ailleurs j'ai découvert de nouvelles choses à propos des possibilités de Qt en lisant des parties de ton code).
En tout cas, c'est cool d'avoir une correction pour le mode console et une pour le mode graphique.
Je vous mets ici mon code pour l'exercice Biblio++:
Biblio++ - Niveau 1
J'ai créé une classe FileMgr qui est capable de lire et écrire dans un fichier texte. Mon séparateur est le ';', ce qui permet de lire les fichiers csv (enfin je pense )
BookMgr.h:
/********************************************
contenu du fichier: Classe BookMgr
Date de creation : 01/10/2011
Description :
Classe servant a lire et ecrire dans les
fichiers de donnees du programme BIBLIO++
********************************************/
#ifndef DEF_BOOKMGR
#define DEF_BOOKMGR
#include "Main.h"
typedef struct Book
{
int ID;
std::string name;
std::string author;
std::string type;
} Book;
class BookMgr
{
public:
bool openFile(std::string fileName);
bool saveFile();
private:
std::list<Book> m_library;
std::string m_fileName;
};
#endif
BookMgr.cpp:
#include "BookMgr.h"
/*ouverture du fichier de donnees*/
bool BookMgr::openFile(std::string fileName)
{
if(fileName.empty())
{
std::cout << "Pas de nom de fichier" << std::endl;
return false;
}
m_fileName = fileName;
std::fstream fileToRead(m_fileName.c_str(), std::fstream::in);
if(!fileToRead.is_open())
{
std::cout << "impossible d'ouvrir le fichier " << m_fileName << " en lecture" << std::endl;
return false;
}
fileToRead.seekg(std::fstream::beg);
for(int i = 0; !fileToRead.eof(); i++)
{
std::string tempLine;
std::getline(fileToRead, tempLine);
if(!tempLine.empty())
{
//pour retrouver facilement le séparateur des champs de données j'utilise un istringstream
std::istringstream iss(tempLine);
m_library.push_back(Book());
//obligé d'utiliser un 2e istringstream, pour convertir le 1e champ en int (je n'ai pas trouvé d'autre moyen...)
getline(iss, tempLine, SEPARATOR);
std::istringstream iss2(tempLine);
iss2 >> m_library.back().ID;
getline(iss, m_library.back().name, SEPARATOR);
getline(iss, m_library.back().author, SEPARATOR);
getline(iss, m_library.back().type, SEPARATOR);
}
}
fileToRead.close();
return true;
}
/*sauvegarde du fichier.
Je supprime tout le contenu du fichier avant de sauver les donnees*/
bool BookMgr::saveFile()
{
std::fstream fileToSave(m_fileName.c_str(), std::fstream::out|std::fstream::trunc);
if(!fileToSave.is_open())
{
std::cout << "impossible d'ouvrir le fichier " << m_fileName << " en ecriture" << std::endl;
return false;
}
for(std::list<Book>::iterator it = m_library.begin(); it != m_library.end(); it ++)
{
std::string tempLine;
//idem que pour la lecture, je n'ai pas trouvé d'autre moyen de convertir un int en chaine qu'en passant par un ostringstream
//le fait d'integrer l'ID directement en faisant "tempLine += m_library[i].ID" rajoutait bien le chiffre, mais en tant que caractere ascii...
std::ostringstream oss;
oss << (*it).ID;
tempLine += oss.str();
tempLine += SEPARATOR;
tempLine += (*it).name;
tempLine += SEPARATOR;
tempLine += (*it).author;
tempLine += SEPARATOR;
tempLine += (*it).type;
tempLine += SEPARATOR;
tempLine += '\n';
fileToSave << tempLine;
}
return true;
}
/*ajout d'un livre*/
void BookMgr::add(std::string name, std::string author, std::string type)
{
int newID = m_library.back().ID + 1;
m_library.push_back(Book());
m_library.back().ID = newID;
m_library.back().name = name;
m_library.back().author = author;
m_library.back().type = type;
}
/*Suppression d'un livre.
puisqu'un livre a un identifiant unique, on l'efface par celui ci*/
void BookMgr::drop(int id)
{
for(std::list<Book>::iterator it = m_library.begin(); it != m_library.end(); it ++)
{
if((*it).ID == id)
{
m_library.erase(it);
break;
}
}
}
/*Modification d'un livre.
On modifie un livre en trouvant d'abord l'id.
Je modifie toutes les données d'un coup (pour le moment en tout cas ) */
void BookMgr::modify(int id, std::string name, std::string author, std::string type)
{
for(std::list<Book>::iterator it = m_library.begin(); it != m_library.end(); it ++)
{
if((*it).ID == id)
{
(*it).name = name;
(*it).author = author;
(*it).type = type;
}
}
}
/*Affichage d'un seul livre.
Pour afficher un livre unique on le cherche par son id (encore lui )
je n'affiche pas l'id puisque pour l'afficher on a deja du le renseigner, donc en toute logique il est connu*/
void BookMgr::view(int id)
{
for(std::list<Book>::iterator it = m_library.begin(); it != m_library.end(); it ++)
{
if((*it).ID == id)
{
std::cout << "titre: " << (*it).name << std::endl;
std::cout << "auteur: " << (*it).author << std::endl;
std::cout << "genre: " << (*it).type << std::endl;
break;
}
}
}
/*Affichage de tous les livres.*/
void BookMgr::viewAll()
{
/*comme la console a une taille limitee on va paginer l'affichage.
J'ai choisi d'afficher les livres par 3, je pense que c'est une taille raisonnable.
Pour le calcul du nombre de pages, vous savez que la division retourne le quotient sous forme d'entier (ici en tout cas, car j'ai choisi un int), et que le modulo renvoie le reste...
Donc si le modulo renvoie quelque chose ca veut dire qu'il y a encore une page. Donc je l'ajoute!*/
int nbPages = m_library.size() / 3;
if(m_library.size() % 3) nbPages ++;
int currentPage = 0;
do{
/*j'efface l'ecran pour y voir plus clair*/
CLEARSCREEN();
/*je positionne mon iterateur a la page demandée*/
std::list<Book>::iterator beginIt = m_library.begin();
for(int i = 0; (i <= currentPage*3) && (beginIt != m_library.end()); i++, beginIt++);
/*j'affiche mes 3 livres*/
for(int i = 0; i < 3 && beginIt != m_library.end(); beginIt ++, i++)
{
/*un petit test pour eviter les acces memoire impossibles/interdits */
if(beginIt != m_library.end())
{
std::cout << "id: " << (*beginIt).ID << std::endl;
std::cout << "\ttitre: " << (*beginIt).name << std::endl;
std::cout << "\tauteur: " << (*beginIt).author << std::endl;
std::cout << "\tgenre: " << (*beginIt).type << std::endl;
}
}
std::cout << std::endl;
/*Je cree un menu, affiché en fin de tableau, pour permettre a l'utilisateur de changer de pages, j'affiche la page courante egalement */
std::cout << "\t\t< Precedent(p) | page " << currentPage+1 << "/" << nbPages << " | ";
if(currentPage + 1 == nbPages) std::cout << "Quitter(q) >" << std::endl;
else std::cout << "Suivant(n) >" << std::endl;
/*je recupere le choix de l'utilisateur. Puis je vide le buffer (au cas ou l'utilisateur s'amuserait a taper une phrase entiere...) */
char c;
std::cin.get(c);
while(std::cin.get() != '\n');
/*enfin je verifie l'action demandee*/
if(c == 'q' || c == 'Q') return;
if((c == 'p' || c == 'P') && currentPage > 0) currentPage--;
if((c == 'n' || c == 'N') && currentPage + 1 < nbPages) currentPage++;
}while(true);
}
#include "BiblioPlusPlus.h"
/*Construction de l'objet.
J'en profite pour ouvrir le fichier de gestion des livres*/
BiblioPlusPlus::BiblioPlusPlus()
{
if(!m_library.openFile("test.txt"))
exit(EXIT_FAILURE);
}
/*affichage du menu principal.
Cette méthode retourne l'action demandée, ce qui permettra a la methode run() de savoir quelle fonction appeler*/
int BiblioPlusPlus::mainMenu()
{
do{
CLEARSCREEN();
std::cout << "Menu:" << std::endl << std::endl;
std::cout << "\ta - Ajouter un livre a la base" << std::endl;
std::cout << "\td - Enlever un livre a la base" << std::endl;
std::cout << "\tm - Modifier un livre a la base" << std::endl;
std::cout << "\tv - Afficher tous les livres de la base" << std::endl;
std::cout << "\tq - Quitter" << std::endl;
std::cout << "Votre choix:";
char c;
std::cin.get(c);
while(std::cin.get() != '\n');
switch(c)
{
case 'a':
case 'A':
return ADD_ACTION;
case 'd':
case 'D':
return DROP_ACTION;
case 'm':
case 'M':
return MODIFY_ACTION;
case 'v':
case 'V':
return VIEW_ACTION;
case 'q':
case 'Q':
return QUIT_ACTION;
}
}while(true);
}
/*ajout d'un livre.
On demande a l'utilisateur de rentrer les infos sur le livre a ajouter, puis on appelle la methode correspondante*/
void BiblioPlusPlus::addAction()
{
CLEARSCREEN();
std::cout << "ajout d'un livre" << std::endl;
std::string name;
std::cout << "\tTitre du livre:";
std::getline(std::cin, name);
std::string author;
std::cout << "\tAuteur:";
std::getline(std::cin, author);
std::string type;
std::cout << "\tGenre:";
std::getline(std::cin, type);
m_library.add(name, author, type);
}
/*suppression d'un livre.
On demande de saisir l'identifiant du livre, c'est par celui ci qu'on le retrouvera.
Si l'utilisateur ne connait pas l'identifiant du livre, alors il devra d'abord le retrouver*/
void BiblioPlusPlus::dropAction()
{
CLEARSCREEN();
std::cout << "suppression d'un livre" << std::endl;
int id;
std::cout << "Entrer l'identifiant du livre a supprimer." << std::endl << "Si vous ne le connaissez pas, consultez la liste complete des livres enregistres." << std::endl;
std::cout << "Id du livre:";
char c = std::cin.peek();
if(c >= '0' && c <= '9')
{
std::cin >> id;
m_library.drop(id);
}
}
/*Modification d'un livre.
Comme pour la suppression on retrouve le livre a modifier par son identifiant.
Ensuite on demande quelles infos modifier.*/
void BiblioPlusPlus::modifyAction()
{
CLEARSCREEN();
std::cout << "modification d'un livre" << std::endl;
int id;
std::cout << "\tEntrer l'identifiant du livre a supprimer." << std::endl << "Si vous ne le connaissez pas, consultez la liste complete des livres enregistres." << std::endl;
std::cout << "Id du livre:";
char c = std::cin.peek();
if(c >= '0' && c <= '9')
{
std::cin >> id;
while(std::cin.get() != '\n');
m_library.view(id);
std::cout << "Veuillez entrer les informations a modifier." << std::endl << "Si vous ne desirez pas modifier un champ, appuyez simplement sur entree." << std::endl;
std::string name = "";
std::cout << "\tTitre du livre:";
if(std::cin.peek() != '\n') std::getline(std::cin, name);
std::string author = "";
std::cout << "\tAuteur:";
if(std::cin.peek() != '\n') std::getline(std::cin, author);
std::string type = "";
std::cout << "\tGenre:";
if(std::cin.peek() != '\n') std::getline(std::cin, type);
m_library.modify(id, name, author, type);
}
}
/*affichage de tous les livres*/
void BiblioPlusPlus::viewAction()
{
m_library.viewAll();
}
/*lancement de l'application.*/
int BiblioPlusPlus::run()
{
bool running = true;
do{
switch(mainMenu())
{
case ADD_ACTION:
addAction();
break;
case DROP_ACTION:
dropAction();
break;
case MODIFY_ACTION:
modifyAction();
break;
case VIEW_ACTION:
viewAction();
break;
case QUIT_ACTION:
running = false;
break;
}
}while(running);
m_library.saveFile();
return EXIT_SUCCESS;
}
Main.h:
/*partie servant a introduire facilement la fonction clear de chaque OS*/
#define WINDOWS_OS
#ifdef WINDOWS_OS
#define CLEARSCREEN() system("cls")
#endif
#ifdef LINUX_OS
#define CLEARSCREEN() system("clear")
#endif
L'affichage de la liste complète des livres demande un peu plus de réflexion, mais cela reste assez simple si on prend un papier, un crayon, et un peu de matière grise
Conclusion
Voili voilou, je suis ouvert à tout commentaire quant à l'amélioration de ce code
PS : Je pense que dans l'énoncé de l'exercice il faudrait ajouter une astuce, ou une piste, pour la conversion d'un entier en chaîne (j'ai un peu galéré mais j'ai trouvé ma solution quand même).
Ca serait vraiment bien pour aider un peu un débutant qui patauge à ce niveau...
EDIT: J'ai remplacé le std::vector par un std::list, dans la classe BookMgr, qui correspond mieux à l'exercice (de mon point de vue )
J'ai aussi ajouté une classe BiblioPlusPlus, qui sert en fait à gérer le programme en général: il récupère les infos de l'utilisateur et déclenche les actions en conséquence.
PS : Je pense que dans l'énoncé de l'exercice il faudrait ajouter une astuce, ou une piste, pour la conversion d'un entier en chaîne (j'ai un peu galéré mais j'ai trouvé ma solution quand même).
Ca serait vraiment bien pour aider un peu un débutant qui patauge à ce niveau...
× 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.
Cours de C++ moderne
Cours de C++ moderne
Cours de C++ moderne