#include // cstring:n memcpy:lle taulukon kopiointiin ei liene // järkevää c++:n vastinetta #include #include #include #include #include #include "bonustoiminnot.hh" #include "perustoiminnot.hh" /* Etsitään mahdolliset siirtovaihtoehdot pelaajalle vuoro. Jokaista nappulaa kohti tarkastellaan viereiset 5x5 nappulaa. */ int mahdollisetsiirrot(std::vector &siirrot, Nappula vuoro, const Nappula pelilauta[LAUDAN_KOKO][LAUDAN_KOKO]) { int maara = 0; Siirto siirto = {0}; int &alkuX = siirto.alkuX; // Lyhyemmät nimet int &alkuY = siirto.alkuY; int &loppuX = siirto.loppuX; int &loppuY = siirto.loppuY; for (alkuX = 0; alkuX < LAUDAN_KOKO; alkuX++) { for (alkuY = 0; alkuY < LAUDAN_KOKO; alkuY++) { if (pelilauta[alkuX][alkuY] != vuoro) { // Muu kuin oma nappula continue; } for (loppuX = alkuX - 2; loppuX <= alkuX + 2; loppuX++) { for (loppuY = alkuY - 2; loppuY <= alkuY + 2; loppuY++) { if (!tarkistakoordinaatit(loppuX, loppuY)) { // Ei laudalla continue; } if (pelilauta[loppuX][loppuY] == TYHJA) { siirrot.push_back(siirto); maara++; if (!tarkistasiirto(pelilauta, vuoro, siirto)) { std::cout << "Msiirrot virhe!" << std::endl; } } } } } } return maara; } struct AIPuu { unsigned int tyhja; unsigned int oma; unsigned int toinen; }; // Esilasketun siirron hakeminen. // Ellei tiedostoa löydy tai se on viallinen, käyttäydytään kuin siirtoa // ei olisi tiedostossa, eli lasketaan se itse. bool ai_precalc(Siirto &tulos, Nappula vuoro, const Nappula pelilauta[LAUDAN_KOKO][LAUDAN_KOKO]) { int x = 0; int y = 0; std::string haettava_vuoro(""); std::ostringstream haettava_lauta; if (vuoro == MUSTA) { haettava_vuoro = "O"; } else { haettava_vuoro = "@"; } // Koostetaan haettava rivin alku for (y = 0; y < LAUDAN_KOKO; y++) { for (x = 0; x < LAUDAN_KOKO; x++) { switch (pelilauta[x][y]) { case MUSTA: haettava_lauta << "O"; break; case VALKOINEN: haettava_lauta << "@"; break; default: haettava_lauta << "."; break; } } } std::ifstream lahde("ai.precalc"); std::string luettu_vuoro(""); std::string luettu_lauta(""); while (lahde.good()) { lahde >> luettu_vuoro >> luettu_lauta; lahde >> tulos.alkuX >> tulos.alkuY; lahde >> tulos.loppuX >> tulos.loppuY; if (luettu_vuoro == haettava_vuoro && luettu_lauta == haettava_lauta.str()) { return true; // Tulos on jo tallennettu tulos-structiin } } return false; // Ei löytynyt } void ai_etsisiirto(Siirto &tulos, Nappula vuoro, const Nappula pelilauta[LAUDAN_KOKO][LAUDAN_KOKO]) { #ifdef AI_PRECALC if (ai_precalc(tulos, vuoro, pelilauta)) { return; } #endif #ifdef AI_SYVYYS std::cout << "HYVYYS " << ai_rekursio(tulos, vuoro, pelilauta, AI_SYVYYS, -9999, 9999) << std::endl; #else #ifndef AI_TD #define AI_TD 0 #endif int hyvyys = ai_rekursio(tulos, vuoro, pelilauta, AI_TD, -9999, 9999); int syvyys = 0; if (hyvyys < -2) syvyys = 4; else if (hyvyys < 2) syvyys = 4; else if (hyvyys < 5) syvyys = 3; else syvyys = 2; std::cout << "HYVYYS " << ai_rekursio(tulos, vuoro, pelilauta, syvyys, -9999, 9999) << std::endl; #endif if (!tarkistasiirto(pelilauta, vuoro, tulos)) { std::cout << "AI tuotti virheellisen siirron!" << std::endl; } } int laskehyvyys(const Nappula pelilauta[LAUDAN_KOKO][LAUDAN_KOKO], Nappula vuoro) { int valkoinen = 0; int musta = 0; laskepisteet(valkoinen, musta, pelilauta); if (vuoro == VALKOINEN) { return valkoinen - musta; } else { return musta - valkoinen; } } int ai_rekursio(Siirto &tulos, Nappula vuoro, const Nappula pelilauta[LAUDAN_KOKO][LAUDAN_KOKO], int syvyys, int alpha, int beta) { int paras = -9999; std::vector siirrot; std::vector::const_iterator iter; Nappula uusilauta[LAUDAN_KOKO][LAUDAN_KOKO] = {{TYHJA}}; if (mahdollisetsiirrot(siirrot, vuoro, pelilauta) == 0) { // Peli loppu int hyvyys = laskehyvyys(pelilauta, vuoro); if (hyvyys > 0) { return 9999; } else { return -9999; } } // Jos tutkiminen päättyy, tuotetaan silti edes jokin siirto. // Tämä tulee käyttöön jos AI esim. tajuaa häviävänsä joka tapauksessa. tulos = siirrot.at(0); if (syvyys == 0) { // Haku loppu return laskehyvyys(pelilauta, vuoro); } for (iter = siirrot.begin(); iter < siirrot.end(); iter++) { int hyvyys; Siirto temp; Nappula uusivuoro; if (vuoro == VALKOINEN) uusivuoro = MUSTA; else uusivuoro = VALKOINEN; memcpy(uusilauta, pelilauta, sizeof(Nappula)*LAUDAN_KOKO*LAUDAN_KOKO); toteutasiirto(uusilauta, *iter); hyvyys = - ai_rekursio(temp, uusivuoro, uusilauta, syvyys - 1, - beta, - alpha); if (hyvyys > paras) { paras = hyvyys; tulos = *iter; } #ifdef ALPHABETA if (paras >= beta) { break; } #endif if (paras > alpha) { alpha = paras; } } return paras; }