// Hoofdprogramma voor oplossing voor eerste programmeeropdracht Algoritmiek, // voorjaar 2021: Aapje Omino. // // Biedt de gebruiker een menustructuur om // * een instantie van Aapje Omino in te lezen, en daarmee het spel te spelen, // waarbij de gebruiker steeds // - (zo nodig) een steen uit de pot kan halen // - een zet kan uitvoeren (een steen op het bord leggen) // - kan vragen om de `goede' zetten // - kan vragen om de score voor de speler die aan de beurt is, als beide // spelers vanaf dit moment optimaal verder spelen // * experimenten te doen, waarbij een gretige strategie met een optimale // strategie vergeleken wordt. // // Hanna Straathof, Jimmy Oei #include <iostream> #include <string> #include <ctime> // voor clock() en clock_t #include "standaard.h" #include "zet.h" #include "aapjeomino.h" #include "steen.h" using namespace std; const int MaxBestandsNaamLengte = 30; // maximale lengte van een bestandsnaam //************************************************************************* // Schrijf de zetten in vector zetten met een passende kop naar het scherm. void schrijfZetten (string kop, vector<Zet> zetten) { size_t nrZetten; cout << endl; cout << kop << endl; nrZetten = zetten.size(); if (nrZetten>0) { for (size_t i=0;i<nrZetten;i++) { cout << i << ": "; zetten[i].drukAf(); } } else cout << "Helaas, 0 zetten." << endl; } // schrijfZetten //************************************************************************* // Schrijf het menu op het scherm (afhankelijk van nrZetten), // en vraag een keuze van de gebruiker. // Retourneer: de keuze van de gebruiker int keuzeUitMenu (size_t nrZetten) { int keuze; cout << endl; if (nrZetten==0) cout << "1. Een steen uit de pot halen" << endl; else cout << "1. Een zet uitvoeren" << endl; cout << "2. De `goede' zetten bepalen" << endl; cout << "3. Optimale score bepalen" << endl; cout << "4. Stoppen met dit spel" << endl; cout << endl; cout << "Maak een keuze: "; cin >> keuze; return keuze; } // keuzeUitMenu //************************************************************************* // Speel het spel op het bord van ao1. // Hierbij krijgt de gebruiker herhaaldelijk de keuze om // * (zo nodig) een steen uit de pot te halen // * een zet uit te voeren (een steen op het bord leggen) // * te vragen om de `goede' zetten // * te vragen om de score voor de speler die aan de beurt is, als beide // spelers vanaf dit moment optimaal verder spelen // // Voor elke iteratie van het menu wordt de stand afgedrukt, met de mogelijke // zetten voor de huidige speler aan de beurt. // // Dit alles gaat door // * totdat er een eindstand is bereikt (een van de spelers heeft geen // stenen meer en/of de huidige speler kan niets meer doen) // * of totdat de gebruiker aangeeft dat hij wil stoppen met het spel void doeSpel (AapjeOmino *ao1) { int keuze, stnr, // steen die uit pot gehaald wordt zetNr, // nummer van de zet die gedaan moet worden score; // returnwaarde van besteScore long long aantalStanden; // aantal bekeken standen bij aanroep besteScore vector<Zet> zetten; size_t nrZetten; Zet besteZet; clock_t t1, t2; keuze = 0; while (keuze!=4 && !ao1->eindstand()) { ao1 -> drukAf(); zetten = ao1 -> bepaalMogelijkeZetten (); schrijfZetten ("Mogelijke zetten voor speler aan beurt zijn:", zetten); nrZetten = zetten.size(); keuze = keuzeUitMenu (nrZetten); switch (keuze) { case 1: if (nrZetten==0) { stnr = ao1 -> haalSteenUitPot (); if (stnr!=-1) // gelukt om een steen uit de pot te halen { zetten = ao1 -> bepaalMogelijkeZetten (); if (zetten.size()==0) ao1 -> wisselSpeler (); } } else { cout << endl; cout << "Geef het nummer van een mogelijke zet (0.." << nrZetten-1 << "): "; // ook als je hiervoor net de `goede' zetten hebt // opgevraagd, moet je kiezen uit alle mogelijke zetten cin >> zetNr; if (integerInBereik ("zetNr", zetNr, 0, nrZetten-1)) ao1->doeZet (zetten[zetNr]); } break; case 2: zetten = ao1 -> bepaalGoedeZetten (); schrijfZetten ("`Goede' zetten voor speler aan beurt zijn:", zetten); break; case 3: t1 = clock (); score = ao1 -> besteScore (besteZet, aantalStanden); t2 = clock (); cout << endl; cout << "Optimale score = " << score << endl; cout << "Een beste zet is: "; besteZet.drukAf(); cout << "We hebben hiervoor " << aantalStanden << " standen bekeken." << endl; cout << "Dit kostte " << (t2-t1) << " clock ticks, ofwel " << (((double)(t2-t1))/CLOCKS_PER_SEC) << " seconden." << endl; break; case 4: break; default: cout << endl; cout << "Voer een goede keuze in!" << endl; } // switch } // while if (ao1->eindstand()) { ao1 -> drukAf(); cout << endl; cout << "De huidige stand is een eindstand.\n"; } } // doeSpel //************************************************************************* // Voert een spel uit waarbij speler1 telkens een willekeurige 'goede zet' uitvoert // en speler2 een zet die een beste score oplevert en dit returned de score voor // speler1 int experimentScore(AapjeOmino *ao1) { vector<Zet> mogelijkeZetten; Zet besteZet; // int score; // houdt de score van besteScore bij maar daar gebeurd niks mee int steenUitPot = -1; // houdt bij welke steen uit de pot is maar // doet er niks mee long long aantalStanden; while(!(ao1->eindstand())) { if(ao1->bepaalMogelijkeZetten().empty()) { steenUitPot = ao1->haalSteenUitPot(); } // speler die aan de beurt is moet een steen uit de pot halen mogelijkeZetten = ao1->bepaalMogelijkeZetten(); if(mogelijkeZetten.empty()) { ao1->wisselSpeler(); } // speler die aan de beurt is, kan nog steeds niks doen en verliest beurt else if(ao1->getAanBeurt() == 1) { mogelijkeZetten = ao1->bepaalGoedeZetten(); ao1->doeZet(mogelijkeZetten.at(randomGetal(0, mogelijkeZetten.size()-1))); } // willekeurige goede zet wordt uitgevoerd als speler 1 aan de beurt is else { score = ao1->besteScore(besteZet, aantalStanden); ao1->doeZet(besteZet); } // de beste zet voor speler 2 wordt uitgevoerd als die aan de beurt is } return ao1->bepaalScore(1); //score bepalen voor speler 1 } // experimentScore //drukt de resultaten van de experimenten af void drukAfExperimenten(int teller, int totaalBesteEindstand, int totaalClockTicks, double totaalSeconden, int scoreExperiment, int N, long long totaalAantalStanden) { cout << endl << "Gemiddelde over 10 spel(len) voor N = " << N << ":" << endl << "- De beste eindscore voor speler 1: " << (totaalBesteEindstand/teller) << endl << "- Aantal bekeken standen: " << (totaalAantalStanden/teller) << endl << "- Het kostte " << ((double)totaalClockTicks/teller) << " clock ticks, ofwel " << (totaalSeconden/teller) << " seconden om de eindscore te berekenen." << endl << "- Score voor speler1 als die elke keer een willekeurige goede " << endl << " zet uitvoert en speler2 een zet die een optimale score " << "oplevert: " << (scoreExperiment/teller) << endl << endl; } // drukAfExperimenten // Voert de experimenten uit zoals beschreven in de opdracht. void doeExperimenten () { AapjeOmino *ao1 = new AapjeOmino; // pointer, om makkeijk nieuwe objecten // te kunnen maken en weer weg te gooien bool gelukt; // of het spel genereren gelukt is int besteEindstand, totaalBesteEindstand = 0, totaalClockTicks = 0, scoreExperiment = 0, teller, N = 0; Zet zet; long long aantalStanden, totaalAantalStanden = 0; clock_t t1, t2; double totaalSeconden = 0; while(N != -1) { cout << "Met hoeveel stenen moet een spel gegenereerd worden? " << "(-1 is stoppen en meer dan 16 wordt niet aangeraden) "; cin >> N; if(N >= 8 && N < 30) { teller = 0; for(int i = 0; i < 10; i++) { gelukt = ao1->genereerRandomSpel(7, 7, N, (N/4), 3, 3, 1, N); if(gelukt) { t1 = clock(); besteEindstand = ao1->besteScore(zet, aantalStanden); t2 = clock(); totaalClockTicks += (t2-t1); totaalSeconden += (((double)(t2-t1))/CLOCKS_PER_SEC); totaalAantalStanden += aantalStanden; totaalBesteEindstand += besteEindstand; scoreExperiment += experimentScore(ao1); teller++; } // alleen als het spel goed is gegenereerd, wordt ermee gewerkt } if(teller > 0) { drukAfExperimenten(teller, totaalBesteEindstand, totaalClockTicks, totaalSeconden, scoreExperiment, N, totaalAantalStanden); } } } } // doeExperimenten //************************************************************************* void hoofdmenu () { AapjeOmino *ao1; // pointer, om makkeijk nieuwe objecten te kunnen maken // en weer weg te gooien int keuze; char invoernaam[MaxBestandsNaamLengte+1]; do { cout << endl; cout << "1. Een spel spelen" << endl; cout << "2. Experimenten doen" << endl; cout << "3. Stoppen" << endl; cout << endl; cout << "Maak een keuze: "; cin >> keuze; switch (keuze) { case 1: ao1 = new AapjeOmino (); cout << "Geef de naam van het tekstbestand met het spel: "; cin >> invoernaam; if (ao1 -> leesIn (invoernaam)) doeSpel (ao1); delete ao1; // netjes opruimen break; case 2: doeExperimenten (); break; case 3: break; default: cout << endl; cout << "Voer een goede keuze in!" << endl; } } while (keuze!=3); } // hoofdmenu //************************************************************************* int main () { hoofdmenu (); return 0; }