#include #include #include #include #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 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 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 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 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 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> 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> &resultaat, int vakid, int tijdslot, int zaal) { vector 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> &nieuweVakken) { int zaal = -1; aantalDeelroosters++; int laatsteles = -1; int besteLaatsteles = MaxNrTijdsloten; vector> resultaat; vector> 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> 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 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 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 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 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 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