// Implementatie van klasse AapjeOmino #include #include #include #include #include #include "standaard.h" #include "aapjeomino.h" #include "steen.h" using namespace std; //************************************************************************* // Verwijdert het actuele spel tot een beginstand waaruit een nieuw spel // gecre�erd kan worden. void AapjeOmino::verwijderSpel() { for(int i = 0; i < MaxDimensie; i++) { for(int j = 0; j < MaxDimensie; j++) { bord[i][j].first = -1; bord[i][j].second = -1; } } // maakt elk paar van het bord (-1,-1) alleStenen.clear(); //maakt speler1.clear(); //alle speler2.clear(); //vectoren pot.clear(); //leeg aanBeurt = 1; // speler1 begint het spel hoogte = breedte = nrStenen = -1; gepaktUitPot = false; } // verwijderSpel //************************************************************************* //constructor van AapjeOmino AapjeOmino::AapjeOmino() { verwijderSpel(); // eerste beginsituatie } // default constructor //************************************************************************* // Controleert of waardes van hoogte, breedte, stenen in hand, stenen in het spel // en de positie van de eerste steen binnen de juiste grenzen vallen bool AapjeOmino::controleerSpel(int hoogte0, int breedte0, int nrStenenInHand0, int nrStenen0, int positieX, int positieY) { if(!integerInBereik("hoogte", hoogte0, 0, (MaxDimensie-1)) || !integerInBereik("breedte", breedte0, 0, (MaxDimensie-1))) { return false; } // of hoogte en breedte klopt if(nrStenen0 < 3 || nrStenen0 < ((nrStenenInHand0 * 2) + 1)) { cout << "Er zijn te weinig stenen in het spel om het het spel te starten." << endl; return false; } // of het aantal stenen klopt if(!integerInBereik("positieX", positieX, 0, (MaxDimensie-1)) || !integerInBereik("positieY", positieY, 0, (MaxDimensie-1))) { return false; } // of de positie van de eerste steen in het bord klopt return true; } // controleerSpel //************************************************************************* // returned aanBeurt zodat het in main gebruikt kan worden int AapjeOmino::getAanBeurt() { return aanBeurt; } //getAanBeurt //************************************************************************* // Returned de vector van de hand van de speler die op dit moment aan de beurt is vector AapjeOmino::spelerAanBeurt() { if(aanBeurt == 1) { return speler1; } return speler2; } // spelerAanBeurt //************************************************************************* // Doet een steen in het spel de parameters noord0, oost0, zuid0 en west0 zijn de // integers van de kanten van de steen respectievelijk. De parameter teller wordt // het nummer van de steen. positieX en positieY geven de positie van de steen op // het bord als dit een startsteen is, dus als het de eerste steen is teller = 0). // Verder is er nog een boolean opPot, als deze true retourneert dan gaat de steen // op de pot, anders is het een startsteen of komt deze in de hand van een speler. void AapjeOmino::doeSteenInSpel(int noord0, int oost0, int zuid0, int west0, int teller, bool opPot, int positieX, int positieY) { Steen hulp; hulp.setWaarden(noord0, oost0, zuid0, west0, teller); alleStenen.push_back(hulp); if(teller == 0) { pair startsteen(hulp.getNummer(),0); bord[positieX][positieY] = startsteen; } // startsteen else if(!opPot) { if(teller % 2 == 1) { speler1.push_back(hulp); } // in de hand van speler1 als teller oneven is else { speler2.push_back(hulp); } // in de hand van speler2 als teller even is } else { pot.push_back(hulp); } // op de pot } // doeSteenInSpel //************************************************************************* // Retourneert het cijfer van een kant van een steen op het bord. // De parameter nrSteen geeft de nummer van de steen. De parameter rotatie geeft // de rotatie van deze steen. De integer kant geeft de kant van deze steen // met als 0 de bovenkant, 1 rechts, 2 onder en 3 links int AapjeOmino::vindCijfer(int nrSteen, int rotatie, int kant) { if((rotatie == 0 && kant == 0) || (rotatie == 1 && kant == 3) || (rotatie == 2 && kant == 2) || (rotatie == 3 && kant == 1)) { return alleStenen.at(nrSteen).getNoord(); } else if((rotatie == 0 && kant == 1) || (rotatie == 1 && kant == 0) || (rotatie == 2 && kant == 3) || (rotatie == 3 && kant == 2)) { return alleStenen.at(nrSteen).getOost(); } else if((rotatie == 0 && kant == 2) || (rotatie == 1 && kant == 1) || (rotatie == 2 && kant == 0) || (rotatie == 3 && kant == 3)) { return alleStenen.at(nrSteen).getZuid(); } else if((rotatie == 0 && kant == 3) || (rotatie == 1 && kant == 2) || (rotatie == 2 && kant == 1) || (rotatie == 3 && kant == 0)) { return alleStenen.at(nrSteen).getWest(); } return -1; } // vindCijfer //************************************************************************* // Leest uit een file, dat is meegegeven, het spel in en controleert daarbij // eerst of de waardes aan alle eisen voldoen en als alles lukt returned die true bool AapjeOmino::leesIn (const char* invoernaam) { ifstream invoer; int beginaantal, positieX, positieY, teller = 0, // voor de waardes uit de file noord0, oost0, zuid0, west0; // deze houden de cijfers voor elke steen bij invoer.open(invoernaam, ios::in); if(invoer.fail()) { cout << "File kan niet geopend worden." << endl; return false; } invoer >> hoogte; invoer >> breedte; invoer >> nrStenen; invoer >> beginaantal; invoer >> positieX; invoer >> positieY; if(!controleerSpel(hoogte, breedte, beginaantal, nrStenen, positieX, positieY)) { return false; } // een van de waardes voldoet niet aan de eisen while(teller < nrStenen) { invoer >> noord0; invoer >> oost0; invoer >> zuid0; invoer >> west0; if(teller > (beginaantal + beginaantal)) { doeSteenInSpel(noord0, oost0, zuid0, west0, teller, true, -1, -1); } // stop de steen in de pot else { doeSteenInSpel(noord0, oost0, zuid0, west0, teller, false, positieX, positieY); } // stop de steen in de hand van een van de spelers teller++; } // voert alle stenen uit de file in het spel return true; } // leesIn //************************************************************************* // controleert of er een eindstand is bereikt en returned true als dat zo is, // anders returned die false bool AapjeOmino::eindstand () { if(speler1.empty() || speler2.empty() || (bepaalMogelijkeZetten().empty() && pot.empty())) { return true; } // eindstand is in deze situaties bereikt return false; } // eindstand //************************************************************************* // Drukt de stenen in de hand van een speler of in de pot af. void AapjeOmino::drukAfHand(int speler) { vector handSpeler; //vector met stenen die afgedrukt gaan worden int aantalStenenHand; if(speler != 0) { handSpeler = spelerAanBeurt(); if(speler == 1) { cout << " Hand speler1:" << endl; } else { cout << " Hand speler2:" << endl; } } else { cout << " Pot:" << endl; handSpeler = pot; } // pot aantalStenenHand = handSpeler.size(); for(int regel = 0; regel < 3; regel++) { for(int k = 0; k < aantalStenenHand; k++) { if(regel == 0) { cout << " " << handSpeler.at(k).getNummer() << ": " << handSpeler.at(k).getNoord() << " "; } // noord if(regel != 0 && handSpeler.at(k).getNummer() >= 10) { cout << " "; } if(regel == 1) { cout << " " << handSpeler.at(k).getWest() << " " << handSpeler.at(k).getOost(); } // oost en west if(regel == 2) { cout << " " << handSpeler.at(k).getZuid() << " "; } // zuid } cout << endl; } // drukt de hand af cout << endl; } // drukAfHand //************************************************************************* // Drukt het bord af met de stenen die er momenteel op zitten void AapjeOmino::drukAfBord() { int nrSteen, rotatie, regel; // teller voor in welke regel we zitten van het afdrukken van // een steen. ��n steen neemt 3 regels in, de eerste voor de // noordkant, de tweede voor oost en west en de derde voor zuid. cout << " "; for(int k = 0; k < breedte; k++) { cout << " " << k; } //de waardes van de kolommen af cout << endl << endl; for(int i = 0; i < hoogte; i++) { for(regel = 0; regel < 3; regel++) { if(regel == 1) { cout << " " << i << " "; } //drukt de waardes van de rijen af else { cout << " "; } for(int j = 0; j < breedte; j++) { nrSteen = bord[i][j].first; rotatie = bord[i][j].second; if(regel == 0 || regel == 2) { if(nrSteen == -1 || rotatie == -1) { cout << " - "; } // geen steen op deze plek else { cout << " " << vindCijfer(nrSteen, rotatie, regel) << " "; } // noordkant of zuidkant steen } else { if(nrSteen == -1 || rotatie == -1) { cout << " - -"; } // geen steen else { cout << " " << vindCijfer(nrSteen, rotatie, 3) << " " << vindCijfer(nrSteen, rotatie, regel); } // oost- �n westkant steen } } cout << endl; } cout << endl; } } // drukAfBord //************************************************************************* // Drukt het hele bord af met de stenen die er op liggen en geeft de stand van het // spel op dit moment: de stenen in de handen van de spelers en in de pot. void AapjeOmino::drukAf() { cout << endl; drukAfBord(); cout << " AAN DE BEURT: "; if(aanBeurt == 1) { cout << "speler1" << endl; } else { cout << "speler2" << endl; } cout << endl; drukAfHand(0); // pot cout << endl; drukAfHand(1); // speler1 cout << endl; drukAfHand(-1); // speler2 } // drukAf //************************************************************************* // Checkt hoeveel toegestane buren de steen zou hebben bij deze zet. Met toegestane // buren wordt bedoelt de buren die volgens de regels aan de steen aansluiten. // Het retourneert -1 als er geen stenen om deze plek zitten en 0 als er wel stenen // omheen zitten, maar deze niet sluiten aan de gegeven steen en anders als de zet // wel kan dan retourneert het het aantal buren. int AapjeOmino::zetToegestaanBuren(int i, int j, int nrSteen, int rotatie) { int aantalToegestaanBuren = 0, // telt het aantal buren noord, oost, zuid, west, // geeft de kanten van de steen van de zet boven, rechts, onder, links; // geeft de cijfers waaraan deze steen aansluit noord = oost = zuid = west = boven = rechts = onder = links = -1; if((i+1 < hoogte) && (bord[i+1][j].first != -1)) { onder = vindCijfer(bord[i+1][j].first, bord[i+1][j].second, 0); zuid = vindCijfer(nrSteen, rotatie, 2); aantalToegestaanBuren++; } if((j+1 < breedte) && (bord[i][j+1].first != -1)) { rechts = vindCijfer(bord[i][j+1].first, bord[i][j+1].second, 3); oost = vindCijfer(nrSteen, rotatie, 1); aantalToegestaanBuren++; } if((i-1 >= 0) && (bord[i-1][j].first != -1)) { boven = vindCijfer(bord[i-1][j].first, bord[i-1][j].second, 2); noord = vindCijfer(nrSteen, rotatie, 0); aantalToegestaanBuren++; } if((j-1 >= 0) && (bord[i][j-1].first != -1)) { links = vindCijfer(bord[i][j-1].first, bord[i][j-1].second, 1); west = vindCijfer(nrSteen, rotatie, 3); aantalToegestaanBuren++; } // per plek die aansluit worden de waardes ingevuld als hier een steen zit if((boven == -1) && (rechts == -1) && (onder == -1) && (links == -1)) { return -1; } // checkt of de stenen eromheen leeg zijn, dan kan de zet sowieso niet else if((boven == noord) && (rechts == oost) && (onder == zuid) && (links == west)) { return aantalToegestaanBuren; } // checkt of de zet kan, dus of de steen aansluit op de stenen er omheen else { return 0; } // anders kan de zet niet, dus zijn er 0 toegestaneBuren } // zetToegestaanBuren //************************************************************************* // Bepaalt alle mogelijke zetten die de speler die nu aan de beurt is kan doen met // de stenen die hij/zij in de hand heeft en zet deze vervolgens in een vector vector AapjeOmino::bepaalMogelijkeZetten () { vector mogelijkeZetten; vector handSpeler; // stenen in de hand van de speler aan de beurt int aantalStenenHand, nrSteen, temp; handSpeler = spelerAanBeurt(); aantalStenenHand = handSpeler.size(); for(int i = 0; i < hoogte; i++) { for(int j = 0; j < breedte; j++) { if(bord[i][j].first == -1) { for(int k = 0; k < aantalStenenHand; k++) { nrSteen = handSpeler.at(k).getNummer(); temp = zetToegestaanBuren(i, j, nrSteen, 0); if(temp != -1) { if(temp > 0) { mogelijkeZetten.push_back(Zet()); mogelijkeZetten.back().setWaardes(nrSteen, 0, i, j); } // rotatie 0 if(zetToegestaanBuren(i, j, nrSteen, 1) > 0) { mogelijkeZetten.push_back(Zet()); mogelijkeZetten.back().setWaardes(nrSteen, 1, i, j); } // rotatie 1 if(zetToegestaanBuren(i, j, nrSteen, 2) > 0) { mogelijkeZetten.push_back(Zet()); mogelijkeZetten.back().setWaardes(nrSteen, 2, i, j); } // rotatie 2 if(zetToegestaanBuren(i, j, nrSteen, 3) > 0) { mogelijkeZetten.push_back(Zet()); mogelijkeZetten.back().setWaardes(nrSteen, 3, i, j); } // rotatie 3 } // als er geen stenen naast de plek zijn, kan er toch geen zet } } } } // checkt alle zetten voor elke steen per mogelijke lege plek op het bord return mogelijkeZetten; } // bepaalMogelijkeZetten //************************************************************************* // Haalt een steen uit de pot naar de hand van de speler die aan de beurt is en // retourneert het nummer van deze steen. Maar doet eerst een paar controles om te // kijken of het mag int AapjeOmino::haalSteenUitPot () { Steen steenVanPot; //steen die uit de pot gehaald wordt if(pot.empty()) { cout << "De pot is leeg!" << endl; return -1; } if (gepaktUitPot) { cout << "U heeft deze beurt al uit de pot gepakt!" << endl; return -1; } if (!bepaalMogelijkeZetten().empty()) { cout << "Er kan niet van de pot gepakt worden, " << "want u heeft nog een mogelijke zet." << endl; return -1; } steenVanPot = pot.front(); if(aanBeurt == 1) { speler1.push_back(pot.front()); // steen in de hand doen } else { speler2.push_back(pot.front()); // steen in de hand doen } pot.erase(pot.begin()); // steen uit de pot verwijderen gepaktUitPot = true; return steenVanPot.getNummer(); } // haalSteenUitPot //************************************************************************* // Wisselt de speler die aan de beurt is en reset gepaktUitPot naar false. // speler1 is aanBeurt = 1 en speler2 is aanBeurt = -1. void AapjeOmino::wisselSpeler () { aanBeurt *= -1; gepaktUitPot = false; } // wisselSpeler //************************************************************************* // Controleert of de speler die aan de beurt is, wel de steen van steennummer // in de hand heeft en als dat zo is geeft die de positie terug waar de steen in de // hand ligt, anders geeft die -1 terug int AapjeOmino::steenInHand(int steennummer) { vector handSpeler = spelerAanBeurt(); // alle stenen van speler aan beurt int spelerAantal = handSpeler.size(); for(int positie = 0; positie < spelerAantal; positie++) { if(handSpeler.at(positie).getNummer() == steennummer) { return positie; // speler heeft de steen in de hand } } return -1; // speler heeft niet de steen in de hand } // steenInHand //************************************************************************* // Voert de zet 'zet' uit als de zet is toegestaan en als het toegestaan en gelukt // is dan wordt true gereturned anders false bool AapjeOmino::doeZet (Zet zet) { bool mogelijkeZet = false; vector mogelijkeZetten = bepaalMogelijkeZetten(); int aantalMogelijkeZetten = mogelijkeZetten.size(); int positieSteenInHand = steenInHand(zet.getI()); if(zet.getI() < 0 || zet.getI() >= nrStenen || zet.getR() < 0 || zet.getR() > 3 || zet.getKolom() >= breedte || zet.getRij() >= hoogte || zet.getKolom() < 0 || zet.getRij() < 0) { cout << "Deze zet kan niet worden uitgevoerd" << endl; return false; } // een of meerdere waardes zit(ten) buiten de grenzen if(positieSteenInHand == -1) { cout << "Deze steen zit niet in de hand" << endl; return false; } for(int i = 0; i < aantalMogelijkeZetten; i++) { if(mogelijkeZetten.at(i).getI() == zet.getI() && mogelijkeZetten.at(i).getR() == zet.getR()) { mogelijkeZet = true; } } // controleert of de zet een van de mogelijke zetten is if(!mogelijkeZet) { cout << "Deze zet kan niet op het bord" << endl; return false; } pair nieuwezet(zet.getI(), zet.getR()); bord[zet.getRij()][zet.getKolom()] = nieuwezet; if(aanBeurt == 1) { speler1.erase(speler1.begin()+positieSteenInHand); } // steen verwijderen uit de hand van de speler else { speler2.erase(speler2.begin()+positieSteenInHand); } // steen verwijderen uit de hand van de speler wisselSpeler(); // nu is de andere speler aan de beurt return true; } // doeZet //************************************************************************* // Een zet 'zet' die gemaakt is weer terugdraaien void AapjeOmino::unDoeZet(Zet zet) { int i = 0; pair oudePaar(-1, -1); // komt op de plek van zet in bord te staan int spelerAantal; wisselSpeler(); // eerste de spelerbeurt terug omwisselen if(aanBeurt == 1) { spelerAantal = speler1.size(); while(i < spelerAantal && zet.getI() > speler1.at(i).getNummer()) { i++; } // plek vinden waar de steen in de hand moet worden neergelegd speler1.insert(speler1.begin()+i, alleStenen.at(zet.getI())); } else { spelerAantal = speler2.size(); while(i < spelerAantal && zet.getI() > speler2.at(i).getNummer()) { i++; } // plek vinden waar de steen in de hand moet worden neergelegd speler2.insert(speler2.begin()+i, alleStenen.at(zet.getI())); } bord[zet.getRij()][zet.getKolom()] = oudePaar; } // unDoeZet //************************************************************************* // Zet in de vector "zetten" alle zetten die het meeste aantal buren opleveren en // returned die zetten vector AapjeOmino::bepaalGoedeZetten () { vector zetten; // vector met alle goede zetten vector alleZetten = bepaalMogelijkeZetten(); // vector met mogelijke zetten Zet hulp; int teller; // telt hoeveel buren een zet heeft int meesteBuren = 0; // houdt bij wat het meeste aantal buren is tot dan toe // op een steen int alleZettenAantal = alleZetten.size(); for(int i = 0; i < alleZettenAantal; i++) { hulp = alleZetten.at(i); teller = zetToegestaanBuren(hulp.getRij(), hulp.getKolom(), hulp.getI(), hulp.getR()); if(teller > meesteBuren) { zetten.clear(); //maakt de vector leeg } //als deze zet meer buren heeft dan het vorige maximum if(teller >= meesteBuren) { zetten.push_back(alleZetten.at(i)); meesteBuren = teller; } //voeg de zet toe aan de vector met het resultaat } //voor elke zet kijken hoeveel buren de steen in de zet heeft return zetten; } // bepaalGoedeZetten //************************************************************************* // Berekent de eindscore voor de speler die is meegegeven int AapjeOmino::bepaalScore(int speler) { if(speler == 1) { return (speler2.size()-speler1.size()); } // score voor speler1 return (speler1.size()-speler2.size()); // score voor speler2 } // bepaalScore //************************************************************************* // Controleert of het een geldig steennummer is en stopt dan de steen in de pot void AapjeOmino::stopInPot(int steennr) { if(steennr != -1) { pot.insert(pot.begin(), alleStenen.at(steennr)); if(aanBeurt == 1) { speler1.pop_back(); } // steen uit de hand van de speler halen else { speler2.pop_back(); } // steen uit de hand van de speler halen gepaktUitPot = false; } } // stopInPot //************************************************************************* // Zet de begin waardes van besteZet en aantalStanden en roept dan berekenBesteScore // aan die de beste score berekent int AapjeOmino::besteScore (Zet &besteZet, long long &aantalStanden) { aantalStanden = 0; besteZet.setDefaultWaardes(); return berekenBesteScore(besteZet, aantalStanden); } // besteScore //************************************************************************* // Berekent recursief de beste score voor de speler die aan de beurt is door alle // mogelijke zetten af te gaan en uit te voeren en vervolgens weer terug te zetten. // In besteZet komt de beste zet te staan voor de speler die aan de beurt is en in // aantalStanden hoeveel standen er bekeken zijn int AapjeOmino::berekenBesteScore(Zet &besteZet, long long &aantalStanden) { vector zetten; // mogelijke zetten voor de speler int steenUitPot = -1; // houdt bij of en welke steen uit de pot is gehaald int resultaat = INT_MIN, score; // resultaat is de hoogste score op dat moment int aantalZetten; Zet hulpZet; // om te kijken of een zet de besteZet wordt hulpZet.setDefaultWaardes(); aantalStanden++; if(eindstand()) { return bepaalScore(aanBeurt); } // spel is in een eindstand en score wordt bereken else { if(bepaalMogelijkeZetten().empty()) steenUitPot = haalSteenUitPot(); zetten = bepaalMogelijkeZetten(); if(zetten.empty()) { wisselSpeler(); score = - berekenBesteScore(besteZet, aantalStanden); wisselSpeler(); // spelerbeurt weer terugdraaien stopInPot(steenUitPot); return score; } // als er nog steeds geen zet is nadat al uit de pot is gepakt // gaat de beurt naar de andere speler else { aantalZetten = zetten.size(); for(int i = 0; i < aantalZetten; i++) { doeZet(zetten.at(i)); score = - berekenBesteScore(besteZet, aantalStanden); unDoeZet(zetten.at(i)); // zet weer terugdraaien if(score >= resultaat) { resultaat = score; hulpZet = zetten.at(i); } // er is een betere score gevonden } besteZet = hulpZet; stopInPot(steenUitPot); // steen in pot stoppen als dat nodig is return resultaat; } } } // berekenBesteScore //************************************************************************* // Genereert een random spel met gegeven hoogte en breedte van het bord, aantal // stenen, aantal stenen in de hand van de spelers, rij en kolom van de startsteen // en een minimum en maximum getal waaruit de cijfers van de stenen bestaan, maar // checkt eerst of alle gegeven waardes voldoen. bool AapjeOmino::genereerRandomSpel(int hoogte0, int breedte0, int nrStenen0, int nrStenenInHand0, int rij0, int kolom0, int minGetal, int maxGetal) { int noord0, oost0, zuid0, west0; // integers voor de kanten if(minGetal > maxGetal) { cout << "minGetal kan niet groter zijn dan maxGetal" << endl; return false; } if(!controleerSpel(hoogte0, breedte0, nrStenenInHand0, nrStenen0, rij0, kolom0)) { return false; } verwijderSpel(); hoogte = hoogte0; breedte = breedte0; nrStenen = nrStenen0; for(int teller = 0; teller < nrStenen0; teller++) { noord0 = randomGetal(minGetal, maxGetal); oost0 = randomGetal(minGetal, maxGetal); zuid0 = randomGetal(minGetal, maxGetal); west0 = randomGetal(minGetal, maxGetal); if(teller > (nrStenenInHand0 + nrStenenInHand0)) { doeSteenInSpel(noord0, oost0, zuid0, west0, teller, true, -1, -1); } // de rest van de stenen met random getallen gaat in de pot else { doeSteenInSpel(noord0, oost0, zuid0, west0, teller, false, rij0, kolom0); } // doet stenen met random getallen in een hand of als startsteen op bord } return true; } //genereerRandomSpel //*************************************************************************