#include "hakkeriai.hh" #include #include // "Ääretön" AI-algoritmia varten, suurempi kuin oikeiden siirtovaihtoehtojen // hyvyysluvut. const int AI_INF = 9999999; // Muodostaa joukon mahdollisia siirtoja. // Sisältää erinäistä logiikkaa, palautetut siirrot eivät ole kaikki // mahdolliset. // Parametri säätelee jossain määrin muodostettujen siirtojen määrää. void muodostaSiirrot(std::vector &siirrot, const Pelitilanne &pelitilanne, int maara) { // Muut kuin vuorossa oleva std::vector hyokkayskohteet; for (unsigned int i = 0; i < pelitilanne.ryhmat.size(); ++i) { const Hakkeriryhma &ryhma = pelitilanne.ryhmat.at(i); if (i != pelitilanne.vuoro) { hyokkayskohteet.insert(hyokkayskohteet.begin(), ryhma.hakkerit.begin(), ryhma.hakkerit.end()); } else { hyokkayskohteet.insert(hyokkayskohteet.begin(), ryhma.hakkerit.begin(), ryhma.hakkerit.begin() + ryhma.vuoro); hyokkayskohteet.insert(hyokkayskohteet.begin(), ryhma.hakkerit.begin() + ryhma.vuoro + 1, ryhma.hakkerit.end()); } } Hakkeritila tila = haeVuoro(pelitilanne)->haeTila(); int hermot = haeVuoro(pelitilanne)->haeHermot(); int kola = haeVuoro(pelitilanne)->haeKola(); siirrot.push_back(Siirto(ODOTA)); if (tila != PERUS) { siirrot.push_back(Siirto(RESETOI)); } // Mahdolliset kolatasot maaran mukaan jaettuna // Kulutusrajat int maksimi = kola + hermot - 1; // Jätetään ainakin 1 piste hermoja int muutos = maksimi / maara; // Jaetaan maara väliin. for (int kolat = maksimi; kolat > maksimi / 10; kolat -= hermot) { Siirto koodaus(KOODAA); koodaus.maara = kolat / pelitilanne.asetukset.koodauskulutus; siirrot.push_back(koodaus); Siirto puskurointi(PUSKUROI); puskurointi.maara = kolat / pelitilanne.asetukset.puskurointikulutus; siirrot.push_back(puskurointi); if (tila == PERUS && kolat <= kola) { Siirto tankkaus(TANKKAA); tankkaus.maara = kolat; siirrot.push_back(tankkaus); } // Vain yksi kohde per reititys for (unsigned int kohde = 0; kohde < hyokkayskohteet.size(); ++kohde) { Siirto reititys(REITITA); reititys.reitityskohteet.push_back(hyokkayskohteet.at(kohde)); reititys.maara = kolat / pelitilanne.asetukset.reitityskulutus; siirrot.push_back(reititys); } } if (haeVuoro(pelitilanne)->haeVirus() != 0) { for (unsigned int kohde = 0; kohde < hyokkayskohteet.size(); ++kohde) { Siirto hyokkays(HYOKKAA); hyokkays.kohde = hyokkayskohteet.at(kohde); siirrot.push_back(hyokkays); } } } Hakkeri *wrapHakkeri(Hakkeri *vanha, const std::vector &vryhmat, const std::vector &uryhmat) { for (unsigned int ryhma = 0; ryhma < vryhmat.size(); ++ryhma) { for (unsigned int hakkeri = 0; hakkeri < vryhmat.at(ryhma).hakkerit.size(); ++hakkeri) { if (vryhmat.at(ryhma).hakkerit.at(hakkeri) == vanha) { return uryhmat.at(ryhma).hakkerit.at(hakkeri); } } } return NULL; } void wrapSiirto(Siirto &siirto, const std::vector &vryhmat, const std::vector &uryhmat) { if (siirto.tyyppi == HYOKKAA) { siirto.kohde = wrapHakkeri(siirto.kohde, vryhmat, uryhmat); } if (siirto.tyyppi == REITITA) { siirto.reitityskohteet.at(0) = wrapHakkeri(siirto.reitityskohteet.at(0), vryhmat, uryhmat); } } int laskeHyvyys(const Pelitilanne &pelitilanne) { int tulos = 0; for (unsigned int ryhmai = 0; ryhmai < pelitilanne.ryhmat.size(); ++ryhmai) { int ryhmansumma = 0; const Hakkeriryhma &ryhma = pelitilanne.ryhmat.at(ryhmai); for (unsigned int hakkeri = 0; hakkeri < ryhma.hakkerit.size(); ++hakkeri) { ryhmansumma += ryhma.hakkerit.at(hakkeri)->haeKola(); ryhmansumma += (ryhma.hakkerit.at(hakkeri)->haeHermot() * pelitilanne.asetukset.tankkauskulutus); ryhmansumma += ryhma.hakkerit.at(hakkeri)->haeVirus(); } if (ryhmai == pelitilanne.vuoro) { tulos += ryhmansumma; } else { tulos -= ryhmansumma; } } return tulos; } void etsiSiirto(Siirto &tulos, const Pelitilanne &pelitilanne) { std::vector siirrot; std::vector::const_iterator iter; Pelitilanne uusitilanne; int paras = -AI_INF; muodostaSiirrot(siirrot, pelitilanne, 2); if (siirrot.size() == 0) { throw; // Ei näin. } tulos = Siirto(ODOTA); // Tutkitaan siirtovaihtoehdot for (iter = siirrot.begin(); iter < siirrot.end(); iter++) { int hyvyys = 0; Siirto uussiirto(*iter); kopioiTilanne(uusitilanne, pelitilanne); wrapSiirto(uussiirto, pelitilanne.ryhmat, uusitilanne.ryhmat); std::ostringstream dummy(""); suoritaSiirto(uussiirto, uusitilanne, dummy); // Hyvyysarvot muutetaan vastaluvuiksi vastustajan siirtoa varten if (uusitilanne.vuoro != pelitilanne.vuoro) { hyvyys = - ai_rekursio(uusitilanne, 2, - AI_INF, AI_INF); } else { hyvyys = ai_rekursio(uusitilanne, 2, - AI_INF, AI_INF); } vapautaHakkerit(uusitilanne.ryhmat); if (hyvyys > paras) { // Parempi kuin paras tähän mennessä -> tallennetaan paras = hyvyys; tulos = *iter; } } std::cout << "Paras " << paras << std::endl; std::cout << "Nyt " << laskeHyvyys(pelitilanne) << std::endl; } // Tekoäly // Etsii seuraavat syvyys siirtoa, vuorotellen oman ja vastustajan siirrot. // Algoritmi on ns. negamax, alpha-beta pruning -optimoinnilla. // Palauttaa omalta kannalta parhaan siirron tulos-viittauksen kautta. int ai_rekursio(const Pelitilanne &pelitilanne, int syvyys, int alaraja, int ylaraja) { int paras = - AI_INF; std::vector siirrot; std::vector::const_iterator iter; Pelitilanne uusitilanne; if (pelitilanne.lopetettu) { if (ryhmaMukana(pelitilanne.ryhmat.at(pelitilanne.vuoro))) { return AI_INF; } else { return -AI_INF; } } if (syvyys == 0) { // Hakusyvyys saavutettu, lasketaan pisteet return laskeHyvyys(pelitilanne); } muodostaSiirrot(siirrot, pelitilanne, 2); if (siirrot.size() == 0) { throw; // Ei näin. } // Tutkitaan siirtovaihtoehdot for (iter = siirrot.begin(); iter < siirrot.end(); iter++) { int hyvyys = 0; Siirto uussiirto(*iter); kopioiTilanne(uusitilanne, pelitilanne); wrapSiirto(uussiirto, pelitilanne.ryhmat, uusitilanne.ryhmat); std::ostringstream dummy(""); suoritaSiirto(uussiirto, uusitilanne, dummy); // std::cout << "NEXT: " << laskeHyvyys(uusitilanne) << std::endl; // Hyvyysarvot muutetaan vastaluvuiksi vastustajan siirtoa varten if (uusitilanne.vuoro != pelitilanne.vuoro) { hyvyys = - ai_rekursio(uusitilanne, syvyys - 1, - ylaraja, -alaraja); } else { hyvyys = ai_rekursio(uusitilanne, syvyys - 1, alaraja, ylaraja); } vapautaHakkerit(uusitilanne.ryhmat); if (hyvyys > paras) { // Parempi kuin paras tähän mennessä -> tallennetaan paras = hyvyys; } if (paras > alaraja) { // Tätä huonompia siirtoja ei tarvitse tutkia alaraja = paras; } if (paras >= ylaraja) { // Löydettiin parempi siirto kuin muissa vaihtoehdoissa, joten // vastustaja ei anna pelin päätyä tähän siirtohaaraan. return paras; } } return paras; }