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