Skip to content
Snippets Groups Projects
Select Git revision
  • d2b4c111b1f1a9c17d2e8fad27359032d6209c68
  • main default
  • frederik
  • Sondre
4 results

rute.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    rute.cpp 22.10 KiB
    /**
     * Kode-fil for rute-klassen
     *
     * @file rute.cpp
     * @author Sondre Sand & Frederik Simonsen
     */
    #include <algorithm> // all_of, sort
    #include <iostream>
    #include <iomanip>
    #include <list>
    #include <fstream>
    #include "const.h"
    #include "rute.h"
    #include "stoppested.h"
    #include "stoppesteder.h"
    #include "LesData3.h"
    using namespace std;
    
    extern Stoppesteder gStoppestederBase;
    
    //********STOPP FUNKSJONER*******************
    
    
    /**
     *  Oppretter et nytt Stopp ifm. rutefunksjonalitet
     *
     * @param stoppNr - Stoppens unike nummer
     * @param antMin - Minutter fra stoppet rett før på ruten
     */
    Stopp::Stopp(const int stoppNr, const int antMin) {
        // Kopierer medsendte verdier til å være egne data:
        nr = stoppNr; minutter = antMin;
    }
    
    /**
     * Henter antall minutter fra en stopp til forrige stopp på ruten
     *
     * @return Antall minutter fra en stopp til forrige stopp
     */
    int Stopp::hentMin() {
        return minutter;
    }
    
    /**
     * Henter en stopps unike nr
     *
     * @return Et stoppesteds unike nr
     */
    int Stopp::hentNr() {
        return nr;
    }
    
    /**
     * Skriver ut data om en Stopp til fil
     *
     * @param ut - Filen det skrives til
     */
    void Stopp::skrivTilFil(ofstream & ut) {
        ut << nr << ' ' << minutter << '\n';
    }
    
    //********RUTE FUNKSJONER*******************
    
    
    /**
     * Default constructor for en Rute, faktiske data leses inn i lesData
     */
    Rute::Rute() {
        // Gjør ingenting, men må være her så man kan opprette buss/bane
    }
    
    /**
     * Opretter en ny rute fra fil
     *
     * @param inn - filen det leses fra
     */
    Rute::Rute(ifstream & inn) {
        int antStopp,
            stoppNr,
            antMin;
    
        inn >> antStopp; inn.ignore(); // Leser antall stopp på ruten
        for (int i = 0; i < antStopp; i++) { // For hvert stopp:
            inn >> stoppNr >> antMin; inn.ignore();
            // Oppretter ny stopp på ruten:
            stoppene.push_back(new Stopp(stoppNr, antMin));
        }
    }
    
    /**
     * Sjekker om en rute har mer enn en stopp (= gyldig rute)
     *
     * @return true Hvis ruten er gyldig (>1 stopp)
     * @return false Hvis ruten ikke er gyldig (<1 stopp)
     */
    bool Rute::erListeGyldig() {
        if (stoppene.size() > 1) {
            return true;
        }
        return false;
    }
    
    /**
     * Går gjennom et medsendt navn fra bruker, sjekker
     * alle tegn i strengen er tall eller ei
     *
     * @param nvn - Mulig stoppestedsnavn eller stoppestedsnr
     * @return true Hvis alle tegnene i strengen er tall
     * @return false Hvis strengen inneholder tegn som ikke er tall
     */
    bool Rute::erTall(std::string nvn) {
        return all_of(nvn.begin(), nvn.end(), ::isdigit);
    }
    
    /**
     * Går gjennom om alle Stopp på en rute for å sjekke om deres
     * unike nr matcher en nr som bruker mener finnes på ruten
     *
     * @param nr - Et stopps nummer som skal forsøkes finnes på ruten
     * @return true - Hvis stoppnummeret finnes på ruten
     * @return false - Hvis stoppnummeret ikke finnes på ruten
     * @see Stopp::hentNr()
     */
    bool Rute::finnesStopp(const int nr) {
            // For alle stopp på ruten:
        for (auto it = stoppene.begin(); it != stoppene.end(); it++) {
            if ((*it)->hentNr() == nr) // Ser om eget nr matcher medsendt nr
                return true; // Match, returnerer at stoppet finnes på ruten
        }
        return false; // Ingen match
    }
    
    /**
     * Funksjon for å sjekke om et klokkeslett er gyldig (00:00-23:59)
     *
     * @param time - Time i et klokkeslett som skal sjekkes for gyldighet
     * @param minutt - Minutter i et klokkeslett som skal sjekkes for gyldighet
     * @return true - Hvis klokkeslettet er gyldig
     * @return false - Hvis klokkeslettet er ugyldig (utenfor intervall)
     */
    bool Rute::gyldigTid(const int time, const int minutt) {
        if ((time >= 0 && time <= 23) && (minutt >= 0 && minutt <= 59)) {
            return true; // Klokkeslett mellom 00:00 og 23:59
        } else
            return false; // Klokkeslett utenfor lovlig intervall
    }
    
    /**
     * Finner antall minutter mellom et faktisk stopp på ruten
     * og startstoppestedet på ruten
     * 
     * @param nr - Et stoppesteds unike nummer
     * @param retning - Fram eller Tilbake (ulikt startsted) avhengig
     *                  av hvilken vei ruten går
     * @return Antall minutter mellom et startsted på ruten og et gitt
     *         stoppested på ruten
     * @see Stopp::hentMin()
     * @see Stopp:hentNr()
     */
    int Rute::finnDifferanse(const int nr, const Retning retning) {
        int totMin = 0;
        if (retning == Fram) {
            // For hvert stopp på ruten:
            for (auto it = stoppene.begin(); it != stoppene.end(); it++) {
                totMin += (*it)->hentMin(); // Teller opp antall minutter
                if ((*it)->hentNr() == nr) // Hvis egen stopp
                    return totMin; // Returnerer antall minutter fra start
                                   // til eget stopp
            }
        } else if (retning == Tilbake) {
            // For hvert stopp på ruten, traverserer ruten "baklengs":
            for (auto it = stoppene.rbegin(); it != stoppene.rend(); it++) {
                if ((*it)->hentNr() == nr) // Hvis egen stopp
                    return totMin; // Returnerer antall minutter mellom
                totMin += (*it)->hentMin(); // Hvis ikke funn, teller opp
                                            // antall minutter
            }
        }
        return -1; // Skal ikke skje
    }
    
    /**
     * Henter navn på startstasjonen på en rute
     * 
     * @param retning Om ruten er Fram (original) eller Tilbake ("baklengs")
     * @return Navnet på startstopp på rute avhengig av retning
     * @see Stoppesteder::hentNavnVhaIndeks(...)
     * @see Stopp::hentNr()
     */
    string Rute::hentNavn(const Retning retning) {
        string startSted;
        if (retning == Fram) { // Henter navn på første stopp i listen:
        startSted = gStoppestederBase.hentNavnVhaIndeks(stoppene.front()->hentNr());
        } else if (retning == Tilbake) // Henter navn på bakerste stopp i listen:
            startSted = gStoppestederBase.hentNavnVhaIndeks(stoppene.back()->hentNr());
    
       return startSted; // Returnerer navnet på startstopp på ruten
    }
    
    /**
     * Leser inn og oppretter en rutetabell for et gitt stoppested på en rute
     * med utgangspunkt i omregning fra et startsted på ruten
     * 
     * @param diff - differanse mellom startsted og aktuelt stoppested i minutter
     * @param start - faktisk startstedsnavn på ruten
     * @param stSted - faktisk navn på aktuellt stoppested
     * @see Rute::gyldigTid(...)
     */
    void Rute::ruteTabell(const int diff, const string start, const string stSted) {
        int startT, // starttime
            startTotal, // regner ut total starttid i minutter
            sluttT, // slutt-time
            sluttTotal, // regner ut total slutttid i minutter
            startM, // startminutter
            sluttM, // slutt-minutter
            tidMellom, // Tid mellom hver avgang i minutter
            tPrint, // hjelpevariabel for å sørge for korrekt utskrift av time
            antGanger, // omregning for å legge inn riktig antall avgangstider
            startTid, // omregning for å legge inn starttid
            n = -1, // hjelpevariabel for linjeskift ved ny time
            timer, // hjelpevariabel for å skrive ut time
            minutter, // hjelpevariabel for å skrive ut minutter
            avgangsTid, // omregning for å legge inn riktig avgangstid
            mPrint; // hjelpevariabel for å sørge for korrekt utskrift av minutter
        vector <int> avgangstider; // Hjelpevektor for å ta vare på avgangstider
    
        cout << "\nRuteavganger fra startstedet (" << start << ") - "
             << "avslutt med 0 0):\n\n";
        // 23:53 her da det må være minimum 6 min mellom avganger:
        cout << "Fra kl. (00:01 - 23:53): ";
        cin >> startT >> startM; cin.ignore(); // Leser starttid
        startTid = (startT*60)+startM; // Regner ut første avgangstid
    
        // Sørger for at starttid er gyldig, samt ikke senere enn 23:53 (1433)
        while (!gyldigTid(startT, startM) || startTid > 1433) { 
            cout << "\nUlovlig klokkeslett. Prøv igjen (tt mm): ";
            cin >> startT >> startM; cin.ignore();
            startTid = (startT*60)+startM;
        }
        
        while (startT != 0 || startM != 0) { // Så lenge bruker ikke taster 0 0
            avgangsTid = startTid+diff;
            // Må ha en sjekk her for lang tid mellom stoppesteder, som
            // kan føre til at antall minutter blir mer enn 1440 (24:00)
            // så man ikke får output med et klokkeslett 25:xx f.eks.
            if (avgangsTid < 1440) { // < 24:00
                avgangstider.push_back(avgangsTid); // Legger første avgang
                                                    // i vector    
            } else { // Omregning av hva som skal i vector, for spesielle
                     // tilfeller, unngå 25:xx f.eks.:
                avgangstider.push_back(avgangsTid%1440);
            }
            
            
            // Leser tid mellom avganger:
            // Ny sjekk her for å sørge for riktig output til bruker
            // basert på valgt "fra"-tid. Godtar ikke 6-120 min for
            // avganger senere enn 21:59, da vi kun skal lese inn
            // for en dag
            if (avgangsTid < 1319) { // < 21:59
                tidMellom = lesInt("Tid mellom avganger", MINTID, MAXTID);
            } else { // Senere klokkeslett enn 21:59, gjør om output til bruker:
                tidMellom = lesInt("Tid mellom avganger", MINTID, 1439%startTid);
            }
            startTotal = (startT*60)+startM+tidMellom; // Omregner for sjekk
            tPrint = startTotal/60; // Regner ut gyldig utskrift av t og min:
            mPrint = startTotal%60;
    
            cout << "Til kl. (" << ((tPrint < 10) ? "0" : "") << tPrint
                 << ':' << ((mPrint < 10) ? "0" : "") << mPrint
                 << " - 23:59): ";
            // Leser slutt tid
            cin >> sluttT >> sluttM; cin.ignore();
            sluttTotal = (sluttT*60)+sluttM; // Omregner for sjekk
            // Sørger for lovlig slutt-tid
            while (!gyldigTid(sluttT, sluttM) || sluttTotal < startTotal) {
                cout << "\nUlovlig klokkeslett. Prøv igjen (tt mm): ";
                cin >> sluttT >> sluttM; cin.ignore();
                sluttTotal = (sluttT*60)+sluttM; // Omregner for sjekk
            }
            // Omregning for for-loop, antall avganger som skal legges i vector:
            antGanger = (((sluttT-startT)*60)+sluttM-startM)/tidMellom;
            avgangsTid = startTid+diff; // Setter på differanse en gang
    
            for (int i = 0; i < antGanger; i++) { // For antall avgangstider:
                avgangsTid = avgangsTid + tidMellom; // Omregning
                if (avgangsTid < 1440) { // < 24:00
                    avgangstider.push_back(avgangsTid);
                } else { // Omregning ved for å unngå 25:xx osv.:
                    avgangstider.push_back(avgangsTid%1440); // Legges i vektor
                }
            }
    
            tPrint = (sluttTotal+1)/60; // Regner ut gyldig utskrift av t og min:
            mPrint = (sluttTotal+1)%60;
            // Hvis slutttid er før 23:53 (siste lovlige avgang mtp. 6 min):
            if (sluttTotal < 1433) {
                cout << "\n\nFra kl. (" << ((tPrint < 10) ? "0" : "") <<  tPrint
                     << ':' << ((mPrint < 10) ? "0" : "") << mPrint
                     << " - 23:53): ";
                cin >> startT >> startM; cin.ignore(); // Leser ny starttid
                startTotal = (startT*60)+startM; // Omregning
                // Sørger for ny lovlig starttid:
                while ((!gyldigTid(startT, startM) || startTotal <= sluttTotal)
                    && (startT != 0 || startM != 0)) { 
                    cout << "\nUlovlig klokkeslett. Prøv igjen (tt mm): ";
                    cin >> startT >> startM; cin.ignore();
                    startTotal = (startT*60)+startM; // Omregning
                }
                startTid = (startT*60)+startM; // Omregning
            } else { // Avslutter innlesning fra bruker hvis slutttid er 23:53
                     // eller senere (da det må være +1 min minimum til neste
                     // avgang). 
                startT = 0; startM = 0; // Sørger for stopp av innlesning
            }
            
        }
        cout << "\n\nRutetabell for stoppested: " << stSted << "\n\n";
             // Sorterer vektoren, slik at man ikke får duplikat utskrift av
             // evt. veldig tidlige avganger og sene avganger:
             sort(avgangstider.begin(), avgangstider.end());
    
        for (int i = 0; i < avgangstider.size(); i++) { // For hver avgangstid:
            timer = avgangstider[i]/60; // Regner ut time
            minutter = avgangstider[i]%60; // Regner ut minutter
            if (timer == n) { // Skriver ut minutter hvis time er lik indikator
                cout << ' ' << ((minutter < 10) ? "0" : "") << minutter;
            } else { // Skriver ut time hvis time er mer enn indikator
                if (timer < 24) { // Hvis time er mindre enn 24:
                    cout << '\n' << ((timer < 10) ? "0" : "")
                         << timer << ':' << ' ' << ((minutter < 10) ? "0" : "")
                         << minutter;
                } else { // Hvis time er mer enn 24 (skjer ved stor avstand)
                         // mellom stoppesteder på en rute, gjør modulo
                         // slik at det ikke skrives ut 25:xx f.eks.:
                    cout << '\n' << ((timer%24 < 10) ? "0" : "") << timer%24 
                         << ": " << ((minutter < 10) ? "0" : "")
                         << minutter;
                }
                n = timer; // Oppdaterer indikator
            }
        }
    }
    
    /**
     * Skriver ut til bruker endestasjonene på en rute i begge retninger
     *
     * @see Stopp::hentNr()
     * @see Stoppesteder::hentNavnVhaIndeks(...)
     */
    void Rute::skrivRetninger() const {
        // Hjelpevariabler for å hente endestasjonsnavnene på ruten:
        int startIndeks = stoppene.front()->hentNr(),
            stoppIndeks = stoppene.back()->hentNr();
        string startStasjon = gStoppestederBase.hentNavnVhaIndeks(startIndeks),
               endeStasjon = gStoppestederBase.hentNavnVhaIndeks(stoppIndeks);
    
        // Skriver ut start- og endestasjon for ruten i begge retninger:
        cout << "\nFram:    Retningen: " << startStasjon << " - "
             << endeStasjon << "\nTilbake: Retningen: "
             << endeStasjon << " - " << startStasjon << '\n';
    }
    
    /**
     * Skriver ut hele ruten forlengs eller baklengs, med tid mellom
     * stopper samt totaltid på ruten
     *
     * @param retning Fram (forlengs) eller Tilbake (baklengs)
     * @see Stoppesteder::hentNavnVhaIndeks(...)
     */
    void Rute::skrivRute(const Retning retning) {
        int minF = 0,
            indeks = 0,
            totMin = 0;
        string navn;
    
        if (retning == Fram) {
            // For hvert stopp på ruten
            for (auto it = stoppene.begin(); it != stoppene.end(); it++) {
                indeks = (*it)->nr; // Henter eget nr
                minF = (*it)->minutter; // Henter antall min til forrige stopp
                // Henter eget navn:
                navn = gStoppestederBase.hentNavnVhaIndeks(indeks);
                totMin += minF; // Oppdaterer totalt antall min på ruten
                cout << setw(25) << navn << " - " << setw(2) << minF
                     << " min fra forrige  (" << setw(3) << totMin
                     << " min fra rutestart)\n";
            }
        }
    
        if (retning == Tilbake) {
            // For hvert stopp på ruten, traverserer listen "baklengs":
            for (auto it = stoppene.rbegin(); it != stoppene.rend(); it++) {
                indeks = (*it)->nr; // Henter eget nr
                // Henter eget navn
                navn = gStoppestederBase.hentNavnVhaIndeks(indeks);
                cout << setw(25) << navn << " - " << setw(2) << minF
                        << " min fra forrige  (" << setw(3) << totMin
                        << " min fra rutestart)\n";
                minF = (*it)->minutter; // Henter antall minutter fra forrige stopp
                totMin += minF; // Oppdaterer totalt antall minutter på ruten
            }
        }
    }
    
    /**
     * Sletter en ugyldig rute (mindre enn et stopp, tømmer listen)
     */
    void Rute::slettData(){
        stoppene.clear(); // Tømmer listen for stopp
    }
    
    /**
     * Leser og oppretter stopp for en ny rute, så lenge bruker ønsker
     * eller det finnes stopp som ikke allerede eksisterer
     * på ruten
     *
     * @see Stoppesteder::byttBokstaver(...)
     * @see Rute::erTall(...)
     * @see Stoppesteder::finnesIndeks(...)
     * @see Stoppesteder::hentNavnVhaIndeks(...)
     * @see Stoppesteder::finnEntydig(...)
     * @see Stoppesteder::hentIndeksVhaNavn(...)
     * @see Stoppesteder::finnDuplikat(...)
     * @see Rute::finnesStopp(...)
     * @see Stoppested::finnesNabo(...)
     * @see Stoppested::hentNaboIndeks(...)
     * @see Stoppested::hentNaboTid(...)
     * @see Stoppested::settNaboIndeks(...)
     * @see Stoppested::settNaboTid(...)
     */
    void Rute::lesData() {
        string navn,
               fNavn;
        int stoppNr = 0,
            indeks = 0,
            naboIndeks = 0,
            tidTilF = 0; // Tid til forrige nabo
        Stopp* nyStopp = nullptr; // Peker til nytt stopp på ruten
        Stoppested* fStopp = nullptr; // Peker til forrige faktiske stoppested
        Stoppested* nStopp = nullptr; // Peker til nåværende faktiske stoppested
    
        cout << "\nStoppested (entydig navn / tall / ENTER for å avslutte): ";
        getline(cin, navn);
        while (navn.size() > 0) {  //Kjør så lenge ikke blank/enter er trykket.
            navn = gStoppestederBase.byttBokstaver(navn); // Fjerner æøåÆØÅ
    
            if (erTall(navn)) {  // Sjekker om navn kun består av tall
                stoppNr = stoi(navn); //Hvis kun tall, gjør om til int
                // Hvis tallet som er oppgitt er et faktisk stoppstedsnr:
                if (gStoppestederBase.finnesIndeks(stoppNr-1)) {
                navn = gStoppestederBase.hentNavnVhaIndeks(stoppNr); // Hent navn
                }
            }
    
            fNavn = gStoppestederBase.finnEntydig(navn); // Sjekker for entydighet
    
            if (fNavn.size() > 0) { // Hvis entydig navn:
                cout << '\n' << fNavn << '\n';
                // Henter et stoppesteds faktiske indeks i vector:
                indeks = gStoppestederBase.hentIndeksVhaNavn(fNavn);
                // Hvis stopp ikke finnes fra før på ruten:
                if (!finnesStopp(indeks+1)) { 
                    
                    if (stoppene.size() > 0) { // Hvis ikke første stopp på ruten
                    // Henter peker til eget stoppested:
                    nStopp = gStoppestederBase.finnDuplikat(fNavn);
                        // Hvis nabo finnes fra før:
                        if (nStopp->finnesNabo(naboIndeks+1)) { 
                            // Henter naboens indeks
                            naboIndeks = nStopp->hentNaboIndeks(naboIndeks+1);
                            // Henter tid til naboen:
                            tidTilF = nStopp->hentNaboTid(naboIndeks);
                            // Oppretter en ny stopp på ruten:
                            nyStopp = new Stopp(indeks+1, tidTilF);
                            stoppene.push_back(nyStopp); // Legges i lista
                            // Oppdaterer naboindeks for videre bruk:
                            naboIndeks = indeks; 
                            cout << "\nTiden mellom stoppestedene er allerede"
                                 << " registrert som "
                                 << tidTilF << " minutter.\n";
    
    
                        } else { // Hvis nabo ikke finnes fra før:
                            // Leser tid til forrige nabo:
                            tidTilF = lesInt("Tid fra forrige stopp", 1, 10);
                            // Finner egen peker:
                            nStopp = gStoppestederBase.finnDuplikat(fNavn);
                            // Oppdaterer forrige stopps nabovektor
                            fStopp->settNaboIndeks(indeks);
                            // Oppdaterer forrige stopps tid til nabo
                            fStopp->settNaboTid(tidTilF);
                            // Oppdaterer nåværende stopps nabovektor
                            nStopp->settNaboIndeks(naboIndeks);
                            // Oppdaterer naværende stopps tid til nabo
                            nStopp->settNaboTid(tidTilF);
                            // Oppretter ny stopp på ruten:
                            nyStopp = new Stopp(indeks+1, tidTilF);
                            // Stoppen legges til ruten:
                            stoppene.push_back(nyStopp);
                            // Oppdaterer naboindeks for videre bruk:
                            naboIndeks = indeks;
                        }
    
                    } else { // Hvis første stopp på ruten:
                        // Henter peker til eget stoppested:
                        nStopp = gStoppestederBase.finnDuplikat(fNavn);
                        nyStopp = new Stopp(indeks+1, 0); // Setter stopp sitt nr
                        // til å korrespondere med indeks i vector, og tid til
                        // forrige nabo å være 0, da første stopp på ruten
                        stoppene.push_back(nyStopp); // Legges til ruten
                        // Oppdaterer naboindeks for videre bruk
                        naboIndeks = indeks; 
                    }
                    // Setter peker til forrige stopp til å peke på riktig
                    // stoppested for videre bruk:
                    fStopp = nStopp; 
                } else // Stoppet finnes allerede på ruten:
                    cout << "\nStoppet er allerede registrert på ruten!\n";
            } else { // Hvis ikke entydig:
                cout << "\nIkke funnet (den entydige) stoppestedet!\n";
            }
    
            // Forsøker å lese evt. ny eller korrekt stopp:
            cout << "\nStoppested (entydig navn / tall / ENTER for å avslutte): ";
            getline(cin, navn);
        } // Ved enter på første navn/tall for early "uthopp":
        cout << "\nOk, du angrer og ønsker ikke å legge til ny rute.\n";
    }
    
    /**
     * Skriver hele ruten begge veier (forlengs) og (baklengs)
     * ifm. menyvalg R B
     *
     * @see Rute::skrivRute(...)
     */
    void Rute::skrivBeskrivelse() {
        cout << "Ruten:\n";
        skrivRute(Fram);
        cout << "\n\n";
        skrivRute(Tilbake);
    }
    
    /**
     * Skriver ut en rutes startstoppested og sluttstoppested
     *
     * @see Stopp::hentNr()
     * @see Stoppesteder::hentNavnVhaIndeks(...)
     */
    void Rute::skrivData() const {
        int startIndeks,
            stoppIndeks;
        string startNavn,
               stoppNavn;
    
        startIndeks = stoppene.front()->hentNr(); // Henter startsteds unike nr
        stoppIndeks = stoppene.back()->hentNr(); // Henter stopsteds unike nr
        startNavn = gStoppestederBase.hentNavnVhaIndeks(startIndeks);
        stoppNavn = gStoppestederBase.hentNavnVhaIndeks(stoppIndeks);
    
        cout << startNavn << " - " << stoppNavn;
    }
    
    /**
     * Skriver ut kort informasjon om en rute (virtuell)
     */
    void Rute::skrivKort() const {
        // Gjør ingenting, må være her så Buss og Bane kan bruke
        // sin skrivKort funksjon
    }
    
    /**
     * Skriver ut en rute til fil
     *
     * @param ut fila det skrives til
     * @see Stopp::skrivTilFil(...)
     */
    void Rute::skrivTilFil(ofstream & ut) {
        ut << stoppene.size() << '\n'; // Skriver ut antall stopp på ruta
        // For hver stopp:
        for (auto it = stoppene.begin(); it != stoppene.end(); it++)
            (*it)->skrivTilFil(ut); // Stopp skriver seg selv til fil
    }