// Komentotulkin funktioiden toteutukset #include "merkkijonokasittely.hh" #include "komentotulkki.hh" #include "hakkeri.hh" #include "poikkeukset.hh" #include #include #include #include const int KOMENNOT_KOKO = 13; const Komento KOMENNOT[KOMENNOT_KOKO] = { {"HYOKKAA", SIIRTO, HYOKKAA, 1, 1}, {"KOODAA", SIIRTO, KOODAA, 1, 1}, {"ODOTA", SIIRTO, ODOTA, 0, 0}, {"PUSKUROI", SIIRTO, PUSKUROI, 1, 1}, {"REITITA", SIIRTO, REITITA, 2, -1}, {"RESETOI", SIIRTO, RESETOI, 0, 0}, {"TANKKAA", SIIRTO, TANKKAA, 1, 1}, {"TILANNE", TILANNE, EISIIRTOA, 0, 0}, {"TIEDOT", TIEDOT, EISIIRTOA, 0, 1}, {"APUA", OHJE, EISIIRTOA, 0, 0}, {"HELP", OHJE, EISIIRTOA, 0, 0}, {"LOPETA", LOPETUS, EISIIRTOA, 0, 0}, {"QUIT", LOPETUS, EISIIRTOA, 0, 0} }; const std::string KOMENTOOHJE( "Pelissä on käytettävissä seuraavat komennot:\n" " HYOKKAA kohde Hyökkää hakkerin hallussa olevalla viruksella.\n" " KOODAA taso Koodaa uuden viruksen.\n" " ODOTA Siirtää vuoron seuraavalle.\n" " PUSKUROI määrä Valmistautuu puskuroimaan viruksia.\n" " REITITA määrä kohteet Valmistautuu reitittämään viruksia.\n" " RESETOI Palauttaa hakkerin perustilaan.\n" " TANKKAA määrä Tankkaa kolaa hermojen palauttamiseksi.\n" " TILANNE Tulostaa koko pelin tilanteen.\n" " TIEDOT [hakkeri] Tulostaa annetun tai nykyisen hakkerin tiedot.\n" " APUA / HELP Tulostaa tämän tekstin.\n" " LOPETA / QUIT Lopettaa pelin.\n"); // Komentotulkin etsiHakkeri-toteutus, joka tuottaa Hakkerivirhe-poikkeuksen // ellei hakkeria löydy. Tällä tavalla ei muissa funktioissa tarvitse // suorittaa vertailuja NULL-osoittimeen. Hakkeri *etsiHakkeri_P(const std::string &nimi, const Pelitilanne &pelitilanne) { Hakkeri *hakkeri = etsiHakkeri(nimi, pelitilanne.ryhmat); if (hakkeri == NULL) { throw Hakkerivirhe(nimi); } return hakkeri; } // Toteuttaa rivin tai tulostaa virheen. Palauttaa true jos komento onnistui. bool toteutaRivi(const std::string &rivi, Pelitilanne &pelitilanne, std::ostream &out, std::ostream &err) { std::istringstream rivivirta(rivi); std::vector osat; lueOsat(rivivirta, osat); if (osat.size() < 1) { // Tyhjä rivi return false; } Komento komento = {"", EITOIMINTOA, EISIIRTOA, 0, 0}; if (!etsiKomento(osat.at(0), komento)) { err << "Virhe: Tuntematon komento." << std::endl; return false; } // Poistetaan komento, jätetään parametrit osat.erase(osat.begin()); int parametreja = osat.size(); // Signed-tyypiksi vertailua varten. if (komento.parametreja_enintaan != -1 && parametreja > komento.parametreja_enintaan) { err << "Virhe: Liikaa parametreja." << std::endl; return false; } if (komento.parametreja_vahintaan != -1 && parametreja < komento.parametreja_vahintaan) { err << "Virhe: Liian vähän parametreja." << std::endl; return false; } return toteutaKomento(komento, osat, pelitilanne, out, err); } // Palauttaa kehotteena käytettävän merkkijonon (mukaanlukien >-merkin). std::string haeKehote(Pelitilanne &pelitilanne) { return haeVuoro(pelitilanne)->haeNimi() + " > "; } // Tulkitsee komennon etsimällä vastaavan komennon vakiotaulukosta. // Palauttaa false jos vastaavuuksia ei ole tai on liikaa. bool etsiKomento(const std::string &annettu, Komento &tulos) { int vastaavuuksia = 0; // Käydään vakiotaulukko läpi for (int i = 0; i < KOMENNOT_KOKO; i++) { if (vertaaLyhennos(annettu, KOMENNOT[i].komento)) { tulos = KOMENNOT[i]; vastaavuuksia += 1; } } // Palautetaan true jos vastaavuuksia oli tasan yksi. return (vastaavuuksia == 1); } // Tulkitsee parametrit ja toteuttaa komennon. // Virheen sattuessa palauttaa false ja tulostaa virheen. bool toteutaKomento(const Komento &komento, const std::vector ¶metrit, Pelitilanne &pelitilanne, std::ostream &out, std::ostream &err) { Siirto siirto(EISIIRTOA); Hakkeri *hakkeri = NULL; try { switch (komento.toiminto) { case SIIRTO: siirto = tulkitseSiirto(komento, parametrit, pelitilanne); hakkeri = haeVuoro(pelitilanne); if (!hakkeri->tarkistaSiirto(siirto, err)) { return false; } suoritaSiirto(siirto, pelitilanne, out); break; case TILANNE: tulostaTilanne(pelitilanne.ryhmat, out); break; case TIEDOT: // Vuorossa oleva tai annettu hakkeri. hakkeri = haeVuoro(pelitilanne); if (parametrit.size() != 0) { hakkeri = etsiHakkeri_P(parametrit.at(0), pelitilanne); } hakkeri->tulostaTiedot(out); break; case OHJE: out << KOMENTOOHJE; break; case LOPETUS: pelitilanne.lopetettu = true; break; default: // Tänne ei pitäisi päätyä throw std::invalid_argument("Virheellinen toimintotyyppi"); } } catch (Lukuvirhe &virhe) { err << "Virhe: " << virhe.what(); err << " ei ole tulkittavissa nollaa suurempana"; err << " positiivisena kokonaislukuna." << std::endl; return false; } catch (Hakkerivirhe &virhe) { if (komento.toiminto == SIIRTO && komento.siirtotyyppi == HYOKKAA) { // Speksi edellyttää että ilmoitus puuttuvasta viruksesta // tuotetaan, vaikkei hakkeriakaan olisi. // Nyt jo tiedetään ettei siirtoa voida suorittaa, mutta // korvataan kohde NULL:lla että saadaan tulostettua ilmoitus. siirto.tyyppi = HYOKKAA; siirto.kohde = NULL; if (!haeVuoro(pelitilanne)->tarkistaSiirto(siirto, err)) { return false; } // Jos siirron tarkistus meni läpi, jatketaan hakkerivirheen // tulostukseen. } err << "Virhe: Tuntematon hakkeri "; err << virhe.what() << "." << std::endl; return false; } return true; } // Tulkitsee siirroksi tiedetyn komennon parametrit // Siirto-rakenteeseen. Tuottaa poikkeuksen Lukuvirhe tai Hakkerivirhe jos // parametreissa on virheitä. Siirto tulkitseSiirto(const Komento &komento, const std::vector ¶metrit, const Pelitilanne &pelitilanne) { Siirto siirto(komento.siirtotyyppi); // Ensimmäinen parametri // Parametreja on ko. komennoilla ainakin yksi, sillä komentotulkki // on jo tarkistanut tämän. switch (komento.siirtotyyppi) { case KOODAA: case PUSKUROI: case REITITA: case TANKKAA: siirto.maara = kokonaisluvuksi(parametrit.at(0)); break; case HYOKKAA: siirto.kohde = etsiHakkeri_P(parametrit.at(0), pelitilanne); break; default: // Komennolla ei parametreja break; } if (komento.siirtotyyppi == REITITA) { // Käsitellään reitityskohteet for (unsigned int i = 1; i < parametrit.size(); i++) { Hakkeri *hakkeri = etsiHakkeri_P(parametrit.at(i), pelitilanne); siirto.reitityskohteet.push_back(hakkeri); } } return siirto; }