schedule_maker / rooster.cc
rooster.cc
Raw
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include "standaard.h"
#include "rooster.h"
#include "vak.h"
#include "docent.h"

using namespace std;

//*************************************************************************

 // Reset de zaalSymmetrie array vanaf een bepaalde tijdslot tot aan het
 // einde van de week, zodat de zaalSymmetrie hiervoor niet verloren gaat
void Rooster::resetZaalSymmetrie(int vanafTijdslot) {
    for(int i = vanafTijdslot; i < nrTijdsloten; i++) {
        for(int j = 0; j < nrVakken; j++) {
            zaalSymmetrie[i][j] = -1;
        }
    }
} // Rooster::resetZaalSymmetrie

//*************************************************************************

// Verwijdert de huidige opzet naar een stand waaruit
// een nieuwe roosteropzet kan worden ingelezen.
void Rooster::verwijderOpzetRooster() {
    docenten.clear();
    vakken.clear();
    tracks.clear();
    nrDagen = -1;
    nrUrenPerDag = -1;
    nrZalen = -1;
    nrDocenten = -1;
    nrVakken = -1;
    nrTijdsloten = -1;
    nrTracks = -1;
    for(int i = 0; i < MaxNrTijdsloten; i++) {
        tijdslotVol[i] = false;
    }
    resetZaalSymmetrie(0);
} // Rooster::verwijderOpzetRooster

//*************************************************************************

// Default constructor
Rooster::Rooster () {
    verwijderOpzetRooster();
}  // Rooster::Rooster

//*************************************************************************

// Simpele hulpfunctie die kijkt of een integer in een intvector zit
bool Rooster::integerInVector(vector<int> vector, int integer) {
    int lengte = vector.size();
    for(int k = 0; k < lengte; k++) {
        if(vector.at(k) == integer) {
            return true;
        }
    } // Vergelijkt de integers in de vector met de integer
    return false;
} // Rooster::integerInVector

//*************************************************************************

// Maakt een nieuwe track aan, als de track nog niet in de vector tracks
// voorkomt, en voegt deze in de vector tracks toe op de juiste plek, zodat
// de tracks in de vector van klein naar groot van hun trackId zitten.
void Rooster::addTrack(int const trackId, int const vakId) {
    for(int i = 0; i < nrTracks; i++) {
        if(tracks.at(i).getId() == trackId) {
            tracks.at(i).addVak(vakId);
            return; 
        } 
    } // Kijkt of de track al in de vector tracks zit

    Track track;
    track.setId(trackId);
    track.addVak(vakId);
    for(int j = 0; j < nrTracks; j++) {
        if(tracks.at(j).getId() > trackId) {
            if(j == 0) {
                tracks.insert(tracks.begin(), track);
                nrTracks = tracks.size(); // nrTracks verandert
                return;
            } // Kijkt of het de eerste track is
            tracks.insert(tracks.begin() + (j - 1), track); 
            nrTracks = tracks.size();
            return;
        } 
    } // Zet de track voor de track met een groter trackId, als deze er is
    tracks.push_back(track);  // De trackId is het grootst dus komt achteraan
    nrTracks = tracks.size();
} // Rooster::addTrack

//*************************************************************************

// Kijkt naar drie situaties of de tracks in track theoretisch mogelijk zijn.
// In het object van de tracks wordt vervolgens opgeslagen of de track theoretisch
// mogelijk is of niet. De drie situaties zijn:
// - Een track bestaat maar uit één vak
// - Een track bestaat uit vakken met allemaal dezelfde docent
// - Een track bestaat uit vakken die allemaal op een andere dag beschikbaar zijn
void Rooster::tracksTheoretischMogelijk() {
    bool zelfdeDocenten = true; // Of de docenten van de vakken hetzelfde zijn
    bool verschillendeDagen = true; // Of de vakken op verschillende dagen zijn
    bool dagenBeschikbaar[nrDagen]; // Houdt bij welke dag vakken van de track beschikbaar zijn
    for(int i = 0; i < nrTracks; i++) {
        vector<int> vakIds = tracks.at(i).getVakIds();
        int aantalVakIds = vakIds.size();
        if(aantalVakIds == 1) {
            tracks.at(i).setTheoretischMogelijk(false);
        } // De track bestaat maar uit één vak
        else {
            for(int m = 0; m < nrDagen; m++){
                dagenBeschikbaar[m] = false;
            } // Initialiseert de array voor een nieuwe track
            int eersteDocentId = vakken.at(vakIds.at(0)).getDocentId();
            for(int j = 1; j < aantalVakIds; j++) {
                if(eersteDocentId != vakken.at(vakIds.at(j)).getDocentId()) {
                    zelfdeDocenten = false;
                    j = aantalVakIds;
                } // Docenten zijn niet hetzelfde, dus kan nu uit de array
            } // Kijkt of de docenten van de vakken in de tracks hetzelfde zijn
            for(int k = 0; k < nrDagen; k++) {
                for(int l = 0; l < aantalVakIds; l++) {
                    if(docenten.at(vakken.at(vakIds.at(l)).getDocentId()).isDagBeschikbaar(k, nrUrenPerDag)) {
                        if(dagenBeschikbaar[k] == true) {
                            verschillendeDagen = false;
                            l = aantalVakIds;
                            k = nrDagen;
                        } // Track heeft twee vakken op zelfde dag, dus kan uit array
                        dagenBeschikbaar[k] = true;
                    } 
                }
            } // Kijkt of de vakken op precies verschillende dagen beschikbaar zijn
            if(zelfdeDocenten || verschillendeDagen) {
                tracks.at(i).setTheoretischMogelijk(false);
            } // Kijkt of de track theoretisch onmogelijk is
            else {
                tracks.at(i).setTheoretischMogelijk(true);
            } // Track is theoretisch mogelijk
            verschillendeDagen = true;
            zelfdeDocenten = true; // Reset de booleans
        }
    } // Doorloopt alle tracks
} // Rooster::tracksTheoretischMogelijk

//*************************************************************************

// Leest de waardes voor een rooster opzet in uit een geopende file
void Rooster::leesWaardesIn(ifstream& invoer) {
    int tijdslot, docentVak, aantalTracks, trackID, aantalTijdsloten;
    string naamVak;

    invoer >> nrDagen; 
    invoer >> nrUrenPerDag;
    invoer >> nrZalen;
    invoer >> nrDocenten;
    nrTijdsloten = nrDagen * nrUrenPerDag;
    for(int k = 0; k < nrDocenten; k++) {
        invoer >> aantalTijdsloten;
        Docent docent;
        docent.setId(k); 
        for(int l = 0; l < aantalTijdsloten; l++) {
          invoer >> tijdslot;
          docent.setTijdslot(tijdslot);
        } // Voegt de beschikbaarheid van de docent toe
        cout << docent.getId() << endl;
        docenten.push_back(docent);
    } // Voegt de docenten toe
    invoer >> nrVakken;

    for(int m = 0; m < nrVakken; m++) {
        invoer >> naamVak;
        invoer >> docentVak;
        invoer >> aantalTracks;
        Vak vak;
        vak.setWaardes(m, naamVak, docentVak);
        docenten.at(docentVak).addVakId(m);

        for(int n = 0; n < aantalTracks; n++) {
            invoer >> trackID;
            addTrack(trackID, m);
            vak.addTrackId(trackID);
        } // Voegt de tracks toe
        vakken.push_back(vak);
    } // Voegt de vakken toe met hun bijbehorende tracks
} // Rooster::leesWaardesIn

//*************************************************************************

// Leest uit een file de waardes in voor een nieuwe opzet
// waaruit roosters kunnen worden gemaakt en maakt deze opzet.
bool Rooster::leesIn (const char* invoerNaam) {
    ifstream invoer;

    invoer.open(invoerNaam, ios::in);
    if(invoer.fail()) {
        cout << "File kan niet geopend worden." << endl;
        return false;
    } // Checkt of file wel geopend kan worden

    verwijderOpzetRooster(); // Verwijdert het huidige rooster opzet
    leesWaardesIn(invoer); // Leest de waardes in voor de nieuwe rooster opzet
    invoer.close();

    tracksTheoretischMogelijk(); // Zet de theoretische mogelijkheid van de tracks
    return true; 
}  // Rooster::leesIn

//*************************************************************************

// Intialiseert de rooster array en reset de waardes van de rooster
// objecten, zodat er een nieuw rooster kan worden gemaakt
void Rooster::initialiseerRooster(int rooster[MaxNrTijdsloten][MaxNrZalen]) {
    for(int i = 0; i < MaxNrTijdsloten; i++) {
        for(int j = 0; j < MaxNrZalen; j++) {
            rooster[i][j] = -1;
        }
    } // Alles in rooster op -1 initialiseren
    for(int i = 0; i < MaxNrTijdsloten; i++) {
        tijdslotVol[i] = false;
    } // De zalen resetten
    for(int k = 0; k < nrVakken; k++) {
        vakken.at(k).setIngeroosterd(false);
    } // Alle vakken op niet ingeroosterd zetten
    for(int l = 0; l < nrDocenten; l++) {
        docenten.at(l).resetBeschikbaarheid(nrTijdsloten);
    } // Alle docent beschikbaarheden resetten
    for(int m = 0; m < nrTracks; m++) {
        tracks.at(m).resetBeschikbaarheid(nrTijdsloten);
        tracks.at(m).resetTrack();
    } // Alle track beschikbaarheden en de tussenuren resetten 
} // Rooster::initialiseerRooster

//*************************************************************************

// Drukt de opzet van een rooster af
void Rooster::drukAf () {
    cout << endl << "Opzet rooster ingelezen: " << endl
         << "nrDagen: " << nrDagen << endl
         << "nrUrenPerDag: " << nrUrenPerDag << endl
         << "nrZalen: " << nrZalen << endl
         << "nrDocenten: " << nrDocenten << endl
         << "nrVakken: " << nrVakken << endl
         << "nrTracks: " << nrTracks << endl << endl;
}  // Rooster::drukAf

//*************************************************************************

// Gaat door de vector vakken heen en kijkt bij elk vak of het is ingeroosterd
// door naar de membervariabele ingeroosterd te kijken en retourneert true
// als alles ingeroosterd is, anders false
bool Rooster::allesIngeroosterd() {
    for(int i = 0; i < nrVakken; i++) {
        if(vakken.at(i).getIngeroosterd() == false) {
            return false;
        } // kijken of er een vak nog niet ingeroosterd is
    }
    return true;
} // Rooster::allesIngeroosterd

//*************************************************************************

// Vindt de laatste les van een track sinds tijdslot met trackid op een dag
// en retourneert die. De dag wordt bepaald aan de hand van het tijdslot
int Rooster::laatsteLesTrack(int trackid, int tijdslot) {
    int dag = tijdslot/nrUrenPerDag;
    int begin = dag*nrUrenPerDag;
    int uur = -1;
    for(int i = begin; i < tijdslot; i++) {
        if(!tracks.at(trackid).isTijdslotBeschikbaar(i)) {
            uur = i;
        }
    }
    return uur;
} // Rooster::laatsteLesTrack

//*************************************************************************

// Vindt de eerste les sinds tijdslot van een track met trackid op een dag
// en retourneert die. De dag wordt bepaald aan de hand van het tijdslot
int Rooster::eerstvolgendeLes(int trackid, int tijdslot) {
    int dag = tijdslot/nrUrenPerDag;
    int teller = tijdslot+1;
    int laatsteuur = dag*nrUrenPerDag + (nrUrenPerDag-1); // laatste uur van de dag
    if((tijdslot+1) % nrUrenPerDag != 0) {
        while(teller <= laatsteuur) {
            if(!tracks.at(trackid).isTijdslotBeschikbaar(teller)) {
                return teller;
            }
            teller++;
        }
    }  // Tijdslot is niet het laatste uur van de dag
    return -1; // Geen uur gevonden
} // Rooster::eerstvolgendeLes

//*************************************************************************

// Kijkt of als een vak ingepland zou worden op een tijdslot een track niet teveel tussenuren
// op die dag zou krijgen. De dag wordt bepaald aan de hand van het tijdslot en true wordt
// geretourneert als het goed gaat, anders false
bool Rooster::tracksGoedTussenuren(int vakid, int tijdslot) {
    int laatsteles = -1;
    int eerstvolgendeles = -1;
    int dag = tijdslot/nrUrenPerDag;
    vector<int> tracksBijVak = vakken.at(vakid).getTrackIds(); // Alle tracks die vak met vakid volgen
    int aantalTracks = tracksBijVak.size();

    for(int i = 0; i < aantalTracks; i++) {
        laatsteles = laatsteLesTrack(tracksBijVak.at(i), tijdslot);
        eerstvolgendeles = eerstvolgendeLes(tracksBijVak.at(i), tijdslot); 
        if((laatsteles != -1 && (tijdslot-2) > laatsteles) || (eerstvolgendeles != -1 && (tijdslot+2) < eerstvolgendeles)) {
            return false;
        } // Er zit meer dan 1 tussenuur achter elkaar
        else if(laatsteles != -1 && eerstvolgendeles != -1 && (tijdslot-2) == laatsteles && (tijdslot+2) == eerstvolgendeles
               && tracks.at(tracksBijVak.at(i)).getTussenuren(dag) >= 0) {
            return false;
        } // Er ontstaan twee tussenuren door dit vak in te plannen
        else if(laatsteles != -1 && (tijdslot-2) == laatsteles && tracks.at(tracksBijVak.at(i)).getTussenuren(dag) >= 1) {
            return false;
        } // De track heeft al een tussenuur op de dag en nu nog een voor dit vak
        else if(eerstvolgendeles != -1 && (tijdslot+2) == eerstvolgendeles && tracks.at(tracksBijVak.at(i)).getTussenuren(dag) >=1) {
            return false;
        } // De track heeft al een tussenuur op de dag en nu nog een na dit vak
    }
    return true;

} // Rooster::tracksGoedTussenuren

//*************************************************************************

// Kijkt of een track les heeft van een vak, retourneert true als
// dat zo is, anders false
bool Rooster::heeftLes(int trackid, int vakid) {
    int tracksVanVak = vakken.at(vakid).getTrackIds().size();
    for(int i = 0; i < tracksVanVak; i++) {
        if(vakken.at(vakid).getTrackIds().at(i) == trackid) {
            return true;
        }
    }
    return false;
} // Rooster::heeftLes

//*************************************************************************

// Controleert of op een dag (bepaald aan de hand van tijdslot) elke track
// niet één les heeft tenzij het voor die track mag. Retourneert true als
// het goed gaat, anders false
bool Rooster::tracksGoedUren(int tijdslot) {
    int dag = tijdslot/nrUrenPerDag;
    int vakkenVanTrack; // de vakken die een track volgt worden hierin opgeslagen
    for(int i = 0; i < nrTracks; i++) {
        vakkenVanTrack = tracks.at(i).getVakIds().size();
        if(!tracks.at(i).getTheoretischMogelijk()) {
            return true; // Theoretisch onmogelijke track
        }
        if(tracks.at(i).aantalVakken(dag) == 1 && vakkenVanTrack != 1) {
            return false;
        } // Dit zou de enige les van de dag worden voor de track
    }
    return true; // Aan de eisen voor tracks zijn voldaan
} // Rooster::tracksGoedUren

//*************************************************************************

// Kijkt of elke track die vak "vak" volgt niet al les heeft op
// tijdslot. Retourneert true als het goed gaat, anders false
bool Rooster::isVakBeschikbaar(Vak vak, int tijdslot) {
    vector<int> vakTracks = vak.getTrackIds(); // alle tracks die het vak volgen
    int aantalTracks = vak.getTrackIds().size();
    for(int i = 0; i < aantalTracks; i++) {
        if(!tracks.at(vakTracks.at(i)).isTijdslotBeschikbaar(tijdslot)) {
            return false; // Track is niet meer beschikbaar op het tijdslot
        }
    }
    return true;
} //Rooster::isVakBeschikbaar

//*************************************************************************

// Kijkt of een docent van een vak "vak" op een gegeven tijdslot les kan geven
// en roept een functie aan die kijkt of tracks niet dubbel les hebben. Retourneert
// true als het goed gaat, anders false
bool Rooster::isCollegeMogelijk(Vak vak, int tijdslot) {
    if(!vak.getIngeroosterd() && docenten.at(vak.getDocentId()).isDocentBeschikbaar(tijdslot, nrUrenPerDag)) {
        return isVakBeschikbaar(vak, tijdslot);
    }
    return false;
} //Rooster::isCollegeMogelijk

//*************************************************************************

// Voegt een tussenuur toe bij alle tracks waar dat nodig is die het vak met vakid volgen
// als nieuw true is en haalt een tussenuur weg bij alle tracks waar dat nodig is die het
// vak met vakid volgen als nieuw false is.
void Rooster::setTussenuur(int tijdslot, int vakid, bool nieuw) {
    int dag = tijdslot/nrUrenPerDag;
    int laatsteles = -1; // laatste les die is ingepland
    int eerstvolgendeles = -1; // eerste les die is ingepland
    vector<int> tracksBijVak = vakken.at(vakid).getTrackIds(); // Alle tracks die het vak volgen
    int aantalTracks = tracksBijVak.size();

    for(int i = 0; i < aantalTracks; i++) {
        laatsteles = laatsteLesTrack(tracksBijVak.at(i), tijdslot);
        eerstvolgendeles = eerstvolgendeLes(tracksBijVak.at(i), tijdslot);
        if((laatsteles != -1 && (tijdslot-1) != laatsteles) ||
          (eerstvolgendeles != -1 && (tijdslot+1) != laatsteles)) {
            if(nieuw) {
                tracks.at(tracksBijVak.at(i)).nieuwTussenuur(dag); // tussenuur toevoegen
            }
            else {
                tracks.at(tracksBijVak.at(i)).verwijderTussenuur(dag); // tussenuur weghalen
            }
        } // Tussenuur als het niet de eerste les is en de laatste les is
          // niet het tijdslot ervoor
    } // Alle tracks afgaan die het vak volgen
} // Rooster::setTussenuur

//*************************************************************************

// Roostert een vak met vakid in op tijdslot "tijdslot" in "rooster". Als tussenuren true is
// worden ook de tussenuren geset, anders niet. De functie retourneert in welke zaal het vak
// is ingeroosterd
int Rooster::inroosteren(int vakid, int tijdslot, int rooster[MaxNrTijdsloten][MaxNrZalen], bool tussenuren) {
    int zaal = -1; // de zaal waarin het vak wordt ingeroosterd
    int i = 0;
    bool gevonden = false; // of er een zaal is gevonden
    int dag = tijdslot/nrUrenPerDag;
    while(i < nrZalen && !gevonden) {
        if(rooster[tijdslot][i] == -1) {
            zaal = i;
            gevonden = true;
        }
        i++;
    } // Eerst moet een lege zaal gezocht worden

    if(zaal != -1) {
        rooster[tijdslot][zaal] = vakid;
        docenten.at(vakken.at(vakid).getDocentId()).setBeschikbaarheid(tijdslot, false);
        vakken.at(vakid).setIngeroosterd(true);
        int aantalTracks = vakken.at(vakid).getTrackIds().size();
        for(int j = 0; j < aantalTracks; j++) {
            tracks.at(vakken.at(vakid).getTrackIds().at(j)).setBeschikbaarheid(tijdslot, false);
            tracks.at(vakken.at(vakid).getTrackIds().at(j)).nieuwVakOpDag(dag);
        } // de tracks van het vak de juiste waardes geven
        if(zaal == (nrZalen-1)) {
            tijdslotVol[tijdslot] = true;
        } // als nu alle zalen zijn ingeroosterd, is het tijdslot vol
    } //Alle zalen zijn ingeroosterd
    if(tussenuren) {
        setTussenuur(tijdslot, vakid, true);
    }
    return zaal;
} // Rooster::inroosteren

//*************************************************************************

// Roostert een vak op een tijdslot en in een zaal uit rooster weer terug
// en zet alle waardes weer terug naar hoe ze waren
void Rooster::terugroosteren(int tijdslot, int rooster[MaxNrTijdsloten][MaxNrZalen], int zaal) {
    int vakid = rooster[tijdslot][zaal]; // vak dat teruggeroosterd moet worden
    int dag = tijdslot/nrUrenPerDag;
    vakken.at(vakid).setIngeroosterd(false);
    docenten.at(vakken.at(vakid).getDocentId()).setBeschikbaarheid(tijdslot, true);
    int tracksVanVak = vakken.at(vakid).getTrackIds().size();
    for(int i = 0; i < tracksVanVak; i++) {
        tracks.at(vakken.at(vakid).getTrackIds().at(i)).setBeschikbaarheid(tijdslot, true);
        tracks.at(vakken.at(vakid).getTrackIds().at(i)).verwijderVakOpDag(dag);
    } // de waardes van de tracks die het vak volgen goed zetten
    if(tijdslotVol[tijdslot]) {
        tijdslotVol[tijdslot] = false;
    } // Er is weer een zaal beschikbaar
    
    rooster[tijdslot][zaal] = -1;
    setTussenuur(tijdslot, vakid, false);
} // Rooster::terugroosteren

//*************************************************************************

// Kijkt wat het laatste ingeroostesterde tijdslot van rooster is en
// retourneert die. Er wordt -1 geretourneerd als er niks gevonden is
int Rooster::laatsteLesRooster(int rooster[MaxNrTijdsloten][MaxNrZalen]) {
    for(int i = nrTijdsloten-1; i >=0; i--) {
        for(int j = 0; j < nrZalen; j++) {
            if(rooster[i][j] != -1) {
                return i;
            }
        }
    }
    return -1;
} // Rooster::laaststeLesRooster

//*************************************************************************

// Recursieve functie die een mogelijk rooster bepaald en retourneert true als die
// gevonden is, anders false. In aantalDeelRoosters wordt bijgehouden naar hoeveel
// deelroosters is gekeken.
bool Rooster::bepaalRoosterBerekening(int rooster[MaxNrTijdsloten][MaxNrZalen], long long &aantalDeelroosters) {
    int zaal = -1;
    aantalDeelroosters++;;
    bool gelukt = false;
    int ingeroosterdTijdslot = -1; // tijdslot waarop het vak is ingeroosterd
    if(allesIngeroosterd()) {
        return true;
    } // alle vakken zijn ingeroosterd
    for(int i = 0; i < nrTijdsloten; i++) {
        for(int j = 0; j < nrZalen; j++) {
            for(int vak = 0; vak < nrVakken; vak++) {
                if(!tijdslotVol[i] && zaalSymmetrie[i][vak] == -1) {
                    if(isCollegeMogelijk(vakken.at(vak), i) && tracksGoedTussenuren(vakken.at(vak).getId(), i)) {
                        zaal = inroosteren(vakken.at(vak).getId(), i, rooster, true);
                        ingeroosterdTijdslot = i;
                        gelukt = bepaalRoosterBerekening(rooster, aantalDeelroosters);
                        if(!gelukt) {
                            terugroosteren(i, rooster, zaal);
                            zaalSymmetrie[i][vak] = 1;
                            resetZaalSymmetrie(i + 1);
                        } // als er geen rooster gevonden kon worden
                    }
                }
            }
        }
        if((i + 1) % nrUrenPerDag == 0 && !tracksGoedUren(i)) {
            if(gelukt) {
                terugroosteren(ingeroosterdTijdslot, rooster, zaal);
            }
            return false; // er is een track dat 1 tijdslot heeft op een dag
                          // en dat niet mag hebben
        }
    }
    if(!allesIngeroosterd()) {
        return false; // nog niet alles is ingeroosterd
    }
    return true;
} // Rooster::bepaalRoosterBerekening

//*************************************************************************

// Deze functie roostert eerst alle vakken uit de vector in voordat 
// gecontroleerd kan worden of er niet een track is dat maar 1 les
// op de dag heeft. Daarna wordt alles weer teruggeroosterd. Er wordt true
// geretourneerd als alles is goed gegaan, anders false
bool Rooster::tracksMinGoedUren(int tijdslot, int rooster[MaxNrTijdsloten][MaxNrZalen],
                               vector<vector<int>> nieuweVakken) {
    int aantalNieuweVakken = nieuweVakken.size();
    int zaal;
    bool gelukt = false;
    for(int i = 0; i < aantalNieuweVakken; i++) {
        zaal = inroosteren(nieuweVakken.at(i).at(0), nieuweVakken.at(i).at(1), rooster, true);
    } // eerst alle vakken inroosteren
    gelukt = tracksGoedUren(tijdslot); // controle of het goed is gegaan
    for(int i = 0; i < aantalNieuweVakken; i++) {
        terugroosteren(nieuweVakken.at(i).at(1), rooster, nieuweVakken.at(i).at(2));
    } // alle vakken weer terugroosteren
    return gelukt;
} // Rooster::tracksMinGoedUren

//*************************************************************************

// Deze functie voegt een vector met vakid, tijdslot en zaal toe aan de vector resultaat
void Rooster::voegVakToe(vector<vector<int>> &resultaat, int vakid, int tijdslot, int zaal) {
    vector<int> inTePlannen;
    inTePlannen.push_back(vakid);
    inTePlannen.push_back(tijdslot);
    inTePlannen.push_back(zaal);
    resultaat.push_back(inTePlannen);
} // Rooster::voegVakToe

//*************************************************************************

// Recursieve functie die een zo kort mogelijk rooster bepaald en het tijdslot retourneert
// van de laatste les in het zo kort mogelijk rooster. In aantalDeelroosters komt te staan
// naar hoeveel deelroosters is gekeken en in nieuweVakken komt te staan welk vak waar moet
// worden ingeroosterd voor het zo kort mogelijke rooster
int Rooster::bepaalMinRoosterBerekening(int rooster[MaxNrTijdsloten][MaxNrZalen], long long
                                       &aantalDeelroosters, vector<vector<int>> &nieuweVakken) {
    int zaal = -1;
    aantalDeelroosters++;
    int laatsteles = -1;
    int besteLaatsteles = MaxNrTijdsloten;
    vector<vector<int>> resultaat;
    vector<vector<int>> hulp; // Vectoren om te kijken welke vakken ingeroosterd moeten worden
    if(allesIngeroosterd()) {
        return laatsteLesRooster(rooster); // Alle vakken zijn ingeroosterd
    }
    for(int i = 0; i < nrTijdsloten; i++) {
        for(int j = 0; j < nrZalen; j++) {
            for(int vak = 0; vak < nrVakken; vak++) {
                if(!tijdslotVol[i]) {
                    if(isCollegeMogelijk(vakken.at(vak), i) && tracksGoedTussenuren(vakken.at(vak).getId(), i)) {
                        zaal = inroosteren(vakken.at(vak).getId(), i, rooster, true);
                        laatsteles = bepaalMinRoosterBerekening(rooster, aantalDeelroosters, hulp);
                        terugroosteren(i, rooster, zaal);
                        if(laatsteles == -1) {
                            zaalSymmetrie[i][vak] = 1;
                            resetZaalSymmetrie(i + 1);
                        } // Zalen symmetrie
                        if(laatsteles != -1 && laatsteles < besteLaatsteles) {
                            besteLaatsteles = laatsteles;
                            resultaat = hulp;
                            voegVakToe(resultaat, vak, i, zaal);
                        } // Alleen als het het korste rooster is een vector maken en aan
                          // nieuweVakken toevoegen
                    }
                }
            }
        }
        if((i + 1) % nrUrenPerDag == 0 && !tracksMinGoedUren(i, rooster, resultaat)) {
            resultaat.clear();
            return -1; // als er een track is met 1 vak op een dag die dat niet mag
                       // dan is het fout gegaan
        }
    }
    int teller = 0;
    for(int i = 0; i < nrVakken; i++) {
        if(vakken.at(i).getIngeroosterd()) {
            teller++;
        }
    } // Tellen hoeveel vakken al waren ingeroosterd
    int tmpsize = resultaat.size();
    if(tmpsize+teller != nrVakken) {
        return -1; // Er is geen volledig rooster gevonden
    }
    nieuweVakken = resultaat;
    return besteLaatsteles;
} // Rooster::bepaalMinRoosterBerekening

//*************************************************************************

// Roept een functie aan die een rooster bepaalt en in rooster zet en bijhoudt
// hoeveel deelroosters bekeken zijn en dat in aantalDeelroosters zet. Er wordt true
// geretourneerd als er een rooster is gevonden, anders false
bool Rooster::bepaalRooster (int rooster[MaxNrTijdsloten][MaxNrZalen], long long &aantalDeelroosters) {
    initialiseerRooster(rooster);
    aantalDeelroosters = 0;
    resetZaalSymmetrie(0);
    return bepaalRoosterBerekening(rooster, aantalDeelroosters);
}  // Rooster::bepaalRooster

//*************************************************************************

// Roept een functie aan die een zo kort mogelijk rooster bepaalt en in rooster zet en bijhoudt
// hoeveel deelroosters bekeken zijn en dat in aantalDeelroosters zet. Ook worden de vakken
// ingeroosterd op de plek die de recursieve functie berekend had. Er wordt true
// geretourneerd als er een rooster is gevonden, anders false
bool Rooster::bepaalMinRooster (int rooster[MaxNrTijdsloten][MaxNrZalen],
                        long long &aantalDeelroosters) {
    int laatsteles = -1;
    int aantalVakken;
    vector<vector<int>> nieuweVakken;
    resetZaalSymmetrie(0);
    initialiseerRooster(rooster);
    aantalDeelroosters = 0;
    laatsteles = bepaalMinRoosterBerekening(rooster, aantalDeelroosters, nieuweVakken);
    if(laatsteles != -1) {
        aantalVakken = nieuweVakken.size();
        for(int i = 0; i < aantalVakken; i++) {
            inroosteren(nieuweVakken.at(i).at(0), nieuweVakken.at(i).at(1), rooster, true);
        } // De vakken inroosteren
        return true;
    }
    return false;

}  // Rooster::bepaalMinRooster
  
//*************************************************************************

// Drukt rooster netjes af. Voor elke dag worden alle uren afgedrukt
// en per uur wordt de indeling van de zalen laten zien.
void Rooster::drukAfRooster (int rooster[MaxNrTijdsloten][MaxNrZalen]) {
    int dagTeller = 1; //voor de output om af te drukken welke dag het is
    int uurTeller; //voor de output om af te drukken welke tijd het is

    cout << endl;
    for(int i = 0; i < nrTijdsloten; i++) {
        if(i % nrUrenPerDag == 0) {
            cout << "Dag " << dagTeller << " :" << endl;
            uurTeller = 1;
        } //dit wordt afgedrukt bij het eerste uur van de dag
        cout << uurTeller << "e uur: ";
        for(int j = 0; j < nrZalen; j++) {
            cout << " [ zaal: " << j+1 << " - ";
            if(rooster[i][j] == -1) {
                cout << "geen les ]  ";
            }
            else {
                cout << "vak: " << vakken.at(rooster[i][j]).getVakNaam()
                     << " - docent: " << vakken.at(rooster[i][j]).getDocentId() << " ]  ";
            }
        } //drukt de zaalindelingen per tijdslot af
        cout << endl;
        if((i + 1) % nrUrenPerDag == 0) {
            cout << endl;
            dagTeller++;
        } //als alle uren van de dag zijn afgedrukt, een extra regelovergang toevoegen
        uurTeller++;
    } //alle tijdsloten van rooster afgaan
}  // Rooster::drukAfRooster

//*************************************************************************

// Geeft de slechtste rang van beschikbaarheid die het vak in zijn tracks heeft
// De rangen zijn als volgt:
// -2 (Het slechtst): er is al een vak van deze track op dit tijdslot
// -1 (slecht): meer dan één tussenuur op een dag
// 0 (goed): er zijn nog geen andere vakken van de track ingeroosterd
// 1 (goed): er komt een tussenuur
// 2 (het best): het vak ligt vlak na een ander vak in de track
// De prioriteit is: -2 > -1 > 0 > 1 > 2
int Rooster::vakBeschikbaarheid(Vak vak, int tijdslot) {
    vector<int> trackIds = vak.getTrackIds();
    int aantalTrackIds = trackIds.size();
    int beschikbaarheid = 2; // Begin: laagste prioriteit
    for(int k = 0; k < aantalTrackIds; k++) {
        int tmp = tracks.at(trackIds.at(k)).trackBeschikbaarheid(tijdslot, nrUrenPerDag);
        if(tmp  == -2) {
            return -2; // beschikbaarheid kan niet slechter dan -2 
        }
        if(tmp < beschikbaarheid) {
            beschikbaarheid = tmp;
        } 
    } // Gaat door alle tracks van het vak en neemt steeds de slechtste rang
    return beschikbaarheid;
} // Rooster::vakBeschikbaarheid

//*************************************************************************

// Kijkt of het beste vak uit het gretige algoritme een track heeft die,
// als dit vak zou worden ingeroosterd, maar één vak op deze dag zou hebben.
// Ook kijken we of dit beste vak andere mogelijke vakken heeft die in dezelfde 
// track zitten in het tijdslot hierna of over twee tijdsloten, zodat er niet
// meerdere tussenuren voor een track ontstaan. Als het vak in een track zit die 
// theoretisch onmogelijk is, dan wordt deze regels overschreden.
// De functie retourneert true als het vak aan de regels voldoet, anders false.
bool Rooster::vakVerbondenheid(int vakId, int tijdslot) {
    vector<int> trackIds = vakken.at(vakId).getTrackIds(); // trackIds van het vak
    int aantalTrackIds = trackIds.size();
    int dag = tijdslot / nrUrenPerDag; 
    int eindeDag = (dag + 1) * nrUrenPerDag;
    bool andereVakkenBeschikbaar = false; // Of er andere vakken beschikbaar zijn in de volgende 2 tijdsloten

    for(int i = 0; i < aantalTrackIds; i++) {
        if(!tracks.at(trackIds.at(i)).getTheoretischMogelijk()) {
            return false;
        } // Kijkt of de track theoretisch onmogelijk is
        vector<int> vakIds = tracks.at(trackIds.at(i)).getVakIds();
        int aantalVakIds = vakIds.size();
        bool enigeVakOpDag = true; // Of de track maar een vak op deze dag heeft
        for(int j = 0; j < aantalVakIds; j++) {
            if(vakIds.at(j) != vakId) { 
                for(int k = (tijdslot + 1); k < eindeDag; k++) {
                    if(docenten.at(vakken.at(vakIds.at(j)).getDocentId()).isDocentBeschikbaar(k, nrUrenPerDag)) {
                        if(k < (tijdslot + 3)) {
                            if(!(k == (tijdslot + 2) && tracks.at(trackIds.at(i)).getTussenuren(dag) != 0)) {
                                andereVakkenBeschikbaar = true;
                            } // Kijkt of er een andere vak is waarmee dit vak kan verbinden
                        } // Kijkt of het vak beschikbaar is in het volgende tijdslot of daarna
                        enigeVakOpDag = false;
                    } // Kijkt of dit vak beschikbaar is op een volgend tijdstip op deze dag
                } // Loopt de tijdsloten van de dag af na int tijdslot
            } // We willen niet hetzelfde vak bekijken
        } // Kijkt of er vakken beschikbaar zijn op deze dag
        if(enigeVakOpDag || !andereVakkenBeschikbaar) {
            return true;
        } // Er is geen ander vak die beschikbaar is in deze track gevonden
    } // Loopt door alle tracks van het vak
    return false;
} // Rooster::enigeVakOpDag

//*************************************************************************

// Kijkt als het vak een slechte beschikbaarheid heeft of het vak nog op
// een later tijdslot beschikbaar is, zo ja, dan is er dus een kans dat het vak
// dan beter is en wordt het niet ingeroosterd. Anders wordt het vak ingroosterd.
bool Rooster::besteVakInroosteren(int tijdslot, int besteVakId, int beschikbaarheid) {
    int laatsteBeschikbaarheid = docenten.at(vakken.at(besteVakId).getDocentId()).getLaatsteBeschikbaarheid();
    if(beschikbaarheid < 0 && tijdslot != laatsteBeschikbaarheid) {
        return false;
    } // Als er nog een mogelijkheid is (in de toekomst) dan deze niet inroosteren
    
    if(beschikbaarheid == 0) {
        if(tijdslot != laatsteBeschikbaarheid && vakVerbondenheid(besteVakId, tijdslot)){
            return false;
        } // Kijkt of het het enige vak op de dag is voor een track
    } // Voor andere beschikbaarheden hoef je niet te kijken, want dat is sowieso niet het enige vak

    if(beschikbaarheid == 1) {
        setTussenuur(tijdslot, besteVakId, true);
    } // Er komt een tussenuur
    return true; 
} // Rooster::besteVakInroosteren

//*************************************************************************

// Hulpfunctie voor het gretige algoritme voor het kijken of een
// vak beter is dan wat op het moment wordt aangenomen als beste vak.
// Als het beter is dan wordt dit het niewe beste vak.
void Rooster::beterVak(int tijdslot, int vakId, int& besteVakId, 
                       int& beschikbaarheid) {
    int beschTemp = vakBeschikbaarheid(vakken.at(vakId), tijdslot); // Beschikbaarheid van vak

    if(beschTemp > beschikbaarheid) {
        beschikbaarheid = beschTemp;
        besteVakId = vakId;
    } // Kijkt of beschikbaarheid beter is
} // Rooster::beterVak

//*************************************************************************

// Bepaald een rooster door per tijdslot het vak van de
// docent die dan beschikbaar is met de beste beschikbaarheid in te roosteren, als
// dit vak wel voldoet, of zo goed mogelijk voldoet aan de eisen.
// Dit doen we tot we geen vakken meer hebben om te bekijken of als de zalen vol zijn.
// Beschikbaarheid: rang voor hoe goed het vak in zijn tracks beschikbaar is
// (zie de functie vakBeschikbaarheid voor meer informatie)
void Rooster::bepaalRoosterGretig (int rooster[MaxNrTijdsloten][MaxNrZalen]) {
    int beschikbaarheid = -3; // Beginwaarde is slechter dan het slechtst
    int besteVakId = -1; 
    bool vakkenBeschikbaar = true;
    vector<int> slechteVakken; // Vakken die op dit tijdslot slecht waren maar niet ingeroosterd 
                               // omdat er een kans is dat deze in de toekomst beter is

    initialiseerRooster(rooster);
    for(int tijdslot = 0; tijdslot < nrTijdsloten; tijdslot++) {
        while(vakkenBeschikbaar && !tijdslotVol[tijdslot]) {
            for(int i = 0; i < nrDocenten; i++) {
                if(docenten.at(i).isDocentBeschikbaar(tijdslot, nrUrenPerDag)) {
                    vector<int> vakIds = docenten.at(i).getVakIds();
                    int aantalVakIds = vakIds.size();
                    for(int j = 0; j < aantalVakIds; j++) {
                        int vakId = vakIds.at(j);
                        if(!(vakken.at(vakId).getIngeroosterd()) && 
                           !(integerInVector(slechteVakken, vakId))) {
                            beterVak(tijdslot, vakId, besteVakId, beschikbaarheid);
                        } // Vak is nog niet ingeroosterd en is niet een slechte vak
                    } // Gaat elk vak af van de docent die beschikbaar is
                } // Kijkt of docent beschikbaar is
            } // Loopt alle docenten af
            if(besteVakId != -1) {
                if(!besteVakInroosteren(tijdslot, besteVakId, beschikbaarheid)) {
                    slechteVakken.push_back(besteVakId);
                } // Er is nog een kans in de toekomst voor een betere inroostering
                else {
                    besteVakId = inroosteren(besteVakId, tijdslot, rooster, false);
                } // Roostert het vak in als er wel een beste vak is
            } // Kijkt of beste vak wel een vak is
            else {
                vakkenBeschikbaar = false;
            } // besteVakId is -1, dus er zijn geen vakken meer over op dit tijdslot
            besteVakId = -1; 
            beschikbaarheid = -3; // Reset de waardes
        } // Stopt met zoeken als er geen beschikbare vakken meer zijn of de zalen vol zijn
        slechteVakken.clear(); // Reset slechteVakken
        vakkenBeschikbaar = true; // Reset vakkenBeschikbaar
    } // loopt alle tijdsloten af
}  // Rooster::bepaalRoosterGretig