-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPlateau.cpp
More file actions
490 lines (383 loc) · 14.8 KB
/
Copy pathPlateau.cpp
File metadata and controls
490 lines (383 loc) · 14.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
/**
* Projet final :: INF1015 :: Livrable 3. Fichier qui définit les fonctions pour le plateau de jeu.
* \file Plateau.cpp
* \author Charles De Lafontaine & Benoit Dambrine & al.
* \date 9 mai 2021
* Créé le 31 mars 2021
*/
#include "Modele.h"
/**
* Le plateau (échiquier).
* \param [const Couleur&] tourInitial <Le tour initial des joueurs.>
*/
modele::Plateau::Plateau(const Couleur& tourInitial) :
tour_(tourInitial)
{}
/**
* Création d'une case pour le plateau.
* \param [const string&] nom <Le nom de la case.>
* \param [bool] estOccupee <Indique si la case est occupée par une pièce.>
* \return [unique_ptr<Case>] <Le pointeur unique vers la case créée.>
*/
std::unique_ptr<modele::Case> modele::Plateau::creerCase(const std::string& nom, bool estOccupee) const
{
return make_unique<Case>(nom, estOccupee);
}
/**
* Création d'une pièce pour le plateau.
* \param [char] caractere <Le nom de la pièce à créer selon le caractère émis.>
* \return [unique_ptr<Piece>] <Le pointeur unique vers la pièce créée.>
*/
std::unique_ptr<modele::Piece> modele::Plateau::creerPiece(char caractere) const
{
using namespace std;
if (caractere == CARACTERE_CAVALIER_NOIR)
return make_unique<Cavalier>(Couleur::Noir);
else if (caractere == CARACTERE_CAVALIER_BLANC)
return make_unique<Cavalier>(Couleur::Blanc);
else if (caractere == CARACTERE_TOUR_NOIRE)
return make_unique<Tour>(Couleur::Noir);
else if (caractere == CARACTERE_TOUR_BLANCHE)
return make_unique<Tour>(Couleur::Blanc);
else if (caractere == CARACTERE_ROI_NOIR) {
++nRois_;
return make_unique<Roi>(Couleur::Noir);
}
else {
++nRois_;
return make_unique<Roi>(Couleur::Blanc);
}
}
/**
* Réinitialise le compte du nombre de rois sur l'échiquier.
*/
void modele::Plateau::reinitialiserNRois()
{
nRois_ = 0;
}
/**
* Crée le plateau en appelant plusieurs fonctions définies plus haut.
* \param [const string&] configuration <Le jeu d'échecs que l'on souhaite obtenir sous forme d'une chaîne de caractères.>
* \return [bool] <Indique si le plateau a été créé avec succès.>
*/
void modele::Plateau::creer(const std::string& configuration)
{
using namespace iter;
int x = 0, y = 0;
for (size_t i : range(configuration.length())) {
if (nRois_ > LIMITE_CONFRONTATIONS_ROIS)
throw NombreRoisExact(ERREUR_TROP_DE_ROIS);
if (configuration[i] == CARACTERE_ESPACE) {
if (configuration[i + 1] == DEBUTER_NOIR)
inverserTour();
break;
}
else if (configuration[i] != CARACTERE_SEPARATION) {
//? Mis à jour des positions
if (configuration[i] == CARACTERE_LIGNE_VIDE) //? Si la configuration lue correspond au chiffre 8
for ([[maybe_unused]] int k : range(DIMENSION_PLATEAU_X)) {
plateau_[x][y] = move(creerCase(nomCases_[DIMENSION_PLATEAU_X * y + x], false));
++x;
}
//? Vérification qu'une pièce doit être instanciée
else if (configuration[i] == CARACTERE_PIECE_VIDE) {
plateau_[x][y] = move(creerCase(nomCases_[DIMENSION_PLATEAU_X * y + x], false));
++x;
}
else {
plateau_[x][y] = move(creerCase(nomCases_[DIMENSION_PLATEAU_X * y + x], true));
plateau_[x][y]->setPieceOccupante(move(creerPiece(configuration[i])));
++x;
}
if ((x + 1) % (DIMENSION_PLATEAU_X + 1) == 0) { //? + 1 pour éviter le modulo 0 (donnant toujours 0) -> (x+1) mod 9
x = 0;
++y;
}
}
}
if (nRois_ < LIMITE_CONFRONTATIONS_ROIS)
throw NombreRoisExact("Le nombre de rois est inférieur au seuil autorisé.");
}
/**
* Trouve une case (et sa position (x, y)) à partir du nom de la pièce.
* \param [const char] nomPiece <Le déplacement que l'on souhaite effectuer.>
* \param [const string&] adverseSeulement <Ne regarde que les cases adverses.>
* \return [pair<Case*, pair<int, int>>] <Un pointeur vers la case trouvée (si applicable), avec sa position (y, x) sur le plateau.>
*/
std::pair<modele::Case*, std::pair<int, int>> modele::Plateau::trouverCase(const char nomPiece, bool adverseSeulement) const
{
using namespace iter;
for (int y : range(DIMENSION_PLATEAU_Y))
for (int x : range(DIMENSION_PLATEAU_X))
if (plateau_[y][x]->getOccupation() && plateau_[y][x]->getPieceOccupante()->getNom() == static_cast<char>(tolower(nomPiece)))
if (adverseSeulement && plateau_[y][x]->getPieceOccupante()->getCouleur() != tour_) //? Ne prendre que les cases adverses
return { plateau_[y][x].get(), { y, x } };
return { nullptr, { 0,0 } };
}
/**
* Trouve une case à partir d'une position sur le plateau.
* \param [const pair<int, int>&] position <La position à rechercher.>
* \return [Case*] <Un pointeur vers la case trouvée (si applicable).>
*/
modele::Case* modele::Plateau::trouverCase(const std::pair<int, int>& position) const
{
return plateau_[position.first][position.second].get();
}
/**
* Calcule les mouvements pour une pièce en particulier sur le plateau.
* \param [Piece*] piece <La pièce à réinitialiser ses mouvements.>
* \param [const pair<int, int>&] positionInitiale <La position initiale de la pièce.>
*/
void modele::Plateau::calculerMouvementsPossibles(Piece* piece, const std::pair<int, int>& positionInitiale)
{
piece->effacerMouvements(); //? Nous réinitialisons les mouvements de toutes les pièces
piece->calculerMouvementsPossibles(positionInitiale, this);
}
/**
* Parcours des pièces et réinitialisation de leurs positions valides.
*/
void modele::Plateau::reinitialiserPositionsValidesPieces()
{
using namespace iter;
for (int y : range(DIMENSION_PLATEAU_Y))
for (int x : range(DIMENSION_PLATEAU_X))
if (plateau_[y][x]->getOccupation()) //? Regarder seulement les cases occupées par les pièces
if (plateau_[y][x]->getPieceOccupante()->getCouleur() == tour_) //? Prendre seulement les pièces qui correspondent au tour du joueur
calculerMouvementsPossibles(plateau_[y][x]->getPieceOccupante(), { y, x });
}
/**
* Inverse le tour des joueurs.
*/
void modele::Plateau::inverserTour()
{
if (tour_ == Couleur::Blanc)
tour_ = Couleur::Noir;
else
tour_ = Couleur::Blanc;
}
/**
* Permet d'initialiser le tour des joueurs.
* \param [const Couleur&] couleur <La couleur initiale qui peut jouer.>
*/
void modele::Plateau::setTour(const modele::Couleur& couleur)
{
tour_ = couleur;
}
/**
* Retourne le tour de la partie courante.
* \return [Couleur] <La couleur du tour.>
*/
modele::Couleur modele::Plateau::getTour() const
{
return tour_;
}
/**
* Permet de retourner la position d'une case.
* \param [Case*] caseATrouver <La case pour laquelle nous devons trouver sa position.>
* \return [pair<int, int>] <La position de la case (x, y).>
*/
std::pair<int, int> modele::Plateau::getPositionCase(Case* caseATrouver) const
{
using namespace iter;
for (int y : range(DIMENSION_PLATEAU_Y))
for (int x : range(DIMENSION_PLATEAU_X))
if (plateau_[y][x].get() == caseATrouver)
return { y,x };
return { 0, 0 };
}
/**
* Permet de savoir si nous sommes en échec après le déplacement d'une pièce.
* \param [Case*] caseFinale <La case finale du déplacement.>
* \return [bool] <Vrai si une pièce met en échec le roi auquel est associé la couleur du tour.>
*/
bool modele::Plateau::testEchecApresDeplacement(Case* caseFinale)
{
using namespace std;
caseFinale->getPieceOccupante()->effacerMouvements();
caseFinale->getPieceOccupante()->calculerMouvementsPossiblesSimples(getPositionCase(caseFinale), this);
const vector<pair<int, int>> MOUVEMENTS_POSSIBLES = caseFinale->getPieceOccupante()->getMouvementsPossibles();
for (auto mouvement : MOUVEMENTS_POSSIBLES)
if (trouverCase(mouvement)->getOccupation() && trouverCase(mouvement)->getPieceOccupante()->getNom() == CARACTERE_ROI_NOIR && trouverCase(mouvement)->getPieceOccupante()->getCouleur() != tour_)
return true;
return false;
}
/**
* Teste si la case possède une pièce adverse qui met en échec le roi.
* \param [Case*] caseFinale <La case finale du déplacement.>
* \return [bool] <Vrai si une pièce met en échec le roi auquel est associé la couleur du tour.>
*/
bool modele::Plateau::testEchecApresDeplacementAdversaire(Case* caseFinale)
{
using namespace std;
inverserTour();
caseFinale->getPieceOccupante()->effacerMouvements();
caseFinale->getPieceOccupante()->calculerMouvementsPossiblesSimples(getPositionCase(caseFinale), this);
const vector<pair<int, int>> MOUVEMENTS_POSSIBLES = caseFinale->getPieceOccupante()->getMouvementsPossibles();
for (auto mouvement : MOUVEMENTS_POSSIBLES)
if (trouverCase(mouvement)->getOccupation() && trouverCase(mouvement)->getPieceOccupante()->getNom() == CARACTERE_ROI_NOIR && trouverCase(mouvement)->getPieceOccupante()->getCouleur() != tour_) {
inverserTour();
return true;
}
inverserTour();
return false;
}
/**
* Permet d'inverser l'échec.
*/
void modele::Plateau::inverserEchec()
{
if (echec_ == true)
echec_ = false;
else
echec_ = true;
}
/**
* Retourne si le plateau est en état d'échec.
* \return [bool] <Vrai si le plateau est en échec.>
*/
bool modele::Plateau::getEtatEchec() const
{
return echec_;
}
/**
* Effectue le changement de position d'une pièce.
* \param [Case*] caseInitiale <La case initiale pour laquelle se trouve la pièce.>
* \param [Case*] caseFinale <La case finale occupée par la pièce.>
*/
void modele::Plateau::effectuerChangementPosition(Case* caseInitiale, Case* caseFinale)
{
//? Si occupation ennemie à la case finale, on enlève la pièce du jeu
if (caseFinale->getOccupation())
caseFinale->detruirePieceOccupante();
else
caseFinale->inverserOccupation();
//? On déplace la pièce à la nouvelle case
caseFinale->setPieceOccupante(move(caseInitiale->changerPossessionPiece()));
//? Nous enlevons la pièce de la case initiale
caseInitiale->detruirePieceOccupante();
caseInitiale->inverserOccupation();
caseFinale->getPieceOccupante()->setCouleur(tour_);
if (testEchecApresDeplacement(caseFinale))
echec_ = true;
else
echec_ = false;
inverserTour();
}
/**
* Retourne un pointeur vers une case du plateau à partir d'une position (x, y).
* \param [const int] x <La position en x du plateau.>
* \param [const int] y <La position en y du plateau.>
* \return [Case*] <Un pointeur vers la case trouvée.>
*/
modele::Case* modele::Plateau::getCase(const int x, const int y) const
{
return plateau_[x][y].get();
}
/**
* Teste si l'échec peut être protégé et dévié par le mouvement d'une pièce.
* \param [const pair<int, int>&] positionInitiale <La position initiale de la pièce.>
* \param [const pair<int, int>&] mouvement <Le mouvement de la pièce.>
* \return [bool] <Vrai si le mouvement empêche l'échec.>
*/
bool modele::Plateau::testEchecProtection(const std::pair<int, int>& positionInitiale, const std::pair<int, int>& mouvement)
{
using namespace iter;
Case* caseSouhaitee = trouverCase(mouvement);
Case* caseInitiale = trouverCase(positionInitiale);
if (caseSouhaitee->getOccupation() && caseSouhaitee->getPieceOccupante()->getCouleur() != tour_)
return true;
else if (caseSouhaitee->getOccupation() && caseSouhaitee->getPieceOccupante()->getCouleur() == tour_)
return false;
caseSouhaitee->setPieceOccupante(caseInitiale->changerPossessionPiece());
caseSouhaitee->inverserOccupation();
caseInitiale->inverserOccupation();
bool protection = true;
inverserEchec();
for (int y : range(DIMENSION_PLATEAU_Y)) {
for (int x : range(DIMENSION_PLATEAU_X))
if (plateau_[y][x]->getOccupation() && plateau_[y][x]->getPieceOccupante()->getCouleur() != tour_ && plateau_[y][x]->getPieceOccupante()->getNom() != CARACTERE_ROI_NOIR)
if (testEchecApresDeplacementAdversaire(plateau_[y][x].get())) {
protection = false;
break;
}
if (!protection)
break;
}
caseInitiale->setPieceOccupante(caseSouhaitee->changerPossessionPiece());
caseSouhaitee->inverserOccupation();
caseInitiale->inverserOccupation();
inverserEchec();
return protection;
}
/**
* Teste si une pièce peut bouger sans causer l'échec (le roi n'est pas à découvert suite au mouvement).
* \param [const pair<int, int>&] positionInitiale <La position initiale de la pièce.>
* \param [const pair<int, int>&] mouvement <Le mouvement de la pièce.>
* \return [bool] <Vrai si le mouvement cause un échec.>
*/
bool modele::Plateau::testEchecADecouvert(const std::pair<int, int>& positionInitiale, const std::pair<int, int>& mouvement)
{
using namespace std;
using namespace iter;
Case* caseInitiale = trouverCase(positionInitiale);
Case* caseSouhaitee = trouverCase(mouvement);
bool echecADecouvert = false;
bool pieceAttaquante = false;
unique_ptr<Piece> pieceAttaquanteTemp;
if (caseSouhaitee->getOccupation()) {
pieceAttaquanteTemp = caseSouhaitee->changerPossessionPiece();
pieceAttaquante = true;
}
caseSouhaitee->setPieceOccupante(caseInitiale->changerPossessionPiece());
caseSouhaitee->inverserOccupation();
caseInitiale->inverserOccupation();
for (int y : range(DIMENSION_PLATEAU_Y)) {
for (int x : range(DIMENSION_PLATEAU_X)) {
if (plateau_[y][x]->getOccupation() && plateau_[y][x]->getPieceOccupante()->getCouleur() != tour_ && plateau_[y][x]->getPieceOccupante()->getNom() != CARACTERE_ROI_NOIR) {
if (testEchecApresDeplacementAdversaire(plateau_[y][x].get())) {
echecADecouvert = true;
break;
}
}
}
if (echecADecouvert)
break;
}
caseInitiale->setPieceOccupante(caseSouhaitee->changerPossessionPiece());
caseSouhaitee->inverserOccupation();
caseInitiale->inverserOccupation();
if (pieceAttaquante)
caseSouhaitee->setPieceOccupante(move(pieceAttaquanteTemp));
return echecADecouvert;
}
/**
* Teste si une pièce cause un échec au roi.
* \param [const pair<int, int>&] positionInitiale <La position initiale de la pièce.>
* \param [const pair<int, int>&] mouvement <Le mouvement de la pièce.>
* \return [bool] <Vrai si le mouvement cause un échec.>
*/
bool modele::Plateau::testEchecAuRoi(const std::pair<int, int>& positionInitiale, const std::pair<int, int>& mouvement)
{
using namespace iter;
Case* caseSouhaitee = trouverCase(mouvement);
Case* caseInitiale = trouverCase(positionInitiale);
if (caseSouhaitee->getOccupation() && caseSouhaitee->getPieceOccupante()->getCouleur() != tour_)
return true;
else if (caseSouhaitee->getOccupation() && caseSouhaitee->getPieceOccupante()->getCouleur() == tour_)
return false;
caseSouhaitee->setPieceOccupante(caseInitiale->changerPossessionPiece());
caseSouhaitee->inverserOccupation();
caseInitiale->inverserOccupation();
bool protection = true;
for (int y : range(DIMENSION_PLATEAU_Y))
for (int x : range(DIMENSION_PLATEAU_X))
if (plateau_[y][x]->getOccupation() && plateau_[y][x]->getPieceOccupante()->getCouleur() != tour_ && plateau_[y][x]->getPieceOccupante()->getNom() != CARACTERE_ROI_NOIR)
if (testEchecApresDeplacementAdversaire(plateau_[y][x].get())) {
protection = false;
break;
}
caseInitiale->setPieceOccupante(caseSouhaitee->changerPossessionPiece());
caseSouhaitee->inverserOccupation();
caseInitiale->inverserOccupation();
return protection;
}