Specifiche di progetto - Istituto di Scienze e Tecnologie dell

Transcript

Specifiche di progetto - Istituto di Scienze e Tecnologie dell
Facoltà di Scienze MM. FF. NN.
Corso di Laurea in Informatica Applicata
INGEGNERIA DEL SOFTWARE
Ing. Tommaso Pagnini
PROGETTO PER L’APPELLO D’ESAME DEL:
01/06/04
“ChessMaster”
A CURA DI: Davide Simoncini (150235) & Marco Masciotti (149780)
Specifica del problema
Implementare il gioco ChessMaster.
ChessMaster è un programma che gestisce una partita a scacchi tra due giocatori.
L’esecuzione del programma consiste in una serie di passi, e in ciascun passo si deve specificare la
mossa da effettuare.
E’ compito del programma, in base all’input ricevuto ad ogni passo:
Verificare la liceità della mossa
Aggiornare lo stato del gioco
Fornire la rappresentazione a video della situazione aggiornata
Indicare il vincitore della partita
Le regole di gioco adottate sono quelle classiche degli scacchi, con qualche semplificazione
(es. senza alcun tipo di arrocco).
Si deve avere la possibilità di scegliere il colore che inizia a muovere per primo, anche se nel
regolamento è specificato che è sempre il bianco che apre la partita.
La rappresentazione a video viene effettuata tramite l’interfaccia testuale, che può essere a colori o
in bianco/nero.
Inoltre si ha la possibilità di salvare o caricare una partita.
I file verranno salvati in una cartella con nome “save”, localizzata nella directory dove è presente
l’eseguibile del programma.
I file salvati avranno una estensione di tipo .cmr.
Dopo aver caricato una partita è possibile visionare lo stato di quest’ultima e proseguirla.
2
Specifica dei requisiti
Diagramma Use Cases UML:
Descrizione degli Use Cases tramite flussi di eventi:
•
Use case Nuova Partita:
–
–
–
–
–
–
–
–
–
–
–
•
Viene visualizzato il menù principale che prevede tre scelte: 1 (nuova partita), 2 (carica
partita) e 3 (esci dal gioco);
I giocatori scelgono l'opzione 1 dal menù iniziale;
Se il valore immesso è corretto viene visualizzata la richiesta del nome per il giocatore
che muove i pezzi neri;
Il “giocatore nero” immette il proprio nome;
Viene visualizzata la richiesta del nome per il giocatore che muove i pezzi bianchi;
Il “giocatore bianco” immette il proprio nome;
Viene visualizzato un menù di scelta del turno iniziale;
I giocatori effettuano la propria scelta;
Se il valore immesso è corretto viene visualizzata la richiesta della modalità grafica da
adottare;
I giocatori effettuano la propria scelta;
Se il valore immesso è corretto inizia la partita (vedi descrizione dell'use case Gioca
Partita).
Use case Carica Partita:
–
–
–
–
–
–
–
–
Viene visualizzato il menù principale che prevede tre scelte: 1 (nuova partita), 2 (carica
partita) e 3 (esci dal gioco);
I giocatori scelgono l'opzione 2 dal menù iniziale;
Se il valore immesso è corretto viene visualizzata la richiesta del nome della partita
salvata;
I giocatori immettono il nome della partita;
Il sistema carica la partita dopo aver verificato che questa esiste, il file è formattato
correttamente, esiste un unico pezzo per scacco e c'è uno ed un unico re nero e bianco;
Viene visualizzata la richiesta della modalità grafica da adottare;
I giocatori effettuano la propria scelta;
Se il valore immesso è corretto continua la partita dal momento in cui era stata
salvata(vedi descrizione dell'use case Gioca Partita).
3
•
Use case Esci dal Gioco:
–
–
–
•
Use case Gioca Partita:
–
–
–
–
•
–
Vedi descrizione dell'use case Salva Partita e Continua;
Vedi descrizione dell'use case Torna al menù.
Use case Torna al menù:
–
–
•
Viene visualizzata la richiesta del nome della partita da salvare;
I giocatori immettono il nome della partita;
Il programma salva la situazione attuale su un file chiamato “<nome_partita>.cmr”
nella directory “.//save/”, se ci sono problemi viene visualizzato un messagio di errore e
si continua;
Si riprende la partita dal punto in cui è stata interrotta (vedi descrizione dell'use case
Continua Partita).
Use case Salva Partita e Torna al Menù:
–
–
•
Viene visualizzata la scacchiera con la modalità grafica scelta;
Viene visualizzato un menù che prevede quattro scelte: 's' (salvare), 'e' (tornare al menù),
'w' (salvare e tornare al menù) e altro (continuare la partita);
I giocatori effettuano una scelta;
Vedi descrizione degli use cases corrispondenti alle varie scelte.
Use case Salva Partita e Continua:
–
–
–
•
Viene visualizzato il menù principale che prevede tre scelte: 1 (nuova partita), 2 (carica
partita) e 3 (esci dal gioco);
I giocatori scelgono l'opzione 3 dal menù iniziale;
Il programma viene terminato.
Viene visualizzato il menù principale che prevede tre scelte: 1 (nuova partita), 2 (carica
partita) e 3 (esci dal gioco);
Vedi descrizione degli use cases corrispondenti alle varie scelte.
Use case Continua a Giocare:
–
–
–
–
–
–
–
–
–
–
–
–
–
–
–
Il sistema verifica se il Re del giocatore che deve eseguirte la mossa è sotto scacco o sotto
scacco matto;
Se si verifica uno scacco matto viene visualizzato il vincitore e, appena viene digitato un
tasto, si torna al menù principale; se si verifica un semplice scacco viene comunicato con
una scrittura a video e si ncontinua, altrimenti si continua senza stampare nulla;
Viene visualizzato un messaggio che specifica a chi spetta la mossa;
Viene visualizzata la richiesta della coordinata riga del pezzo da muovere;
Il giocatore che ha il turno immette la coordinata;
Il sistema verifica che la coordinata sia interna alla scacchiera, se non lo è la fa reinserire;
Viene visualizzata la richiesta della coordinata colonna del pezzo da muovere;
Il giocatore che ha il turno immette la coordinata;
Il sistema verifica che la coordinata sia interna alla scacchiera, se non lo è la fa reinserire;
Viene visualizzata la richiesta della coordinata riga dello scacco di destinazione;
Il giocatore che ha il turno immette la coordinata;
Il sistema verifica che la coordinata sia interna alla scacchiera, se non lo è la fa reinserire;
Viene visualizzata la richiesta della coordinata colonna dello scacco di destinazione;
Il giocatore che ha il turno immette la coordinata;
Il sistema verifica che la coordinata sia interna alla scacchiera, se non lo è la fa reinserire;
4
–
–
–
–
–
Il sistema verifica se lo scacco di partenza contiene un pezzo del giocatore che ha il turno
di gioco, se no stampa a video che la mossa è errata;
Il sistema verifica se la mossa relativa al pezzo selezionato per andare allo scacco di
destinazione è lecita, se non lo è stampa a video che la mossa è errata;
Il sistema verifica se è presente uno scacco e se la mossa attuale lo risolve, e se,
ammettendo che lo scacco esista e non lo si risolve con la mossa, la mossa è illecita e
viene stampato a video un errore di mossa errata;
Se i vari controlli precedenti vanno a buon fine viene eseguita la mossa;
Vedi descrizione dell'use case Gioca Partita.
Diagramma degli Stati UML:
5
Analisi di progetto
Input del programma
Il programma deve gestire una partita a scacchi tra giocatori. La partita può essere una
precedentemente salvata e quindi da caricare, oppure può essere una nuova partita. Per questo
motivo per prima cosa occorre scegliere cosa si vuol fare tra queste due opportunità.
A seconda della scelta effettuata ci sarà una diversa interazione con l’utente:
1. Nel caso di una nuova partita occorre:
Inserire il nome dei due giocatori.
Decidere chi sarà il primo ad effettuare la mossa.
Scegliere la modalità di visualizzazione della scacchiera.
Giocare la partita (inserimento delle mosse da parte dei giocatori).
2. Nel caso di carica di una partita occorre:
Scegliere la modalità di visualizzazione della scacchiera.
Giocare la partita (inserimento delle mosse da parte dei giocatori).
Nel caso si cercasse di caricare una partita non esistente, viene stampato a video un messaggio di
errore e si ritorna al menù principale.
Per effettuare una mossa è necessario inserire le coordinate del pezzo che si vuol muovere e le
coordinate di destinazione.
Le coordinate sono composte da riga e colonna espresse entrambe da numeri.
In caso di inserimento di una coordinata errata (es. un carattere o un intero fuori range) sarà
necessario reinserire la coordinata.
In caso di mossa errata dovranno essere reinserite tutte le coordinate.
Tra una mossa ed un’altra è possibile scegliere di:
Salvare la partita
Ritornare al menù principale
Salvare e ritornare al menù principale
Continuare la partita
Nel caso di salvataggio è necessario inserire il nome della partita.
Output del programma
Ad ogni mossa la scacchiera deve essere aggiornata e quindi rispecchiare la situazione effettiva.
Inoltre dopo ogni mossa vengono visualizzati messaggi che indicano se la mossa è errata, se ha
causato uno scacco o uno scacco matto.
Nel caso di salvataggio di una partita sarà creato un file, con estensione .cmr, contenente le
informazioni riguardanti la partita.
All’interno del file sono presenti, nel seguente ordine, le informazioni:
<Nome giocatore nero>
<Nome giocatore bianco>
<Turno>
<Riga>,<Colonna>|<NomePezzo>-<ColorePezzo>
<Riga>,<Colonna>|<NomePezzo>-<ColorePezzo>
…………
6
Esempio di file:
Simo
Mascio
nero
0,0|T-nero
0,7|T-nero
2,1|A-nero
0,4|K-nero
7,4|K-bianco
6,4|P-bianco
7,3|Q-bianco
5,5|C-bianco
In caso di file danneggiato o modificato malamente, l'operazione di caricamento della partita
stamperà a video un messaggio di errore.
Progettazione del sistema
Breve descrizione dell’intero progetto
Nella progettazione del sistema si è pensato di sfruttare il principio della “modularità”, ossia di
dividere l'intero programma in blocchi che risultano logicamente a se stanti e che possono essere
implementati individualmente ottenendo un codice che risulta essere più leggibile, comprensibile e
manutenibile.
A questo scopo ci viene in contro il linguaggio C++, che ci fornisce il costrutto “classe” il quale ci
permette di lavorare ad un livello di astrazione molto alto e di rappresentare in codice C++
direttamente le idee che abbiamo.
Il percorso che abbiamo tenuto è stato quello di rappresentare, dopo aver raccolto le idee, l'intero
sistema con un diagramma UML delle Classi, in modo da poter passare, in un secondo momento,
molto più velocemente alla realizzazione del codice visto che la corrispondenza tra il diagramma e
il codice C++ è molto alta. Il diagramma delle Classi ottenuto è riportato nella pagina seguente.
Il passo successivo è stato quello di tradurre le classi rappresentate nel diagramma UML in classi
C++, costruendo per ogni classe un header file che contiene la dichiarazione della classe omonima,
l’unica eccezione per la classe Pezzo e le sue classi derivate che sono state collocate tutte nel file
“Pezzo.h”. Nei relativi file “.cpp” sono state definite le classi corrispondenti.
Infine è presente un file “Main.cpp” che crea un’istanza della classe Partita e poi ne richiama i
metodi per eseguire il gioco.
7
Diagramma delle Classi UML:
Descrizione di alcuni punti delle classi
Classe Partita: Questa classe contiene le informazioni riguardanti una partita.Quindi gli attributi
necessari sono le informazioni riguardanti i due giocatori, la scacchiera e il colore del giocatore che
deve effettuare la mossa (Turno). Per questo abbiamo scelto di avere un puntatore alla scacchiera e
un puntatore per ogni giocatore. Quando creiamo una partita creiamo di conseguenza anche una
istanza di tipo scacchiera e due istanze di tipo giocatore. Adottando questa soluzione (creazione
dinamica delle istanze) sarà necessario liberare la zona di memoria occupata da questi ultimi,
all’interno del distruttore di Partita, utilizzando la keyword “delete” in modo da non avere sprechi di
memoria.
I metodi necessari sono quelli che permettono di modificare il turno e di ottenere informazioni sui
giocatori. Inoltre sono necessari metodi per salvare e caricare una partita come richiesto da
specifiche. E’ presente il metodo GiocaPartita che permette al giocatore che ha il turno di effettuare
una mossa. Questi metodi sono tutti pubblici in modo che possono essere utilizzati da altre classi.
Abbiamo deciso di inserire GiocaPartita come metodo della classe in modo da rendere la classe più
coesa e di avere un numero limitato di metodi pubblici. Questo ne va ovviamente a discapito della
possibilità di riutilizzo della classe stessa. Inizialmente si era pensato di non inserire questo metodo
nella classe e di “implementarlo nel main” del nostro progetto. Abbiamo abbandonato questa
soluzione perché abbiamo spostato la complessità all’interno della classe, rendendo di conseguenza
il main più leggibile.
Ci sono metodi privati che possono essere utilizzati solo dalla classe stessa come InizializzaTurno e
EseguiMossa. Nel caso in cui GiocaPartita non fosse stato un metodo interno alla classe il metodo
EseguiMossa doveva essere dichiarato come pubblico. Oltre a questo dovevo esserci altri metodi
pubblici come GetTurno e GetScacchiera.
Classe Giocatore: Questa classe contiene gli attributi Nome e ColoreG. I metodi GetNome e
GetColore permettono di accede a questi attributi ritornando il valore che assumono nella
particolare istanza. I metodi AssegnaColore e AssegnaNome permettono invece di modificarne il
valore.
Questa classe è abbastanza generica è potrebbe essere riutilizzata per progetti futuri.
Classe Scacchiera: Questa classe contiene una matrice di puntatori di tipo Pezzo. Il metodo
InizializzaScacchiera viene invocato per posizionare i pezzi nella scacchiera. Questo metodo viene
utilizzato ogni volta che si crea una nuova partita. Il posizionamento è quello tipico degli scacchi.
Ovviamente quando carichiamo una partita il posizionamento dipende dalle informazioni presenti
all’interno del file.
Inoltre molto importanti sono il metodo AssegnaPezzo e GetPezzo.
Il primo assegna un pezzo in una determinata posizione all’interno della scacchiera.
Il secondo restituisce il puntatore al pezzo che occupa una determinata posizione nella scacchiera.
Inoltre sono presenti due metodi che implementano la visualizzazione della scacchiera in modalità
testuale bianco/nero e a colori.
Classe Pezzo: Questa è una classe virtuale.Infatti da questa derivano altre classi che rappresentano
il pezzo vero e proprio: Pedone, Torre, Cavallo, Alfiere, Regina e Re.
Abbiamo deciso di realizzare questa classe perché tutti i pezzi sopra riportati sono dello stesso tipo.
Il metodo MossaValida è un metodo virtuale puro.Infatti ogni mossa è specifica per ogni pezzo e
viene implementata in ogni classe derivata.
Gli attributi Nome e Colore sono protected per poter essere utilizzati dalle classi derivate.
I metodi,GetColore e GetNome, sono implementati nella classe base perché possono essere
utilizzati indipendentemente dal tipo di pezzo.
Ovviamente i costruttori sono specifici per ogni classe derivata.
Il distruttore deve invece essere virtuale perché altrimenti si avrebbero dei problemi con la
distruzione delle classi derivate. Quest’ultime si possono permettere di non implementare i
distruttori.
Classe MyException: Classe per la gestione di tutte le eccezioni che si possono presentare
all’interno del programma. Composta essenzialmente da una stringa contenente informazioni
sull’errore.
Classe Scacco: Questa classe non è stata implementata ma inizialmente ne avevamo prevista la
presenza. E’ stata poi eliminata per i pochi vantaggi portati a fronte di un elevato numero di linee di
codice. Un vantaggio che poteva portare questa classe era in fase di stampa. Infatti al posto di avere
una complicata funzione di visualizzazione a colori della scacchiera (ved. codice) potevamo
ipotizzare la presenza l’attributo colore all’interno della classe Scacco per sfruttarlo in fase di
stampa.
10
Implementazione
Main.cpp
#include
#include
#include
#include
#include
#include
"Partita.h"
"Giocatore.h"
"Scacchiera.h"
<iostream>
<string>
<conio>
using namespace std;
//########## Entry Point dell'applicazione ##########
int main ()
{
string scelta;
bool GiocaAncora = true;
while (GiocaAncora) {
clrscr();
cout << "\t\t\tCHESS MASTER\n";
cout << "Menù principale\n\n";
cout << "\tNuova partita
--> 1\n";
cout << "\tCarica partita --> 2\n";
cout << "\tEsci
--> 3\n";
cout << "\nInserisci la tua scelta: ";
cin >> scelta;
// Verifica la scelta
// Inizia nuova partita
if (scelta == "1"){
string nome_gn, nome_gb;
cout << "\nInserisci il nome del giocatore nero: ";
cin >> nome_gn;
cout << "Inserisci il nome del giocatore bianco: ";
cin >> nome_gb;
// Creo una nuova partita passando il nome della partita e i nomi dei due giocatori
Partita NewPartita(nome_gn, nome_gb);
NewPartita.NuovaPartita(); // Inizializza la scacchiera
NewPartita.GiocaPartita();
cout << "\n\n";
}
// Carica partita
else if (scelta == "2"){
bool StatoOk;
string nome_partita;
cout << "\nImmettere il nome della partita salvata: ";
cin >> nome_partita;
string path = ".//save/" + nome_partita + ".cmr";
// Crea un oggetto Partita
Partita PartitaCurr;
// Inizializza la Scacchiera
StatoOk = PartitaCurr.CaricaPartita(path);
// Controlla se la partita è stata caricata correttamente,
// in caso affermativo la gioca altrimenti torna al menù principale
if (StatoOk) GiocaPartita(PartitaCurr);
cout << "\n\n";
}
// Esci dal programma
else if (scelta == "3"){
GiocaAncora = false;
cout << "\nArrivederci!!!";
}
// Ogni altro valore inserito
else{
cout << "\nIl valore immesso è errato!\n";
getch();
}
}
return 1;
}
11
Partita.h
#ifndef PARTITA_H
#define PARTITA_H
#include "MyType.h"
#include <string>
using namespace std;
class Scacchiera; //forward declaration
class Giocatore; //forward declaration
//########## Partita ##########
class Partita {
Giocatore *GioBianco, *GioNero;
Scacchiera *Scacc;
color Turno;
color InizializzaTurno(); // Metodo privato
ResultMossa EseguiMossa(int x, int y, int x_new, int y_new);
public:
Partita();
Partita(string GN, string GB);
~Partita();
Giocatore* GetGiocatore(color colore) const;
void CambiaTurno();
void NuovaPartita();
int GiocaPartita();
bool CaricaPartita(string FilePath);
bool SalvaPartita() const;
bool SottoScacco(Giocatore* G) const;
bool ScaccoMatto(Giocatore* G) const;
};
#endif
Partita.cpp
#include
#include
#include
#include
#include
#include
#include
#include
"Partita.h"
"Scacchiera.h"
"Giocatore.h"
"Pezzo.h"
"MyException.h"
<conio>
<fstream>
<iostream>
using namespace std;
// Definisce i colori per la modalità grafica
const int S_N = BLUE;
// Scacco nero
const int S_B = WHITE;
// Scacco bianco
const int P_N = YELLOW; // Pezzo nero
const int P_B = RED;
// Pezzo bianco
const int COORD = BROWN; // Coordinate
// Acquisisce una coordinata da tastiera
int AcquisisciCoord();
// Costruttore1 di Partita con valori di default
Partita::Partita()
{
Scacc = new Scacchiera;
GioBianco = new Giocatore("Giocatore BIANCO", bianco);
GioNero = new Giocatore("Giocatore NERO", nero);
}
// Costruttore2 di Partita con inizializzazione del turno
Partita::Partita(string GN, string GB)
{
Scacc = new Scacchiera;
12
GioBianco = new Giocatore(GB, bianco);
GioNero = new Giocatore(GN, nero);
}
// Distruttore di Partita
Partita::~Partita()
{
// Cancello i giocatori e la scacchiera allocati dinamicamente
delete Scacc;
delete GioBianco;
delete GioNero;
}
// Ritorna un puntatore al giocatore associato al colore passato
Giocatore* Partita::GetGiocatore(color colore) const
{
if (colore == bianco) return GioBianco;
else return GioNero;
}
// Assegna il Turno la prima volta
color Partita::InizializzaTurno()
{
int scelta_init;
cout
cout
cout
cout
<<
<<
<<
<<
"\nSeleziona chi sarà il primo ad effettuare la mossa:\n";
"\n\tNero
--> 1\n";
"\tBianco --> 2\n";
"\tRandom --> 3\n";
do {
string s;
cout << "\nEffettua la tua scelta: ";
cin >> s;
scelta_init = atoi(s.c_str());
switch (scelta_init) {
case 1:
return nero;
case 2:
return bianco;
case 3:
return (rand() % 2) ? bianco : nero;
default:
cout << "\nIl valore immesso è errato!\n";
break;
}
}
while ((scelta_init > 3) || (scelta_init < 1));
return bianco;
}
// Assegna il turno all'altro giocatore
void Partita::CambiaTurno()
{
if (Turno) Turno = nero;
else Turno = bianco;
}
// Inizia una nuova Partita
void Partita::NuovaPartita()
{
Turno = InizializzaTurno();
Scacc -> InizializzaScacc();
}
// Esegue le mosse dei giocatori finchè la partita non finisce
// o si desidera uscire
int Partita::GiocaPartita()
{
int r, c, r_new, c_new;
// Giocatore del turno precedente
Giocatore *GPrec;
// Giocatore corrente
Giocatore *GCurr = Turno? GioBianco:GioNero;
13
// Scacco matto = false
bool SMatto = false;
// Modalità grafica
string mod;
// Sceglie modalità grafica
do {
cout << "\nInserisci modalità grafica della scacchiera(c=color, t=text): ";
cin >> mod;
if (mod != "c" && mod != "C" && mod != "t" && mod != "T")
cout << "\nValore errato! Reinserirlo.";
}
while(mod != "c" && mod != "C" && mod != "t" && mod != "T");
// Stampa scacchiera
// 'C' modalità a colori 'T' modalità testo
if (mod == "c" || mod == "C")
Scacc -> StampaColor(S_N, S_B, P_N, P_B, COORD);
else
Scacc -> StampaText();
// Controllo necessario per verificare lo stato di partite caricate da file
// Aggiorna giocatore precedente
GPrec = GetGiocatore(Turno? nero:bianco);
// Verifica la presenza di uno scacco matto
if (ScaccoMatto(GCurr)) {
cout << "\n\nPARTITA FINITA!! VINCE " << (GPrec -> GetNome()) << "!!";
cout << "\n\nPremi un tasto per tornare al menù principale!";
getch();
SMatto = true;
}
// Verifica se la partita è finita
while (!SMatto)
{
// Da la possibilità al giocatore di salvare la partita ('S'),
// di tornare al menù principale ('E'), di effettuare entrambe
// le cose ('W') o di continuare la partita
bool StatoOk = true;
do {
cout << "\nPremi : 's'
per salvare\n\t'e'
per tornare al menu'";
cout << "\n\t'w'
per salvare e tornare al menu'\n\taltro per continuare\n";
char key = getch();
if (key == 's' || key == 'S')
StatoOk = SalvaPartita();
else if (key == 'E' || key == 'e')
return 0;
else if (key == 'w' || key == 'W'){
StatoOk = SalvaPartita();
return 0;
}
}
while (!StatoOk);
// Verifica se il giocatore è sotto scacco
if (SottoScacco(GCurr))
cout << "\nRE "<< (Turno ? "BIANCO" : "NERO") << " SOTTO SCACCO!!\n";
// Esegui un'altra mossa
cout << "\n\nMossa al " << (Turno ? "BIANCO" : "NERO");
cout << " (" << (GCurr -> GetNome()) << ")\n\n";
// Leggi le coordinate della mossa
cout << "Inserisci coordinate del pezzo da muovere:";
cout << "\n\tRiga: ";
r = AcquisisciCoord();
cout << "\tColonna: ";
c = AcquisisciCoord();
cout << "Inserisci nuove coordinate del pezzo:";
cout << "\n\tRiga: ";
r_new = AcquisisciCoord();
cout << "\tColonna: ";
c_new = AcquisisciCoord();
// Esegue la mossa il giocatore che ha il turno
switch (EseguiMossa(r, c, r_new, c_new))
{
// Mossa inserita valida
case ok:
CambiaTurno();
// Stampa scacchiera
14
if (mod == "c" || mod == "C")
Scacc -> StampaColor(S_N, S_B, P_N, P_B, COORD);
else
Scacc -> StampaText();
break;
// Mossa inserita errata
case errore:
// Stampa scacchiera
if (mod == "c" || mod == "C")
Scacc -> StampaColor(S_N, S_B, P_N, P_B, COORD);
else
Scacc -> StampaText();
cout << "\nMOSSA ERRATA!!\n";
break;
// La mossa inserita non risolve lo scacco e quindi non è valida
case scacco:
// Stampa scacchiera
if (mod == "c" || mod == "C")
Scacc -> StampaColor(S_N, S_B, P_N, P_B, COORD);
else
Scacc -> StampaText();
cout << "\nMOSSA NON VALIDA A CAUSA DELLO SCACCO!!\n";
break;
}
// Aggiorna turno e giocatore corrente
GPrec = GCurr;
GCurr = GetGiocatore(Turno);
// Verifica la presenza di uno scacco matto
if (ScaccoMatto(GCurr)) {
cout << "\n\nPARTITA FINITA!! VINCE " << (GPrec -> GetNome()) << "!!";
cout << "\n\nPremi un tasto per tornare al menù principale!";
getch();
SMatto = true;
}
}
return 1;
}
// Carica una partita salvata precedentemente; ritorna true se la
// operazione è andata a buon fine, false altrimenti
bool Partita::CaricaPartita(string FilePath)
{
// Contengono il numero di re caricati per ogni colore
int ContaReNero = 0,ContaReBianco = 0;
// Cattura le eccezioni
try {
fstream SourceFile;
// Apre il file in sola lettura
SourceFile.open(FilePath.c_str(), ios::in);
// Controlla se il percorso del file è errato
if (SourceFile.fail())
throw MyException("\nErrore! File non trovato o danneggiato!");
char str[100];
// Legge il nome del Giocatore nero
str[0] = '\0';
SourceFile.getline(str, 100);
// Controlla se la lettura della riga è fallita
if (SourceFile.fail())
throw MyException("Errore in lettura della riga 1!");
string NomeGN(str);
GioNero -> AssegnaNome(NomeGN);
// Legge il nome del Giocatore bianco
str[0] = '\0';
SourceFile.getline(str, 100);
// Controlla se la lettura della riga è fallita
if (SourceFile.fail())
throw MyException("Errore in lettura della riga 2!");
string NomeGB(str);
GioBianco -> AssegnaNome(NomeGB);
// Legge il turno
str[0] = '\0';
SourceFile.getline(str, 100);
15
// Controlla se la lettura della riga è fallita
if (SourceFile.fail())
throw MyException("Errore in lettura della riga 3!");
string Trn(str);
// Verifica se il turno è corretto
if ((Trn == "bianco") || (Trn == "nero"))
if (Trn == "bianco") Turno = bianco;
else Turno = nero;
else
throw MyException("Valore turno errato!");
// Leggi pezzi e coordinate
while (!SourceFile.eof()) {
// Legge una riga dal file di testo
str[0] = '\0';
SourceFile.getline(str, 100);
// Controlla se la lettura della riga è fallita
if (SourceFile.fail())
throw MyException("Errore in lettura della riga!");
string riga(str);
// Leggi coordinate pezzo
int i = riga.find(',');
// Controlla se il carattere ',' non viene trovato
if (i < 0)
throw MyException("Formattazione file errata!");
string temp = riga.substr(0, i);
int r = atoi(temp.c_str());
int j = riga.find('|');
// Controlla se il carattere '|' non viene trovato
if (i < 0)
throw MyException("Formattazione file errata!");
temp = riga.substr(i + 1, j);
int c = atoi(temp.c_str());
// Verifica la presenza di coordinate corrette
if ((!r || !c) && (r < 0 || r > 7) && (c < 0 || r > 7))
throw MyException("Coordinate di pezzo/i mancanti o errate!");
// Leggi il tipo di pezzo e il colore
i = riga.find('-');
// Controlla se il carattere '-' non viene trovato
if (i < 0)
throw MyException("Formattazione file errata!");
temp = riga.substr(j + 1, i);
const char* TipoPezzo = temp.data();
string Colore = riga.substr(i + 1, riga.length() - i);
color Clr;
// Verifica se il colore è corretto
if ((Colore == "bianco") || (Colore == "nero"))
if (Colore == "bianco") Clr = bianco;
else Clr = nero;
else
throw MyException("Colore pezzo/i errato!");
// Controlla se nello scacco esiste già un pezzo
if (Scacc -> GetPezzo(r, c) != NULL)
throw MyException("Uno o più pezzi sono sovrapposti!");
// Inserisci Pezzo letto nella scacchiera
switch (TipoPezzo[0]) {
case 'T':
case 't':
Scacc -> AssegnaPezzo(new Torre(Clr),r,c);
break;
case 'C':
case 'c':
Scacc -> AssegnaPezzo(new Cavallo(Clr),r,c);
break;
case 'A':
case 'a':
Scacc -> AssegnaPezzo(new Alfiere(Clr),r,c);
break;
case 'K':
case 'k':
Scacc -> AssegnaPezzo(new Re(Clr),r,c);
if (Clr == nero) ContaReNero++;
else ContaReBianco++;
break;
case 'Q':
16
case 'q':
Scacc -> AssegnaPezzo(new Regina(Clr),r,c);
break;
case 'P':
case 'p':
Scacc -> AssegnaPezzo(new Pedone(Clr),r,c);
break;
default:
throw MyException("\nPezzo/i errato/i!");
}
}
SourceFile.close();
if ((ContaReNero != 1) || (ContaReBianco != 1))
throw MyException("\nNumero di Re errato!");
}
catch (MyException e) {
cout << "\n" << e.getError() << "\n";
getch();
return false;
}
return true;
}
// Salva una partita; ritorna true se il salvataggio è andato a buon fine, false altrimenti
bool Partita::SalvaPartita() const
{
string nome_partita;
cout << "\nImmettere il nome da assegnare alla partita: ";
cin >> nome_partita;
// La partita viene salvata nella cartella salva della directory corrente
string FilePath = ".//save/" + nome_partita + ".cmr";
// Cattura le eccezioni
try {
fstream DestFile;
// Apre il file in scrittura
DestFile.open(FilePath.c_str(), ios::out);
// Verifica se il percorso del file è errato
if (DestFile.fail())
throw MyException("\nErrore! File non trovato o danneggiato!");
// Scrivi il nome del Giocatore nero
string NomeGN = (GioNero -> GetNome()) + "\n";
DestFile << NomeGN;
// Controlla se la lettura della riga è fallita
if (DestFile.fail())
throw MyException("Errore in scrittura della riga 1!");
// Scrivi il nome del Giocatore bianco
string NomeGB = (GioBianco -> GetNome()) + "\n";
DestFile << NomeGB;
// Controlla se la lettura della riga è fallita
if (DestFile.fail())
throw MyException("Errore in scrittura della riga 2!");
// Scrivi il turno
string Trn = (Turno ? "bianco" : "nero");
DestFile << Trn;
// Controlla se la lettura della riga è fallita
if (DestFile.fail())
throw MyException("Errore in scrittura della riga 3!");
// Scrivi le coordinate dei pezzi, i pezzi e il loro colore
for (int i = 0; i < DIM; i++)
for (int j = 0; j < DIM; j++)
{
Pezzo* CurrP = Scacc -> GetPezzo(i,j);
if (CurrP != NULL) {
char r = i + 48;
char c = j + 48;
DestFile << "\n";
// Controlla se la lettura della riga è fallita
if (DestFile.fail())
throw MyException("Errore in scrittura di uno o più pezzi!");
DestFile << r;
// Controlla se la lettura della riga è fallita
if (DestFile.fail())
throw MyException("Errore in scrittura di uno o più pezzi!");
17
DestFile << ",";
// Controlla se la lettura della riga è fallita
if (DestFile.fail())
throw MyException("Errore in scrittura di uno
DestFile << c;
// Controlla se la lettura della riga è fallita
if (DestFile.fail())
throw MyException("Errore in scrittura di uno
DestFile << "|";
// Controlla se la lettura della riga è fallita
if (DestFile.fail())
throw MyException("Errore in scrittura di uno
string Clr = (CurrP -> GetColore() ? "bianco" :
DestFile << (CurrP -> GetNome());
// Controlla se la lettura della riga è fallita
if (DestFile.fail())
throw MyException("Errore in scrittura di uno
DestFile << "-";
// Controlla se la lettura della riga è fallita
if (DestFile.fail())
throw MyException("Errore in scrittura di uno
DestFile << Clr;
// Controlla se la lettura della riga è fallita
if (DestFile.fail())
throw MyException("Errore in scrittura di uno
o più pezzi!");
o più pezzi!");
o più pezzi!");
"nero");
o più pezzi!");
o più pezzi!");
o più pezzi!");
}
}
DestFile.close();
}
catch (MyException e) {
cout << "\n" << e.getError() << "\n";
getch();
return false;
}
return true;
}
// Fa eseguire al giocatore G una mossa
ResultMossa Partita::EseguiMossa(int r, int c, int r_new, int c_new)
{
// Imposta il Giocatore corrente
Giocatore* G = (Turno ? GioBianco : GioNero);
//Ricavo il pezzo che sto per muovere
Pezzo* CurrP = Scacc -> GetPezzo(r,c);
//Ricavo il pezzo che sarebbe nella nuova posizione
Pezzo* CurrPNew = Scacc -> GetPezzo(r_new,c_new);
// Se lo scacco non è vuoto e il pezzo è dello stesso colore del giocatore
if ((CurrP != NULL) && (CurrP -> GetColore() == G -> GetColore()))
{
// Se la mossa è valida (in base al pezzo) faccio la mossa
if (CurrP -> MossaValida(Scacc,r, c, r_new, c_new))
{
// Se lo scacco di destinazione contiene un altro pezzo lo mangio
Scacc -> AssegnaPezzo(CurrP,r_new,c_new);
Scacc -> AssegnaPezzo(NULL,r,c);
// Se il Giocatore non è sotto sacco la mossa è valida..
if (!SottoScacco(G))
{
if (CurrPNew != NULL) delete CurrPNew;
return ok;
}
// ..altrimenti viene annullata
else
{
Scacc -> AssegnaPezzo(CurrP,r,c);
Scacc -> AssegnaPezzo(CurrPNew,r_new,c_new);
return scacco;
}
}
}
return errore;
}
// Ritorna true se il Re di G è sotto scacco
bool Partita::SottoScacco(Giocatore* G) const
{
bool trovato = false;
int re_r, re_c;
18
// Trova la posizione del Re del giocatore G
for (int i = 0; (i < DIM && !trovato); i++)
for (int j = 0; (j < DIM && !trovato); j++)
{
Pezzo* CurrP = Scacc -> GetPezzo(i,j);
if ((CurrP != NULL) && (CurrP -> GetColore() == G -> GetColore())
&& (CurrP -> GetNome() == 'K'))
{
trovato = true;
re_r=i;
re_c=j;
}
}
// Verifica se il re di G è sotto scacco
for (int i = 0; i < DIM; i++)
for (int j = 0; j < DIM; j++)
{
Pezzo* CurrP = Scacc -> GetPezzo(i, j);
if ((CurrP != NULL) && (CurrP -> GetColore() != G -> GetColore())
&& (CurrP -> MossaValida(Scacc,i, j, re_r, re_c)))
return true;
}
return false;
}
// Ritorna true se il giocatore G è sotto scacco matto
bool Partita::ScaccoMatto(Giocatore* G) const
{
// Analizza tutti gli scacchi uno alla volta
for (int r = 0; r < DIM; r++)
for (int c = 0; c < DIM; c++)
{
Pezzo* CurrP = Scacc -> GetPezzo(r,c);
// Se lo scacco possiede un pezzo di G..
if ((CurrP != NULL) && (CurrP -> GetColore() == G -> GetColore()))
{
// ..guardo se questo per qualche mossa può risolvere lo scacco al RE
for (int i = 0; i < DIM; i++)
for (int j = 0; j < DIM; j++)
{
Pezzo* CurrPNew = Scacc -> GetPezzo(i, j);
// Simula la mossa e guarda se è possibile risolvere lo scacco
if (CurrP -> MossaValida(Scacc,r, c, i, j)) {
Scacc -> AssegnaPezzo(CurrP, i, j);
Scacc -> AssegnaPezzo(NULL, r, c);
// Lo sacco è stato risolto e la mossa viene annullata
if (!SottoScacco(G)) {
Scacc -> AssegnaPezzo(CurrPNew, i, j);
Scacc -> AssegnaPezzo(CurrP, r, c);
return false;
}
// Lo scacco non è stato risolto, la mossa viene annullata
// e si procede con la simulazione di un'atra mossa
else {
Scacc -> AssegnaPezzo(CurrPNew, i, j);
Scacc -> AssegnaPezzo(CurrP, r, c);
}
}
}
}
}
// Se non trovo nessuna mossa che risolve lo scacco significa che è scacco matto
return true;
}
// Acquisisce una coordinata da tastiera
int AcquisisciCoord()
{
string s;
int r;
do {
cin >> s;
19
if ((s != "0") && (s != "1") && (s != "2") && (s != "3") &&
(s != "4") && (s != "5") && (s != "6") && (s != "7"))
cout << "Ultimo valore errato! Reinserirlo: "
else r = atoi(s.c_str());
}
while ((s != "0") && (s != "1") && (s != "2") && (s != "3") &&
(s != "4") && (s != "5") && (s != "6") && (s != "7"));
return r;
}
Giocatore.h
#ifndef GIOCATORE_H
#define GIOCATORE_H
#include "MyType.h"
#include <string>
using namespace std;
//########## Giocatore ##########
class Giocatore {
string Nome;
color ColoreG;
public:
Giocatore(string NomeG, color c);
~Giocatore();
void AssegnaColore(color c);
void AssegnaNome(string n);
string GetNome() const;
color GetColore() const;
};
#endif
Giocatore.cpp
#include "Giocatore.h"
using namespace std;
// Costruttore di Giocatore
Giocatore::Giocatore(string NomeG, color c)
: Nome(NomeG), ColoreG(c)
{ }
// Distruttore di Giocatore
Giocatore::~Giocatore()
{ }
// Assegna il colore al giocatore
void Giocatore::AssegnaColore(color c) { ColoreG = c; }
// Assegna il nome al giocatore
void Giocatore::AssegnaNome(string n) { Nome = n; }
// Ritorna il nome del giocatore
string Giocatore::GetNome() const { return(Nome); }
// Ritorna il colore del giocatore
color Giocatore::GetColore() const { return(ColoreG); }
Scacchiera.h
#ifndef SCACCHIERA_H
#define SCACCHIERA_H
// Numero di scacchi in una riga (o colonna) della scacchiera
const int DIM = 8;
20
class Pezzo; // forward declaration
//########## Classe Scacchiera ##########
class Scacchiera {
Pezzo* Sch[DIM][DIM];
public:
Scacchiera();
~Scacchiera();
void InizializzaScacc();
void AssegnaPezzo(Pezzo* P, int r, int c);
Pezzo* GetPezzo(int r, int c) const;
void StampaText() const;
void StampaColor(int CScN, int CScB, int CPzN, int CPzB, int CTxt) const;
};
#endif
Scacchiera.cpp
#include
#include
#include
#include
#include
"Scacchiera.h"
"Pezzo.h"
"MyType.h"
<iostream>
<conio>
using namespace std;
// Costruttore di Scacchiera
Scacchiera::Scacchiera()
{
// Riempo la scacchiera di puntatori a scacchi
for(int i = 0; i < DIM; i++)
for(int j = 0; j < DIM; j++)
Sch[i][j] = NULL;
}
// Distruttore della Scacchiera
Scacchiera::~Scacchiera()
{
//Cancello gli scacchi allocati dinamicamente
for(int i = 0; i < DIM; i++)
for(int j = 0; j < DIM; j++)
delete Sch[i][j];
}
// Inizializza la scacchiera con tutti i pezzi
void Scacchiera::InizializzaScacc()
{
// Inserisco le Torri nella scacchiera
AssegnaPezzo(new Torre(nero),0,0);
AssegnaPezzo(new Torre(nero),0,7);
AssegnaPezzo(new Torre(bianco),7,0);
AssegnaPezzo(new Torre(bianco),7,7);
// Inserisco i Cavalli nella scacchiera
AssegnaPezzo(new Cavallo(nero),0,1);
AssegnaPezzo(new Cavallo(nero),0,6);
AssegnaPezzo(new Cavallo(bianco),7,1);
AssegnaPezzo(new Cavallo(bianco),7,6);
// Inserisco gli
AssegnaPezzo(new
AssegnaPezzo(new
AssegnaPezzo(new
AssegnaPezzo(new
Alfieri nella scacchiera
Alfiere(nero),0,2);
Alfiere(nero),0,5);
Alfiere(bianco),7,2);
Alfiere(bianco),7,5);
// Inserisco le Regine nella scacchiera
AssegnaPezzo(new Regina(nero),0,3);
AssegnaPezzo(new Regina(bianco),7,3);
// Inserisco i Re nella scacchiera
AssegnaPezzo(new Re(nero),0,4);
AssegnaPezzo(new Re(bianco),7,4);
21
// Inserisco i Pedoni nella scacchiera
for (int i = 1, j = 0; j < DIM; j++)
AssegnaPezzo(new Pedone(nero),i,j);
for (int i = 6, j = 0; j < DIM; j++)
AssegnaPezzo(new Pedone(bianco),i,j);
}
// Restituisce un puntatore allo scacco con coordinate x e y
Pezzo* Scacchiera::GetPezzo(int r, int c) const {
return Sch[r][c];
}
// Assegna il Pezzo P allo scacco Sch[r][c]
void Scacchiera::AssegnaPezzo(Pezzo* P,int r,int c) {
Sch[r][c] = P;
}
// Stampa a video la scacchiera in modalità colori
void Scacchiera::StampaColor(int CScN, int CScB, int CPzN, int CPzB, int CTxt) const
{
clrscr();
gotoxy (0,0);
cout << "\t\t\t CHESS MASTER\n\n\t";
textcolor(CTxt);
cprintf ("
0
1
2
3
4
5
6
7");
for(int i = 0; i < DIM; i++)
{
cout << "\n\t";
cprintf(" %d ", i);
for(int j = 0; j < DIM; j++)
{
Pezzo *PzCurr = GetPezzo(i,j);
if (PzCurr == NULL) {
if((((i%2)==0)&&((j%2)==0)) || (((i%2)==1)&&((j%2)==1)))
textbackground(CScB);
else if ((((i%2)==0)&&((j%2)==1)) || (((i%2)==1)&&((j%2)==0)))
textbackground(CScN);
cprintf ("
");
}
else{
if((((i%2)==0)&&((j%2)==0)) || (((i%2)==1)&&((j%2)==1)))
textbackground(CScB);
else if ((((i%2)==0)&&((j%2)==1)) || (((i%2)==1)&&((j%2)==0)))
textbackground(CScN);
if(GetPezzo(i,j) -> GetColore() == nero)
textcolor(CPzN);
else
textcolor(CPzB);
if (PzCurr -> GetColore() == 0)
cprintf(" N_%c ", (PzCurr -> GetNome()));
else
cprintf(" B_%c ", (PzCurr -> GetNome()));
}
}
cout <<"\n\t
";
for (int j=0;j<DIM;j++){
if((((i%2)==0)&&((j%2)==0)) || (((i%2)==1)&&((j%2)==1)))
textbackground(CScB);
else if ((((i%2)==0)&&((j%2)==1)) || (((i%2)==1)&&((j%2)==0)))
textbackground(CScN);
cprintf ("
");
}
textbackground(BLACK);
textcolor(CTxt);
cprintf(" %d", i);
}
cout << "\n\t";
cprintf("
0
cout << "\n";
1
2
3
4
5
6
7");
}
22
// Stampa a video la scacchiera in modalità testo
void Scacchiera::StampaText() const
{
cout << "\n
0
1
2
3
4
5
6
7\n";
cout << "
_____ _____ _____ _____ _____ _____ _____ _____\n";
for(int i = 0; i < DIM; i++)
{
cout << i << " |";
for(int j = 0; j < DIM; j++)
{
Pezzo *PzCurr = GetPezzo(i,j);
if (PzCurr == NULL)
cout << "
|";
else
if (PzCurr -> GetColore() == 0)
cout << " N_" << (PzCurr -> GetNome()) << " |";
else
cout << " B_" << (PzCurr -> GetNome()) << " |";
}
cout << " " << i;
cout << "\n |_____|_____|_____|_____|_____|_____|_____|_____|\n";
}
cout << "
0
1
2
3
4
5
6
7\n";
}
Pezzo.h
#ifndef PEZZO_H
#define PEZZO_H
#include "MyType.h"
class Scacchiera; //forward declaration
//########## Pezzo ##########
class Pezzo {
protected:
char Nome;
color Colore;
public:
virtual ~Pezzo();
char GetNome() const;
color GetColore() const;
virtual bool MossaValida(Scacchiera* SchCurr, int r, int c, int r_new, int c_new) = 0;
};
//########## Pedone ##########
class Pedone : public Pezzo {
public:
Pedone(color c);
bool MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new);
};
//########## Torre ##########
class Torre : public Pezzo {
public:
Torre(color c);
bool MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new);
};
//########## Cavallo ##########
class Cavallo : public Pezzo {
public:
Cavallo(color c);
bool MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new);
};
23
//########## Alfiere ##########
class Alfiere : public Pezzo {
public:
Alfiere(color c);
bool MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new);
};
//########## Regina ##########
class Regina : public Pezzo {
public:
Regina(color c);
bool MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new);
};
//########## Re ##########
class Re : public Pezzo {
public:
Re(color c);
bool MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new);
};
#endif
Pezzo.cpp
#include "Pezzo.h"
#include "Scacchiera.h"
#include <iostream>
using namespace std;
//########## Pezzo ##########
// Distruttore di Pezzo
Pezzo::~Pezzo()
{ }
// Ritorna il Nome del Pezzo
char Pezzo::GetNome() const { return(Nome); }
// Ritorna il Colore del Pezzo
color Pezzo::GetColore() const { return(Colore); }
//########## Pedone ##########
// Costruttore di Pedone
Pedone::Pedone(color c)
{
Nome = 'P';
Colore = c;
}
// Controlla se è valida la mossa per il Pedone
bool Pedone::MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new)
{
// PezzoNew: pezzo che occupa lo scacco SchCurr[r_new][c_new]
Pezzo *PezzoNew = SchCurr -> GetPezzo(r_new, c_new);
switch (Colore)
{
// Pedone nero (si muove dall'alto al basso della scacchiera)
case nero:
// Avanti di un passo
if ((r_new == r + 1) && (c_new == c) && (PezzoNew == NULL))
return true;
// Avanti di due passi se è la prima volta che viene mosso
if ((r == 1) && (r_new == r + 2) && (c_new == c) && (PezzoNew == NULL)
&& ((SchCurr -> GetPezzo(r + 1, c_new)) == NULL))
return true;
// Avanti di un passo in obliquo (per mangiare una pedina)
if ((r_new == r + 1) && ((c_new == c + 1) || (c_new == c - 1)) && (PezzoNew != NULL))
if (PezzoNew -> GetColore() == bianco)
return true;
24
break;
// Pedone bianco (si muove dal basso all'alto della scacchiera)
case bianco:
// Avanti di un passo
if ((r_new == r - 1) && (c_new == c) && (PezzoNew == NULL))
return true;
// Avanti di due passi se è la prima volta che viene mosso
if ((r == 6) && (r_new == r - 2) && (c_new == c) && (PezzoNew == NULL)
&& ((SchCurr -> GetPezzo(r - 1, c_new)) == NULL))
return true;
// Avanti di un passo in obliquo (per mangiare una pedina)
if ((r_new == r - 1) && ((c_new == c - 1) || (c_new == c + 1)) && (PezzoNew != NULL))
if (PezzoNew -> GetColore() == nero)
return true;
break;
}
return false;
}
//########## Torre ##########
// Cstruttore di Torre
Torre::Torre(color c)
{
Nome = 'T';
Colore = c;
}
// Controlla se è valida la mossa per la Torre
bool Torre::MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new)
{
int i, j;
// Spostamenti verticali
if (c_new == c)
{
// Dall'alto al basso
if (r_new > r)
{
for (j = r + 1, i = c; j <= r_new; j++)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if (j == r_new)
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
// Dal basso all'alto
else if (r_new < r)
{
for(j = r - 1, i = c; j >= r_new; j--)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if (j == r_new)
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
}
// Spostamenti orizzontali
else if (r_new == r)
{
// Da sx a dx
if (c_new > c)
25
{
for(j = r, i = c + 1; i <= c_new; i++)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if (i == c_new)
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
// Da dx a sx
else if (c_new < c)
{
for(j = r, i = c - 1; i >= c_new; i--)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if (i == c_new)
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
}
return false;
}
//########## Cavallo ##########
// Costruttore di Cavallo
Cavallo::Cavallo(color c)
{
Nome = 'C';
Colore = c;
}
// Controlla se è valida la mossa per il Cavallo
bool Cavallo::MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new)
{
// PezzoNew: pezzo che occupa lo scacco SchCurr[r_new][c_new]
Pezzo *PezzoNew = SchCurr -> GetPezzo(r_new, c_new);
// Un passo laterale a dx o sx e due passi avanti o indietro
if ((c_new == c + 1 || c_new == c - 1) && (r_new == r + 2 || r_new == r - 2))
if ((PezzoNew == NULL) || (PezzoNew -> GetColore() != Colore))
return true;
// Due passi laterali a dx o sx e un passo avanti o indietro
if ((c_new == c + 2 || c_new == c - 2) && (r_new == r + 1 || r_new == r - 1))
if ((PezzoNew == NULL) || (PezzoNew -> GetColore() != Colore))
return true;
return false;
}
//########## Alfiere ##########
// Costruttore di Alfiere
Alfiere::Alfiere(color c)
{
Nome = 'A';
Colore = c;
}
// Controlla se è valida la mossa per l'Alfiere
bool Alfiere::MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new)
{
int i, j;
if (c_new > c)
26
{
if (r_new > r)
{
// Controlla gli spostamenti obliqui da alto sx a basso dx
for (j = r + 1, i = c + 1; ( j <= r_new && i <= c_new); j++, i++)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if ((j == r_new) && (i == c_new))
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
else
{
// Controlla gli spostamenti obliqui da basso sx a alto dx
for(j = r - 1, i = c + 1; (j >= r_new && i <= c_new); j--, i++)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if ((j == r_new) && (i == c_new))
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
}
else if (c_new < c)
{
if (r_new > r)
{
// Controlla gli spostamenti obliqui da alto dx a basso sx
for(j = r + 1, i = c - 1; (j <= r_new && i >= c_new); j++, i--)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if ((j == r_new) && (i == c_new))
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
else
{
// Controlla gli spostamenti obliqui da basso dx ad alto sx
for(j = r - 1, i = c - 1; (j >= r_new && i >= c_new); j--, i--)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if ((j == r_new) && (i == c_new))
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
}
return false;
}
27
//########## Regina ##########
// Costruttore di Regina
Regina::Regina(color c)
{
Nome = 'Q';
Colore = c;
}
// Controlla se è valida la mossa per la Regina
bool Regina::MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new)
{
int i, j;
if (r_new == r || c_new == c)
{
//--- Esegue un movimento simile alla Torre --if (c_new == c) // Spostamenti verticali
{
if (r_new > r) // Dall'alto al basso
{
for (j = r + 1, i = c; j <= r_new; j++)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if (j == r_new)
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
else if (r_new < r) // Dal basso all'alto
{
for(j = r - 1, i = c; j >= r_new; j--)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if (j == r_new)
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
}
else if (r_new == r) // Spostamenti orizzontali
{
if (c_new > c) // Da sx a dx
{
for(j = r, i = c + 1; i <= c_new; i++)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if (i == c_new)
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
else if (c_new < c) // Da dx a sx
{
for(j = r, i = c - 1; i >= c_new; i--)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if (i == c_new)
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
28
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
}
}
else
{
//--- Esegue un movimento simile
if (c_new > c)
{
if (r_new > r)
{
// Controlla gli spostamenti
for (j = r + 1, i = c + 1; (
{
Pezzo *PezzoCurr = SchCurr
all'Alfiere ---
obliqui da alto sx a basso dx
j <= r_new && i <= c_new); j++, i++)
-> GetPezzo(j, i);
if ((j == r_new) && (i == c_new))
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
else
{
// Controlla gli spostamenti obliqui da basso sx a alto dx
for(j = r - 1, i = c + 1; (j >= r_new && i <= c_new); j--, i++)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if ((j == r_new) && (i == c_new))
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
}
else if (c_new < c)
{
if (r_new > r)
{
// Controlla gli spostamenti obliqui da alto dx a basso sx
for(j = r + 1, i = c - 1; (j <= r_new && i >= c_new); j++, i--)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if ((j == r_new) && (i == c_new))
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
else
{
// Controlla gli spostamenti obliqui da basso dx ad alto sx
for(j = r - 1, i = c - 1; (j >= r_new && i >= c_new); j--, i--)
{
Pezzo *PezzoCurr = SchCurr -> GetPezzo(j, i);
if ((j == r_new) && (i == c_new))
{
if ((PezzoCurr != NULL) && (PezzoCurr -> GetColore() == Colore))
return false;
29
return true;
}
else if (PezzoCurr != NULL)
return false;
}
}
}
}
return false;
}
//########## Re ##########
// Costruttore Re
Re::Re(color c)
{
Nome = 'K';
Colore = c;
}
// Controlla se è valida la mossa per la Re
bool Re::MossaValida(Scacchiera* SchCurr,int r, int c, int r_new, int c_new)
{
// PezzoNew: pezzo che occupa lo scacco SchCurr[r_new][c_new]
Pezzo *PezzoNew = SchCurr -> GetPezzo(r_new,c_new);
if (((r_new == r + 1) || (r_new == r - 1) || (r_new == r)) &&
((c_new == c + 1) || (c_new == c - 1) || (c_new == c)))
if ((PezzoNew == NULL) || (PezzoNew -> GetColore() != Colore))
return true;
return false;
}
MyException.h
#ifndef MYEXCEPTION_H
#define MYEXCEPTION_H
#include <exception>
#include <string>
using namespace std;
//########## MyException ##########
class MyException : public exception {
string tipoErr;
public:
MyException() { tipoErr = "Errore!"; }
MyException(string s) { tipoErr = s; }
string getError() { return tipoErr; }
};
#endif
MyType.h
#ifndef MYTYPE_H
#define MYTYPE_H
// Definisce i due colori possibili per il colore
// degli scacchi e delle pedine.
typedef enum {nero, bianco} color;
// Definisce i possibili risultati ottenibili da una mossa
typedef enum {ok, errore, scacco, matto} ResultMossa;
#endif
30
Testing del programma
La tecnica di testing utilizzata per il programma sono state due: la white box e la black box.
La white box è stata applicata a classi prese singolarmente. Infatti è più semplice effettuare questa
tipologia di testing su parti relativamente piccole di codice.
La tipologia di testing white box utilizzata è stato il collaudo per condizioni. In particolare si è
utilizzata la branch testing.
Per la realizzazione di queste prove sono stati necessari programmi specifici scritti da noi.
Ad esempio per verificare se le mosse dei pezzi erano corretti è stato realizzato un programmino
che passava loro le coordinate è verificava se queste erano giuste. Le coordinate venivano passate in
modo da ottenere condizioni in alcuni casi vere ed in altre false. Ad esempio per provare le mosse
della Torre abbiamo inserito mosse per spostamenti orizzontali da destra a sinistra e viceversa,
mosse verticali dall’alto al basso e viceversa(ved. codice sorgente Torre.MossaValida). Per poi
introdurre degli spostamenti non validi(presenza di un pezzo tra la coordinata di partenza e quella di
destinazione) o coordinate che lasciavano inalterata la posizione del pezzo. Così facendo abbiamo
testato tutte le condizioni. Questo procedimento è stato effettuato per tutti i pezzi. Ci si è soffermati
soprattutto sul metodo MossaValida di ogni singolo Pezzo perché questo metodo è molto utilizzato.
Sarebbe stato più impegnativo testare tutte le mosse a progetto quasi terminato. Inoltre non è stato
possibile avere una copertura del 100% per motivi di tempo.
Poi si è passati ad un testing di tipo black box. Sono state effettuate simulazioni di partite in modo
da verificare il funzionamento e la facilità di utilizzo del programma. Questa tipologia di test è stata
effettuata ogni volta che si inseriva una particolare funzionalità in modo da individuare
malfunzionamenti dovuti a problemi di integrazione tra “moduli”.
Oltre ad essere testato da noi il programma è stato provato dal fratello di Davide che ha
rappresentato il nostro Beta test (lo ringraziamo per il tempo prestatoci) .
Di seguito vengono riportate alcune schermate del programma che riportano alcuni casi di test.
I° Testing.Creazione di una partita e inserimento di una mossa
CHESS MASTER
Menù principale
Nuova partita
--> 1
Carica partita --> 2
Esci
--> 3
Inserisci la tua scelta: 1
Inserisci il nome del giocatore nero: gio1
Inserisci il nome del giocatore bianco: gio2
Seleziona chi sarà il primo ad effettuare la mossa:
Nero
Bianco
Random
--> 1
--> 2
--> 3
Effettua la tua scelta: 1
Inserisci modalità grafica della scacchiera(c=colore,t=text): c
31
CHESS MASTER
Premi : 's'
'e'
'w'
altro
per
per
per
per
salvare
tornare al menu'
salvare e tornare al menu'
continuare
Mossa al NERO(gio1)
Inserisci le coordinate del pezzo da muovere:
Riga: 1
Colonna: 1
Inserisci nuove coordinate del pezzo:
Riga: 3
Colonna: 1
CHESS MASTER
II° Testing.Carica di una partita e inserimento di una mossa che causa scacco.
Introduzione di una mossa valida che non risolve però lo scacco.
Introduzione di una mossa che risolve lo scacco.
CHESS MASTER
Menù principale
Nuova partita
--> 1
Carica partita --> 2
Esci
--> 3
Inserisci la tua scelta: 2
Immettere il nome della partita salvata: prova
Inserisci modalità grafica della scacchiera(c=colore,t=text): c
32
CHESS MASTER
Premi : 's'
'e'
'w'
altro
per
per
per
per
salvare
tornare al menu'
salvare e tornare al menu'
continuare
Mossa al NERO(gio1)
Inserisci le coordinate del pezzo da muovere:
Riga: 2
Colonna: 1
Inserisci nuove coordinate del pezzo:
Riga: 6
Colonna: 5
CHESS MASTER
RE BIANCO SOTTO SCACCO!!
Mossa al BIANCO(gio2)
Inserisci le coordinate del pezzo da muovere:
Riga: 7
Colonna: 3
Inserisci nuove coordinate del pezzo:
Riga: 0
Colonna: 3
33
CHESS MASTER
MOSSA NON VALIDA A CAUSA DELLO SCACCO!!
Mossa al BIANCO(gio2)
Inserisci le coordinate del pezzo da muovere:
Riga: 7
Colonna: 4
Inserisci nuove coordinate del pezzo:
Riga: 6
Colonna: 5
CHESS MASTER
III° Testing.Inserimento di coordinate errate
Inserimento di una mossa errata.
CHESS MASTER
34
Mossa al NERO(gio1)
Inserisci le coordinate del pezzo da muovere:
Riga: 8
Ultimo valore errato. Reinserirlo: 7
Colonna: 3
Inserisci nuove coordinate del pezzo:
Riga: 5
Colonna: 2
CHESS MASTER
MOSSA ERRATA!!
IV° Testing.Inserimento di opzioni errate.
Carica di un file non presente e di file con dati non formattati correttamente.
CHESS MASTER
Menù principale
Nuova partita
--> 1
Carica partita --> 2
Esci
--> 3
Inserisci la tua scelta: 2
Immettere il nome della partita salvata: NonPresente
Errore! File non presente o danneggiato!
Menù principale
Nuova partita
--> 1
Carica partita --> 2
Esci
--> 3
Inserisci la tua scelta: 2
Immettere il nome della partita salvata: TurnoErrato
Valore turno errato!
Menù principale
Nuova partita
--> 1
Carica partita --> 2
Esci
--> 3
Inserisci la tua scelta: 2
35
Immettere il nome della partita salvata: PezzoErrato
Pezzo/i errato/i!
Menù principale
Nuova partita
--> 1
Carica partita --> 2
Esci
--> 3
Inserisci la tua scelta: 2
Immettere il nome della partita salvata: SpazioInPiù
Formatazione file errata!
Menù principale
Nuova partita
--> 1
Carica partita --> 2
Esci
--> 3
Inserisci la tua scelta: 2
Immettere il nome della partita salvata: CoordinataErrata
Errore in lettura della riga!
Menù principale
Nuova partita
--> 1
Carica partita --> 2
Esci
--> 3
Inserisci la tua scelta: 2
Immettere il nome della partita salvata: PezziSovrapposti
Uno o più pezzi sono sovrapposti!
V° Testing.Salvataggio di una partita e inserimento di una mossa che causa scacco matto.
CHESS MASTER
Premi : 's'
'e'
'w'
altro
per
per
per
per
salvare
tornare al menu'
salvare e tornare al menu'
continuare
Immettere il nome da assegnare alla partita: ProvaMatto
Mossa al BIANCO(gio2)
Inserisci le coordinate del pezzo da muovere:
Riga: 7
Colonna: 3
36
Inserisci nuove coordinate del pezzo:
Riga: 3
Colonna: 7
CHESS MASTER
PARTITA FINITA!!VINCE gio2!!
Premi un tasto per tornare al menù principale!
FILE ALLEGATI:
• File sorgenti: Main.cpp
Partita.h
Partita.cpp
Giocatore.h
Giocatore.cpp
Scacchiera.h
Scacchiera.cpp
Pezzo.h
Pezzo.cpp
MyException.h
MyType.h
• File di progetto C++BuilderX: Chess_master.cbx
• File eseguibile: Chess_master.exe (nella directory
“windows/Debug_Build/”)
• Alcune partite salvate:
(nella directory “windows/Debug_Build/save/”)
37