Beruflich Dokumente
Kultur Dokumente
Rapport écrit
Groupe B4
Rédigé le 18/06/10
Sommaire
1) Introduction
3) Implantation
1) Définition précise des types plateau, element_plateau
2) Définition précise des types sequence, coup_joue
3) Explication détaillée de l’initialisation du plateau
4) Schéma explicatif du sous-programme PointerCase
5) Ecriture en pseudo-langage du sous-programme Deplacement
6) Explications détaillées des sous-programmes permettant de gérer
l’interface console
4) Conclusion
A cela s’ajoutent les différentes règles d’Abalone (Sumito, Pac, mouvement en ligne…) qui
restreignent les choix de déplacement des boules. Le joueur noir commence toujours la partie et la
partie s’arrête dès qu’un joueur a réussi à éjecter 6 boules adverses.
Nous avons choisi la 1ère représentation de programmation conseillée dans le sujet, c’est-à-
dire représenter toutes les cases du plateau sous forme d’une liste chaînée. Ainsi chaque case
comporte 6 pointeurs afin de pouvoir accéder aux cases adjacentes. Les bords du plateau sont
représentés par un pointeur NULL. L’accès au plateau se fait via le pointeur de tête positionné en A1.
Enfin, le joueur pourra déplacer la boule en cliquant sur celle-ci puis sur la position finale de la boule.
Lorsque nous nous sommes engagés sur la représentation de toutes les cases du
plateau, la 1ère question a été de se demander de quels sous-programmes nous aurions
besoin afin de relier toutes les cases du plateau entre elles pour créer un plateau vide. Le
plateau étant de forme hexagonale, il fallait trouver un moyen de rentrer toutes les cases
dans une liste chaînée sans alourdir le programme. Nous avons donc créé les sous
programmes CreerLigneVide, AjouterLignePlateau et CréerPlateauVide décrits dans la partie
Implantation.
Après avoir placé les boules dans l’état initial du jeu, nous nous sommes intéressés
aux différentes conditions de déplacement imposées par les règles du jeu :
Le joueur doit cliquer sur une boule de sa couleur
La règle du jeu impose de plus que chaque joueur ne peut effectuer qu’un
mouvement par tour. Les déplacements du joueur noir puis du joueur blanc ont donc été
insérés dans une boucle qui s’arrête lorsque la condition de victoire a été validée. Le sous-
programme CompteurBoulesEjectees permet de savoir le nombre de boules éjectées pour
chacun des joueurs. Il est ensuite utilisé par le sous-programme Victoire qui permet de
terminer la partie lorsque 6 boules d’une même couleur ont été éjectées.
Il ne restait plus ensuite qu’à créer des menus et modifier l’apparence de l’interface afin que
celle-ci soit ergonomique.
2) Initialisation du plateau : définition des types plateau, element_plateau
Le type plateau correspond au pointeur qui permet d’accéder à toutes les cases du plateau.
Element_plateau correspond à une case du plateau. Elle est définie par ses coordonnées
(abscisse et ordonnée en chiffre), la présence d’une boule noire, blanche ou non et enfin les
6 cases adjacentes. Pour plus de clarté, nous les avons nommées comme des points
cardinaux.
Nord-Ouest Nord-Est
Ouest Est
Sud-Ouest Sud-Est
Le type sequence correspond au pointeur qui permet d’accéder à tous les coups joués de la
partie précédente.
Le type coup_joue correspond à un coup joué contenu dans la séquence. Il est défini par les
coordonnées de départ et d’arrivée (abscisse et ordonnée en chiffre) et le coup joué suivant.
/* Initialisation du plateau */
/* Actions de l'utilisateur */
void Deplacement (int Da, int Do, int Aa, int Ao, plateau &P, int
couleuractif, int couleurpassif, int &r);
//Déplace la boule sur le plateau en fonction du déplacement choisi
par l'utilisateur
/* Fin de partie */
III. Implantation
1) Définition précise des types plateau, element_plateau
L’initialisation du plateau a été la partie la plus difficile de la programmation car elle fait
intervenir de nombreux pointeurs. Nous allons expliquer ci-dessous le raisonnement effectué
pour créer le plateau avec les boules à l’état initial.
La 1ère étape a été d’imaginer un processus itératif permettant de relier le plus de cases
possibles entre elles grâce aux 6 pointeurs adjacents. Nous avons alors remarqué que chaque
ligne du plateau avait les mêmes caractéristiques mises à part leur longueur qui varie suivant
l’ordonnée. En effet chaque ligne commence par une case dont le pointeur Ouest est NULL et
se termine par une case dont le pointeur Est est NULL. Entre ces 2 cases, toutes les cases
sont reliées par le fait que le pointeur Est correspond au pointeur Ouest de la case adjacente
droite et le pointeur Ouest correspond au pointeur Est de la case adjacente gauche.
Le pointeur courant sert à se déplacer sur la ligne à partir du pointeur P. Le pointeur courant2
va servir à relier les pointeurs Est et Ouest entre eux. On crée ensuite chaque case de la ligne
en définissant leurs coordonnées, leur valeur à 0, et les pointeurs Est, Nord-Est, Nord-Ouest.
for (i=1;i<=longueur;i++) {
(*courant).E=new element_plateau;
(*courant).abscisse=i;
(*courant).ordonnee=ord;
(*courant).valeur=0;
(*courant).NO=new element_plateau;
(*courant).NE=new element_plateau;
(*courant).NO=NULL;
(*courant).NE=NULL;
Les cases des lignes 1 et 9 sont des cas particuliers car elles se trouvent sur le bord du plateau.
On peut donc mettre certains de leurs pointeurs comme renvoyant sur NULL.
if (ord==1) {
(*courant).SO=new element_plateau;
(*courant).SO=NULL;
(*courant).SE=new element_plateau;
(*courant).SE=NULL;
}
if (ord==9) {
(*courant).abscisse=4+i;
(*courant).NO=new element_plateau;
(*courant).NO=NULL;
(*courant).NE=new element_plateau;
(*courant).NE=NULL;
}
On traite ensuite les cases sur le bord du plateau suivant leur ordonnée (inférieure à 5, égale
à 5 puis supérieure à 5). De même, certains de leurs pointeurs renvoient sur NULL.
if (ord<5){
if (i==1) {
(*courant).O=new element_plateau;
(*courant).O=NULL;
(*courant).SO=new element_plateau;
(*courant).SO=NULL;
}
if (i==longueur) {
(*courant).SE=new element_plateau;
(*courant).SE=NULL;
(*courant).E=NULL;
}
}
if (ord==5) {
if (i==1) {
(*courant).O=new element_plateau;
(*courant).O=NULL;
(*courant).SO=new element_plateau;
(*courant).SO=NULL;
(*courant).NO=new element_plateau;
(*courant).NO=NULL;
}
if (i==longueur) {
(*courant).SE=new element_plateau;
(*courant).SE=NULL;
(*courant).E=new element_plateau;
(*courant).E=NULL;
(*courant).NE=new element_plateau;
(*courant).NE=NULL;
}
}
if (ord>5) {
(*courant).abscisse=i+ord-5;
if (i==1) {
(*courant).O=new element_plateau;
(*courant).O=NULL;
(*courant).NO=new element_plateau;
(*courant).NO=NULL;
}
if (i==longueur) {
(*courant).NE=new element_plateau;
(*courant).NE=NULL;
(*courant).E=new element_plateau;
(*courant).E=NULL;
}
}
Il ne reste plus qu’à relier les cases avec les pointeurs Est et Ouest.
courant=(*courant).E;
if (courant!=NULL) {
(*courant).O=courant2;
courant2=(*courant2).E;
}
}
return P;
}
Il faut maintenant relier chaque ligne entre elles. On va alors les concaténer en remarquant
que les pointeurs Nord-Ouest, Nord-Est, Sud-Est, Sud-Ouest s’agencent entre 2 lignes comme le
schéma ci-dessous :
Nord-Ouest=Sud-Est Nord-Est=Sud-Ouest
if (ligne<=5){
while ((*courant).NO!=NULL) {
courant=(*courant).NO;
}
while((*courant2).E!=NULL) {
(*courant).NO=courant2;
(*courant2).SE=courant;
courant2=(*courant2).E;
(*courant2).SO=courant;
(*courant).NE=courant2;
courant=(*courant).E;
}
}
…ou de 5 à 9 :
else {
while ((*courant).NO!=NULL) {
courant=(*courant).NO;
}
while ((*courant).NE!=NULL) {
courant=(*courant).NE;
}
while((*courant).E!=NULL){
(*courant).NE=courant2;
(*courant2).SO=courant;
courant=(*courant).E;
(*courant2).SE=courant;
(*courant).NO=courant2;
courant2=(*courant2).E;
}
}
return fixe;
}
Afin de placer des pointeurs courant sur des cases précises dans les sous-programmes de
condition et de déplacement, nous avons eu besoin de créer ce sous-programme. Voici
comment le pointeur se déplace à la case désirée :
Il y a 2 cas majeurs :
Entier couleuractif
Plateau P
Entier Da Procédure
Entier Do
Entier Aa Deplacement
Entier Ao
Entier r
Entier couleurpassif
La première partie de ce sous programme dispose les 5 lignes horizontales du haut du plateau. Pour
la première ligne du haut (i=9), on fait déplacer le pointeur sur la première case de cette ligne, de
coordonnées (5,9), à l’écran on affiche 10 espaces, puis on fait déplacer vers l’Est le pointeur en
affichant à l’écran la valeur de chaque case (voir sous-programme AfficherCase) :
for (i=4;i>=1;i--) {
nouveau=PointerCase(1,i,P);
Espace (j+6);
j=j+1;
while (nouveau!=NULL) {
AfficherCase (nouveau);
nouveau=(*nouveau).E;
}
cout<<endl;
}
cout<<endl<<endl<<endl;
}
La deuxième partie dispose les 4 lignes horizontales du bas du plateau. On procède de la même façon
que dans la première partie, en partant de la ligne 4 jusqu’à la ligne 1, sauf qu’on augmente l’espace
à chaque ligne pour avoir la forme trapézoïdale. Ce qui nous donne bien à la fin un hexagone de coté
5.
Cependant, un problème surgit : les coordonnées des cases du plateau ne correspondent pas aux
coordonnées de la console. D’où l’obligation de créer un sous-programme ConversionConsole
reliant les coordonnées acquises dans la console à celles du plateau. Le plateau est affiché à l’écran
dans le repère console tel que le schéma ci-dessous le représente :
1 2 3 4 5 6 7
Y
On constate assez facilement que pour les ordonnées, la relation est :
Ordonnée_plateau = 13 – Ordonnée_console
Mais la règle reliant l’abscisse plateau à l’abscisse console est plus implicite. Remarquons que la
rangée d’abscisse 1 du plateau peut être assimilée à une droite affine dans le repère console
d’équation : Y = X + 2, pour la rangée d’abscisse 2 du plateau : Y = X. On constate que toutes les
droites assimilées aux rangées de 1 à 9 sont parallèles, donc par récurrence on établit une loi
générale pour chaque rangée : Y = X + i, avec i variant de 2 à -14 par pas de 2. D’où le sous-
programme ConversionConsole :
L’abscisse plateau correspond alors à j, où j est égal au numéro de la ligne rouge sur le dessin du
plateau dans le repère console.
return b;
}
Ce sous-programme permet d’éviter au programme de planter lorsqu’un joueur clique sur une case
non-définie comme l’extérieur du plateau ou un espace horizontal entre 2 cases. Les conditions
écrites dans la 1ère boucle « if » délimitent la zone intérieure du plateau dans le repère console. De
plus, on remarque dans le dessin du plateau dans le repère console ci-dessus, que les cases du
plateau (symbolisées par un point sur le schéma) ont soit leur abscisse et leur ordonnée paires ou
soit leur abscisse et ordonnée impaires. On définit donc ces 2 autres conditions dans les 2ème et 3ème
boucles « if » grâce à l’utilisation du modulo. Ainsi, si un joueur clique à l’extérieur du plateau ou
dans un espace vide à l’intérieur du plateau, la fonction renvoie faux, ce qui évite au programme
principal de récupérer des coordonnées inconnues et donc de planter.
IV. Conclusion
Au final, la réalisation de ce projet nous a demandé beaucoup de temps mais nous a apporté
en contrepartie une grande dose d’expérience sur l’utilisation du langage de programmation C++.
Notamment l’initialisation du plateau nous a permis de bien cerner la définition des pointeurs et leur
rôle dans les listes chainées. Ce mini-projet nous semble maintenant réellement complémentaire du
cours et des travaux dirigés étudiés à l’école. Nous aurions cependant souhaité commencer plus tôt
la réalisation de ce projet car il nous a manqué du temps pour achever complètement le jeu Abalone.
Il serait bien en effet de rajouter plus tard les mouvements en flèche, dernière règle du jeu d’Abalone.
Au niveau de l’interface, nous aurions aimé aussi pouvoir agrandir la taille des caractères car les
caractères qui représentent les boules sont trop petits et ne facilitent pas le pointage des boules.
Enfin, une extension possible du jeu que l’on aurait aimé souhaiter faire serait la possibilité de
pouvoir jouer en réseau à Abalone, soit en réseau local ou encore via Internet.
SequenceVide
AjouterEnFin
ParcourirSequence
NombreCoupsJoues
ConvertirChiffreLettre
Visionnage
EnregistrementSequence
ChargementSequence
InitialisationConsole
CouleurtexteConsole
DeplaceCurseur
EffaceConsole
Init_aleatoire
Aleatoire
Attendre
Effacer_ecran