Examen de Conception et Programmation orientées objets

École Nationale des Sciences de l'Informatique (ENSI)
1/12
100%
Rendu du PDF...
Page 1 sur 12Lecteur de document UniversityLib

Examen de Conception et Programmation orientées objets

École Nationale des Sciences de l'Informatique (ENSI) · Informatique, Programmation · exam

Voir tous les documents en programmation

UNIVERSITE DE LA MANOUBA

-----¤¤¤¤-----

ECOLE NATIONALE DES SCIENCES

DE L'INFORMATIQUE

ANNEE UNIVERSITAIRE

2008-2009

Examen 1er semestre

(Session Principale – Janvier 2009)

Matière : Conception et Programmation orientées objets

Niveau : II2

Enseignants : Y. JAMOUSSI, I. BEN HAMOUDA, G. ZIADA, S. MTIBAA

Date : mardi 6 janvier 2009

Durée: 2H

Documents : non autorisés

• Nombre de pages = Sujet (2 pages) + 3 Formulaires

• Il est fortement conseillé de lire toutes les questions avant de commencer…

NB :

Problème

Le but de ce problème est de suivre l’évolution d’un jeu se jouant moyennant les règles du jeu

d’échecs citées dans ce qui suit. On rappelle que le jeu d’échecs se joue à deux joueurs (jouant à tour

de rôle) qui font évoluer des pièces, respectivement blanches et noires, sur un échiquier de 8x8 cases.

Les pièces pour chaque joueur pouvant être un mélange de ces catégories : pions, roi, dame,

cavaliers, fous, tours. Les huit lignes « verticales » sont appelées colonnes. Les huit lignes

« horizontales » sont appelées rangées. Les lignes obliques à 45° sont appelées diagonales.

Les colonnes peuvent être repérées pas des lettres

minuscules : de ‘a’ à ‘h’, et les rangées par des

chiffres : de 1 à 8. Chaque case est ainsi repérée par

un couple colonne-rangée, par exemple : e5. Une

case contient au maximum une pièce. Chaque pièce

est positionnée sur une case ou hors de l’échiquier

si elle est capturée. La disposition initiale des

pièces sur l’échiquier est illustrée sur la figure à

droite. Toutefois, le jeu peut démarrer par une

disposition quelconque de pièces comportant au

moins un roi pour chaque joueur.

Lors de son tour, un joueur effectue un coup. Ce

coup porte sur une de ses pièces sur l’échiquier et

modifie sa position vers une nouvelle position. Un

coup peut entraîner la capture d’une pièce de

l’adversaire. La pièce capturée finit ainsi à

l’extérieur de l’échiquier.

Disposition initiale des pièces

Quand un coup mène à une position qui menace le roi adverse de prise au prochain coup, ce roi est en

échec. Le jeu consiste à faire une séquence de coups alternée entre les deux joueurs jusqu’à l’échec et

mat. Le mat est une situation dans laquelle le roi n’a pas d'échappatoire de l’échec.

Le déplacement de chaque pièce est comme suit :

• Le pion se déplace droit devant lui d'une case à chaque coup. La première fois qu'il se

déplace, il peut avancer de deux cases. Il convient de mentionner que les pions blancs (resp.

noirs) évoluent de manière croissante (resp. décroissante) sur les rangées.

1/2

• Le roi se déplace d'une case dans n'importe quelle direction. Il n'a toutefois pas le droit de se

mettre en échec.

• La dame, la tour et le fou sont des pièces à longue portée, c'est-à-dire qu'elles peuvent se

déplacer d'autant de cases qu'elles le souhaitent, en ligne droite, en n'étant limitées que par

l'obstacle infranchissable que constitue toute autre pièce (adverse ou non).

o La tour se déplace en suivant colonnes ou rangées ;

o Le fou se déplace en suivant les diagonales ;

o La dame combine les deux (la tour et le fou).

• Le cavalier est une pièce sauteuse (si sa case d'arrivée est jouable, il peut y accéder quel que

soit l'encombrement environnant). Le cavalier se déplace sur une des cases se trouvant sur la

colonne voisine et la rangée après la voisine ou sur la rangée voisine et la colonne après la

voisine (ce qui lui donne 8 possibilités de mouvement dans le meilleur des cas).

Par ailleurs, toutes les pièces, sauf les pions, capturent comme elles se déplacent : l'obstacle sur le

Publicité

trajet constitué par une pièce adverse est accessible avec capture de cette pièce adverse. Les pions ont

un mode de prise particulier : ils capturent en avançant d'une case en diagonale.

Comme l’objectif fonctionnel est de représenter une situation de jeu pour les 2 joueurs ainsi que son

évolution au cours du temps, un développeur a proposé le diagramme de classes UML incomplet

suivant :

Travail à faire

On se propose de comprendre, compléter améliorer la modélisation UML proposée par le

développeur et de coder en partie le jeu avec le langage C++. Pour cela, il s’agit de répondre

directement sur les 3 formulaires qui vous ont été distribués.

2/2

Partie 1 : Modélisation UML

Question 1.1 (3 pt)

20

Le diagramme de classes proposé par le développeur est incomplet. Parmi les

manques nous signalons : (1) le manque de cardinalités de l’association reliant la

classe ‘Piece’ à la classe ‘Case’ – (2) le manque d’associations entre la classe

‘Coup’ et les autres classes du diagramme – (3) l’absence de liens unidirectionnels.

a) Complétez ces trois manques sur cette portion de diagramme de classes.

b)

Indiquez, en commentant éventuellement, l’ordre de navigation nécessaire de ou des

associations vous permettant de :

Savoir si une case est jouable ou non pour une pièce donnée.

Pièce – case – Echiquier – (case)*

Savoir si une pièce est en risque d’attaque par une pièce de l’adversaire.

Pièce – case – Echiquier – (case)* – Pièce

1/10

Question 1.2 (1 pt)

Elaborez le diagramme d’objets conformément au diagramme de classes proposé par le

développeur reflétant la disposition initiale du jeu d’échecs illustrée par la figure ci-dessous.

Concernant les objets de type ‘Case’ :

Pour des raisons de clarté, on vous demande de

représenter que les cases occupées.

2/10

Question 1.3 (2 pt)

Elaborez le diagramme d’objets conformément au diagramme de classes proposé par le développeur

et aux modifications que vous avez apportées dans la question (1.1-a) reflétant une évolution du jeu

d’échecs de la question précédente (Q1.2) de deux coups illustrée par la figure ci-dessous.

Concernant les objets de

type ‘Case’ :

Pour des raisons de clarté,

on vous demande de

représenter que les cases

occupées dans la

disposition initiale du jeu

d’échecs (Q 1.2).

3/10

Question 1.4 (1 pt)

Pour comprendre les règles de gestion gouvernant l’évolution des liens entre les objets, on se

propose de décorer les associations par les contraintes {ordered} , {addOnly} , {frozen}, {notUnique}.

Donnez pour chacune de ces associations les contraintes appropriées.

JeuEchecs-Piece : {addOnly} , catégoriquement non {notUnique}

JeuEchecs-Coup : {ordered}, catégoriquement non {frozen}

Echiquier-Case : {frozen}, catégoriquement non {notUnique}, {addOnly}

Partie 2 : Codage C++

Question 2.1 (1 pt)

L’implémentation des associations avec cardinalité multiple nécessite l’utilisation d’une structure

qui implémente une collection (non définie dans le modèle UML proposé par le développeur).

Sachant qu’on souhaite utiliser des tableaux statique C++ et/ou les collections STL (sans

exagération), proposez les différentes structures que vous comptez utiliser pour les différentes

associations multiples ci-dessous indiquées. Commentez brièvement vos choix.

JeuEchecs-Piece :

Collection qcq (séquentielle ou non) => Exple : Set, vector, …

JeuEchecs-Coup :

Publicité

Collection ordonnée => Exple vector, List

Echiquier-Case : Collection indéxé =>

  • Tableau bidimentionnel de case, car la taille est connue à l’avance
  • pourquoi pas aussi un map !!

Question 2.2 (2 pts)

Une pièce peut se déplacer de sa case vers une autre case en capturant éventuellement une autre

pièce. A cet effet, on se propose de définir deux méthodes virtuelles pures ‘peutSeDeplacer’ et

‘peutCapturer’ ayant comme argument une case cible et retournant une valeur booléenne indiquant

la validité de l’action. Donnez le code C++ de la classe ‘Piece’ conformément à vos réponses à la

question 1.1.a) :

classe Piece {

public:

// Constructeur à compléter : interface + implémentation

// Symbole = {Pion, Roi, Dame, Fou, Tour, Cavalier} ; Couleur = {Blanc, Noir}

Piece(Symbole symbole, Couleur couleur, Case * caseInitiale = NULL)

: symbole(symbole), couleur(couleur)

{

dans = caseInitiale ;

}

virtual bool peutSeDeplacer(Case *) = 0 ;

4/10

virtual bool peutCapturer(Case * c) { return peutSeDeplacer(c); }

// les méthodes : interface + implémentation

setCase( Case * c) { dans = c ; }

Case * getCase() const { return dans ; }

Couleur getCouleur() const { return couleur ; }

Symbole getSymbole() const { return symbole ; }

// les attributs

private :

Symbole symbole ;

Couleur couleur

Case * dans ;

};

Question 2.3 ( 2 pts)

Dans le diagramme de classes proposé par le développeur, la classe ‘Case’ ne comporte pas de

coordonnées (colonne et rangée).

a) Discuter brièvement l’avantage et l’inconvénient de la mémorisation ou non mémorisation

des coordonnées dans la classe ‘Case’.

Mémoriser coordonnées de case :

Avantage : une pièce peut connaître sa position en consultant

la case où elle se trouve.

Inconvénient : redondance des coordonnées car l’échiquier doit

indexer les cases par leur coordonnée.

Non mémorisation des coordonnées de case :

Avantage(s) : - le code de case est léger

  • Pas de redondance

Inconvénient : - il faut passer par la classe Echiquier pour savoir

les coordonnées d’une case => parcourir la

collection des cases pour retrouver les coordonnées

5/10

b) Donnez le code C++ de l’interface de la classe ‘Case’ conformément à vos choix (surtout les

réponses aux questions 1.1.a et 2.3.a).

class Case {

public :

// méthodes Obligatoire

Case(Echiquier * e) ;

setPiece (Piece * p) ;

Piece * getPiece() ;

// méthodes dépendants des réponses 1.1a) et 2.3a)

Echiquier * getEchiquier() ;

S’il y a les coordonnées de la case => ajouter les méthodes appropriées

// les attributs

private :

Piece * contient ;

Publicité

Echiquier * ech ; // pour permettre la navigation de 1.1b)

// il est possible de faire la déclaration en static

=> le constructeur est sans arguments

// au choix => les coordonnées de la Case

} ;

Question 2.4 (1,5 pts)

Donnez le code C++ de l’interface de la classe ‘Coup’ conformément à vos réponses à la question

1.1.a) sachant qu’il peut s’agir d’un déplacement avec ou sans prise d’une pièce adverse.

class Coup {

public:

// Constructeur et méthodes à compléter : interface

Coup(Case de, Case vers, Piece pb, Piece pc) ;

+ accesseurs aux attributs

// Les attributs

private :

Case * de ;

Case * vers ;

Piece * piece_bougee ;

Piece * piece_capturee ;

} ;

6/10

Question 2.5 (1,5 pts)

Les méthodes ‘estOccupe’ et ‘placer’ assurent respectivement le test d’occupation et le placement

d’une pièce sur l’échiquier. Donnez le code C++ de l’interface de la classe ‘Echiquier’

conformément à vos réponses à la question 1.1.a). Donnez uniquement le corps du constructeur.

class Echiquier {

public:

// Constructeur à compléter : interface + implémentation

Echiquier( ) {

for (int i=0 ; i<8 ; i++)

for (int j=0 ; j<8 ; j++) tab[i][j] = new Case(this) ;

// a revoir corps si la déclaration est : Case tab[8][8]

}

// les interfaces des méthodes + un commentaire par interface

Case * getCase(char lig, char col) ;

bool estOccupe(Case *) ;

void initialiser() ;

char getLigne(Case *) ;

char getColonne(Case *) ;

~Echiquier() ;

// les attributs

private:

Case * tab[8][8] ;

// un attribut jeu s’il y a navigation de Echiquier à JeuEchecs

} ;

Question 2.6 (1 pts)

En utilisant la syntaxe c++, donnez les attributs de la classe ‘JeuEchecs’. Indiquez comment vous

distinguez les rois des deux joueurs.

class JeuEchecs {

...

// Les attributs

private :

Echiquier ech ;

Vector<Piece *> pieces ;

Vector< Coup *> coups ;

Couleur TourDeQui ;

// on peut distinguer les rois par une position fixe dans le vecteur

// ou en ajoutant 2 pointeurs qui pointent sur les 2 rois

} ;

Question 2.7 (2 pts)

7/10

La méthode ‘jouer’ de la classe ‘JeuEchecs’ donne au joueur qui a son tour la main pour jouer. Elle

lui demande une ancienne et une nouvelle position. Si le coup est valide, il sera ajouté à la séquence

Publicité

de coups et la pièce sera effectivement déplacée avec éventuellement la capture nécessaire. La

méthode ‘jouer’ retourne un booléen indiquant si le coup est valide ou non.

bool JeuEchecs::jouer() {

// soit (l1,c1) & (l2,c2) ancienne position & nouvelle position

cin >> l1 >> c1 >> l2 >> c2 ;

Case source = getCase(l1,c1); Case cible = getCase(l2,c2) ;

Piece * piece_bougee = source->getPiece() ;

if ((piece_bougee==NULL) || (piece_bougee->getCouleur() != TourDeQui))

return false

Piece * piece_Capturee = cible->getPiece() ;

if ( piece_bougee->peutSeDeplacer( cible ) ||

piece_bougee->peutCapturer( cible ) ) {

coups.push_back( new Coup(getCase(l1,c1), getCase(l2,c2),

getCase(l1,c1)->getPiece(),

getCase(l2,c2)->getPiece()) ) ;

source->setPiece(NULL) ; cible->setPiece(piece_bougee);

piece_bougee->setCase(cible) ;

if ( piece_Capturee != NULL ) piece_capturee->setCase(NULL) ;

return true ;

}

else return false ;

}

Question 2.8 (2 pts)

On se propose d’écrire la classe ‘Pion’ conformément à vos réponses aux questions précédentes.

a) Avez-vous besoin d’attributs spécifiques ? Justifiez votre réponse.

// Oui

// car le déplacement d’un pion pour la 1ère fois diffère des autres

// ==> il faut mémoriser si le pion a déjà fait un déplacement

b) Donnez le corps du constructeur, le corps de la méthode ‘deplacer’.

// Constructeur à compléter : interface + implémentation

8/10

Pion::Pion(Couleur couleur, Case * c) : Piece(couleur, ‘Pion’,c)

{

aFaitDeplacement = false ;

}

// à compléter l’implémentation

bool Pion::peutSeDeplacer(Case * cible) {

Case * source = getCase() ;

ech = source->getEchiquier() ;

char l1 = ech->getLigne(source) ;

char c1 = ech->getColonne(source) ;

char l2 = ech->getLigne(cible);

char c2 = ech->getColonne(cible) ;

if ( cible->getPiece() != NULL) return false ;

if (c1 != c2) return false

if ( aFaitDeplacement )

if ( couleur == blanc ) return ((l2-l1)==1) ;

else return (l1-l2)==1 ;

else if ( couleur == blanc ) return (((l2-l1)==1) ||((l2-l1)==2)) ;

else return ((l1-l2)==1) ||((l1-l2)==2)) ;

}

Question 2.9 (1 pts)

9/10

On se propose de réutiliser les classes ‘Tour’ et ‘Fou’ pour l’écriture de la classe ‘Dame’. Expliquez

comment ? Donner le corps de la méthode ‘peutSeDeplacer’ de la classe ‘Dame’.

Il s’agit de faire un héritage multiple de Tour et Fou

bool Dame::peutSeDeplacer(Case * c)

{

return ( Tour::peuSeDeplacer(c) || Fou::peuSeDeplacer(c) ) ;

}

10/10