Zadanie zaliczeniowe z laboratorium programowania obiektowego
Laboratorium programowania obiektowego Zadanie zaliczeniowe 2 termin oddawania rozwiazan: 9 czerwca 2008 Bezszachy sa wariantem gry w szachy. Gra prowadzona jest przez dwoch graczy, nazywanych dalej bialym i czarnym, na szachownicy 8 na 8, za pomoca bierek szachowych. Poczatkowe ustawienie figur na szachownicy jest takie samo, jak w zwyklych szachach. Pionkow w bezszachach nie uzywa sie. Gracze wykonuja na przemian po jednym ruchu, zaczynajac od gracza bialego. Figury bezszachowe poruszaja sie po szachownicy tak samo, jak szachowe. Jedynym wyjatkiem jest brak mozliwosci roszady. W bezszachach, w odroznieniu od szachow, nie istnieja pojecia szachu i mata. Zabicie krola nie powoduje zakonczenia gry. Gra konczy sie zwyciestwem jednego z graczy, gdy uda mu sie zabic wszystkie bierki przeciwnika. Gra konczy sie remisem, gdy gracz nie moze wykonac zadnego legalnego ruchu, choc pozostaly mu bierki. Remis oglaszamy rowniez, gdy gracze wykonaja 1000 ruchow (po 500 ruchow kazdego gracza). Napisz w C++ program, ktory przeprowadzi partie bezszachow miedzy para graczy: * losowym, ktory za kazdym razem losowo wybiera jeden z dozwolonych w danej chwili ruchow. Wybor kazdego z legalnych ruchow powinien byc rownie prawdopodobny. Jesli np. gracz ma dwie bierki i jedna z nich moze wykonac 10 legalnych ruchow, a druga 5, to prawdopodobienstwo ruchu pierwsza bierka powinno byc dwa razy wieksze, niz druga. * leniwym, ktory sposrod bierek, ktorymi moze sie ruszyc wybiera ta, ktora znajduje sie najblizej jego krawedzi szachownicy (wiersz pierwszy dla bialego, osmy dla czarnego). Jesli wybrana bierka gracz moze wykonac bicie, robi to, jesli nie, wykonuje nia losowy ruch. Program powinien ruszac sie za obu graczy i, po kazdym ruchu, wypisywac na wyjscie stan szachownicy. Na zakonczenie partii nalezy wypisac informacje o wyniku - kto wygral, czy byl remis. Informacje dla osob, ktore nie znaja zasad gry w szachy: Poslugujemy sie piecioma rodzajami figur: * wieza - moze sie przesuwac po wierszu lub kolumnie szachownicy * goniec - moze sie przesuwac po przekatnych szachownicy * hetman - moze wykonywac ruchy takie, jak wieza lub goniec * krol - moze wykonac ruch na jedno z sasiednich pol, takze po przekatnej * skoczek - moze wykonac ruch, zmieniajac numer wiersza o 2 a kolumny o 1, lub na odwrot. Jest jedyna bierka, ktora moze "przeskoczyc" nad innymi. Jesli na polu, na ktore przestawiamy bierke, jest bierka przeciwnika, zabijamy ja, czyli zdejmujemy z szachownicy. Poczatkowo na szachownicy w pierwszym wierszu sa figury gracza bialego, kolejno: wieza, skoczek, goniec, hetman, krol, goniec, skoczek, wieza. W ostatnim wierszu znajduja sie figury gracza czarnego, w tej samej kolejnosci. Uwaga 1: zadanie trzeba rozwiazac obiektowo. Nalezy unikac powtarzania kodu lub pisania wielokrotnie podobnych fragmentow. Uwaga 2: hierarchia klas powinna umozliwiac latwe rozszerzenie o nowe rodzaje bierek i graczy. Pytania do tresci zadania prosze kierowac na adres: zaroda@mimuw.edu.pl
A oto rozwiązanie:
#include #include #include #include #include using namespace std; class Pionek; class Gracz; //struktura przechowujaca mozliwy ruch. typedef struct Ruch{ Pionek* pionek; int x; //Wiersz docelowy int y; //Kolumna docelowa int bicie; //Czy bicie. } Ruch; //dlugosc boku planszy #define ROZMIAR_PLANSZY 8 //Nadklasa dla Gracza. class Gracz { protected: char nawias_start; //Poczatek nawiasu zaznaczajacego gracza char nawias_stop; // Koniec tegoz. public: vector Pionki; //Pionki gracza pozostajace na planszy string nazwa_gracza; //Nazwa gracza. void Usun(Pionek*); //Usuwa pionek z listy pionkow. void inicjuj(string nazwa_gracza,char nawias_start,char nawias_stop); virtual int Ruch(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]); //Rusza graczem string Wypisz(string pionek); // Wypisuje pionek gracza int WykonajRuch(struct Ruch rusz,Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]); // Fizycznie wykonuje ruch }; //nadklasa dla pionka class Pionek { public: int x; //wiersz planszy int y;//kolumna planszy int WyprobujRuch(vector* ruchy,Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY],int x,int y); Gracz* gracz; virtual vector MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]); //Mozliwe ruchy pionka void Zbij(); //Usuwa pionek void inicjuj(int i,int j,Gracz* gracz); string nazwa; virtual string Wypisz(); }; // klasa główna - gra class Gra { private: int numer_ruchu; int ograniczenie; //Ograniczenie na ilosc ruchow. Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]; Gracz* gracz1; Gracz* gracz2; public: Gra(Gracz* gracz1,Gracz* gracz2,int ograniczenie) { this->gracz1=gracz1; this->gracz2=gracz2; this->ograniczenie=ograniczenie; for(int i=0;i<ROZMIAR_PLANSZY;i++) { for(int j=0;j<ROZMIAR_PLANSZY;j++) { plansza[i][j]=NULL; } } // umieszczanie pionkow na planszy. for(unsigned int i=0;i Pionki.size(); i++) { plansza[gracz1->Pionki.at(i)->x][gracz1->Pionki.at(i)->y]=gracz1->Pionki.at(i); } for(unsigned int i=0;i Pionki.size(); i++) { plansza[gracz2->Pionki.at(i)->x][gracz2->Pionki.at(i)->y]=gracz2->Pionki.at(i); } //pionki umieszczone. } int Ruch(); void Wypisz(); //Wypisuje stan planszy }; // Metody GRA int Gra::Ruch() { cout << "Ruch " << numer_ruchu << ": n"; if((numer_ruchu Ruch(plansza) && gracz2->Ruch(plansza)) { Wypisz(); numer_ruchu++; if(gracz1->Pionki.size()==0) { //wygral gracz 2. cout << "Wygral gracz '" <nazwa_gracza <Pionki.size()==0) { //wygral gracz 1. cout << "Wygral gracz '" <nazwa_gracza << "' Gratulacje! n"; return 0; } else { //Gramy dalej return 1; } } else { cout << "REMIS!! n"; return 0; } } void Gra::Wypisz() //Wypisuje stan planszy { int i,j,k; cout << "nn"; //gorna sciana planszy cout << "#"; for(k=0;k<ROZMIAR_PLANSZY;k++) { cout <<"##"<< k+1 <<"###"; } //kolejne wiersze planszy for (i=0;i<ROZMIAR_PLANSZY;i++) { cout << "n#"; for(k=0;k<ROZMIAR_PLANSZY;k++) { cout <<" #"; } cout << "n"<< (char)('A'+i); for(j=0;j<ROZMIAR_PLANSZY;j++) { cout<<' '; if(plansza[i][j]!=NULL) { //jest tu jakis pionek. cout <Wypisz(); } else { cout << " "; } cout << " #"; } cout << "n#"; for(k=0;k<ROZMIAR_PLANSZY;k++) { cout <<" #"; } cout << 'n'; for(k=0;k<ROZMIAR_PLANSZY;k++) { cout <<"######"; } cout << "#"; } cout <nazwa_gracza=nazwa_gracza; this->nawias_start=nawias_start; this->nawias_stop=nawias_stop; } int Gracz::WykonajRuch(struct Ruch rusz,Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]) { //przesuwa pionek, na razie bez bicia. string bicie=""; if(plansza[rusz.x][rusz.y]!=NULL) { //Cos tu jest. Zakladam ze bicie. plansza[rusz.x][rusz.y]->Zbij(); bicie=" (bicie)"; } cout << "Gracz '"<nazwa_gracza <<"': "<nazwa <<" z [" <x) <y+1 <<"] na [" << (char)('A'+rusz.x) << rusz.y+1 <<"]"<< bicie <x][rusz.pionek->y]=NULL; rusz.pionek->x=rusz.x; rusz.pionek->y=rusz.y; return 1; } void Gracz::Usun(Pionek* pionek) { //Usuwa pionek z listy. unsigned int i=0; while((i Pionki.size())&&(this->Pionki.at(i)!=pionek)) { i++; } if(i Pionki.size()) { //znaleziono //usuwamy. this->Pionki.erase(this->Pionki.begin()+i,this->Pionki.begin()+i+1); } } string Gracz::Wypisz(string pionek) { string wynik=""; wynik=nawias_start+pionek+nawias_stop; return wynik; } //Koniec metod GRACZ //METODY PIONEK vector Pionek::MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]) { vector zwroc; //Bez tego linker g++ się sypie. return zwroc; } void Pionek::inicjuj(int i,int j,Gracz* gracz) { this->x=i; this->y=j; this->gracz=gracz; gracz->Pionki.push_back(this); } string Pionek::Wypisz() { return gracz->Wypisz("|"); } void Pionek::Zbij() { //Jak nietrudno się domyślić zbija pionek. this->gracz->Usun(this); } int Pionek::WyprobujRuch(vector* ruchy,Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY],int i,int j) { Ruch gdzie; if((i>=ROZMIAR_PLANSZY)||(i=ROZMIAR_PLANSZY)||(jpush_back(gdzie); return 1; } else if(plansza[i][j]->gracz!=this->gracz) { //Obcy gracz gdzie.pionek=this; gdzie.x=i; gdzie.y=j; gdzie.bicie=1; ruchy->push_back(gdzie); return 0; } else { return 0; } } //Koniec metod pionek. // Wszystkie nadklasy zostaly zadeklarowane. Teraz zrobimy podklasy. // Najpierw gracze. class Losowy: public Gracz { public: int Ruch(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]); Losowy(string nazwa_gracza,char nawias_start,char nawias_stop) { inicjuj(nazwa_gracza,nawias_start,nawias_stop); } }; int Losowy::Ruch(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]) { vector tmp; vector mozliwe_ruchy; for(unsigned int i=0;i Pionki.size(); i++) { tmp=this->Pionki.at(i)->MozliweRuchy(plansza); mozliwe_ruchy.insert(mozliwe_ruchy.end(),tmp.begin(),tmp.end()); tmp.clear(); } if(mozliwe_ruchy.size()>0) { //jesli sa ruchy do wykonania. int losowa = rand()%(int)mozliwe_ruchy.size(); return WykonajRuch(mozliwe_ruchy[losowa],plansza); } else { return 0; } } class Leniwy: public Gracz { public: int krawedz ; // Krawedz okreslana jako 'nalezaca' do gracza. int Ruch(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]); Leniwy(string nazwa_gracza,char nawias_start,char nawias_stop,int krawedz) { this->krawedz=krawedz; inicjuj(nazwa_gracza,nawias_start,nawias_stop); } }; int Leniwy::Ruch(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]) { int najblizej = ROZMIAR_PLANSZY+1; //Odleglosc do najblizszej bierki. Pionek* najblizszy = NULL; //Najblizsza bierka vector tmp; vector mozliwe_ruchy; int odleglosc; for(unsigned int i=0;i Pionki.size(); i++) { tmp=this->Pionki.at(i)->MozliweRuchy(plansza); odleglosc=this->krawedz-(Pionki.at(i)->x); if(odleglosc0)&&(odlegloscMozliweRuchy(plansza); //Jego mozliwe ruchy. unsigned int i=0; while((i<tmp.size())&&(tmp.at(i).bicie==0)) { i++; } if(i<tmp.size()) //Znalezlismy ruch z biciem. { return WykonajRuch(tmp.at(i),plansza); } else // Nie ma bicia, ale losowy ruch. { int losowa = rand()%(int)tmp.size(); return WykonajRuch(tmp[losowa],plansza); } } else { return 0; } } // Teraz pionki. class Wieza:public Pionek { public: vector MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]); string Wypisz(); Wieza (int i,int j,Gracz* gracz) { inicjuj(i,j,gracz); this->nazwa="Wieża"; } }; vector Wieza::MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]) { vector ruchy; int i; //Skaczemy po wierszach i=y+1; while(WyprobujRuch(&ruchy,plansza,x,i)==1) { i++; } i=y-1; while(WyprobujRuch(&ruchy,plansza,x,i)==1) { i=i-1; } //Skaczemy po kolumnach i=x+1; while(WyprobujRuch(&ruchy,plansza,i,y)==1) { i++; } i=x-1; while(WyprobujRuch(&ruchy,plansza,i,y)==1) { i=i-1; } return ruchy; } string Wieza::Wypisz() { return gracz->Wypisz("W"); } class Skoczek:public Pionek { public: vector MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]); string Wypisz(); Skoczek (int i,int j,Gracz* gracz) { inicjuj(i,j,gracz); this->nazwa="Skoczek"; } }; vector Skoczek::MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]) { vector ruchy; WyprobujRuch(&ruchy,plansza,x+2,y-1); WyprobujRuch(&ruchy,plansza,x+2,y+1); WyprobujRuch(&ruchy,plansza,x-2,y-1); WyprobujRuch(&ruchy,plansza,x-2,y+1); WyprobujRuch(&ruchy,plansza,x+1,y+2); WyprobujRuch(&ruchy,plansza,x+1,y-2); WyprobujRuch(&ruchy,plansza,x-1,y+2); WyprobujRuch(&ruchy,plansza,x-1,y-2); return ruchy; } string Skoczek::Wypisz() { return gracz->Wypisz("S"); } class Krol:public Pionek { public: vector MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]); string Wypisz(); Krol (int i,int j,Gracz* gracz) { inicjuj(i,j,gracz); this->nazwa="Król"; } }; vector Krol::MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]) { vector ruchy; WyprobujRuch(&ruchy,plansza,x-1,y-1); WyprobujRuch(&ruchy,plansza,x,y-1); WyprobujRuch(&ruchy,plansza,x+1,y-1); WyprobujRuch(&ruchy,plansza,x-1,y); WyprobujRuch(&ruchy,plansza,x+1,y); WyprobujRuch(&ruchy,plansza,x-1,y+1); WyprobujRuch(&ruchy,plansza,x,y+1); WyprobujRuch(&ruchy,plansza,x+1,y+1); return ruchy; } string Krol::Wypisz() { return gracz->Wypisz("K"); } class Goniec:public Pionek { public: vector MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]); string Wypisz(); Goniec (int i,int j,Gracz* gracz) { inicjuj(i,j,gracz); this->nazwa="Goniec"; } }; vector Goniec::MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]) { vector ruchy; int i; //W prawy gorny rog i=1; while(WyprobujRuch(&ruchy,plansza,x+i,y+i)==1) { i++; } i=1; // W lewy dolny while(WyprobujRuch(&ruchy,plansza,x-i,y-i)==1) { i++; } //W prawy dolny i=1; while(WyprobujRuch(&ruchy,plansza,x+i,y-i)==1) { i++; } i=1; // W lewy gorny while(WyprobujRuch(&ruchy,plansza,x-i,y+i)==1) { i++; } return ruchy; } string Goniec::Wypisz() { return gracz->Wypisz("G"); } class Hetman:public Pionek { public: vector MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]); string Wypisz(); Hetman (int i,int j,Gracz* gracz) { inicjuj(i,j,gracz); this->nazwa="Hetman"; } }; vector Hetman::MozliweRuchy(Pionek* plansza[ROZMIAR_PLANSZY][ROZMIAR_PLANSZY]) { vector ruchy; int i; //W prawy gorny rog i=1; while(WyprobujRuch(&ruchy,plansza,x+i,y+i)==1) { i++; } i=1; //W lewy dolny while(WyprobujRuch(&ruchy,plansza,x-i,y-i)==1) { i++; } //W lewy dolny i=1; while(WyprobujRuch(&ruchy,plansza,x+i,y-i)==1) { i++; } i=1; // W lewy gorny while(WyprobujRuch(&ruchy,plansza,x-i,y+i)==1) { i++; } //Skaczemy po wierszach i=y+1; while(WyprobujRuch(&ruchy,plansza,x,i)==1) { i++; } i=y-1; while(WyprobujRuch(&ruchy,plansza,x,i)==1) { i=i-1; } //Skaczemy po kolumnach i=x+1; while(WyprobujRuch(&ruchy,plansza,i,y)==1) { i++; } i=x-1; while(WyprobujRuch(&ruchy,plansza,i,y)==1) { i=i-1; } return ruchy; } string Hetman::Wypisz() { return gracz->Wypisz("H"); } /**************************************************************** *********************** --- MAIN --- **************************** ****************************************************************/ int main(int argc, char **argv) { Gracz* gracz1; Gracz* gracz2; Gra* gierka; //Inicjalizujemy generator liczb pseudolosowych. srand(time(NULL)); //Tworzymy graczy gracz1 = new Losowy("Biały",'[',']'); gracz2 = new Leniwy("Czarny",'{','}',7); //Rozmieszczamy pionki gracza 1 new Wieza(0,0,gracz1); new Skoczek(0,1,gracz1); new Goniec(0,2,gracz1); new Hetman(0,3,gracz1); new Krol(0,4,gracz1); new Goniec(0,5,gracz1); new Skoczek(0,6,gracz1); new Wieza(0,7,gracz1); //Rozmieszczamy pionki gracza 2 new Wieza(7,0,gracz2); new Skoczek(7,1,gracz2); new Goniec(7,2,gracz2); new Hetman(7,4,gracz2); new Krol(7,3,gracz2); new Goniec(7,5,gracz2); new Skoczek(7,6,gracz2); new Wieza(7,7,gracz2); //Tworzymy gierka=new Gra(gracz1,gracz2,500); //No i siup! while(gierka->Ruch()) { } return 0; }