solving_aapjeomino / main.cc
main.cc
Raw
// 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;

}