XOBoard negaMaxThink(XOBoard::Player player, XOBoard board) { /* TODO: Fa o miscare si returneaza tabla dupa aceasta miscare. Aceasta * functie de AI trebuie sa respecte acest format pentru ca este data in * constructorul jocului, dar puteti apela aici o functie scrisa de voi. */ int sol = -10, x, y, alpha = -10, beta =10; for (int i = 0; i < 3; ++ i) for (int j = 0; j < 3; ++ j) if (board.is_empty(i,j)) { board.put(player,i,j); int k = -negaMaxMin(OPPONENT(player), board, -beta, -alpha); std::cerr<<i<<" "<<j<<" "<<k<<std::endl; board.erase(i,j); if (k > alpha) { alpha = k; x = i; y = j; } } /* TODO: Stergeti linia de mai jos dupa ce rezolvati. */ board.put(player, x,y); return board; }
/** Un exemplu de functie de gandire care foloseste rezultatele furnizate de * o abordare negaMax exhaustiva. */ int negaMaxMin(XOBoard::Player player, XOBoard& board, int alpha, int beta) { int sol = -10; if( board.is_full()) { return board.get_score(player)-board.get_score(OPPONENT(player)); } for (int i = 0; i < 3; ++ i) for (int j = 0; j < 3; ++ j) if (board.is_empty(i,j)) { board.put(player,i,j); int k = -negaMaxMin(OPPONENT(player), board, -beta, -alpha); board.erase(i,j); if (k >= beta) return beta; if (k > alpha) alpha = k; } return alpha; }
/** Puteti folosi functia negaMax pentru a implementa un AI pe baza de negaMAX. * * Functia primeste ca parametri: * * player = Jucatorul care trebuie sa mute in continuare (identitatea * calculatorului care gandeste cu aceasta functie). * Valorile posibile sunt { XOBoard::PlayerX, XOBoard::PlayerO } * * board = Tabla pe care o vede jucatorul care trebuie sa mute in continuare. * * alpha = Inseamna ca player a gasit deja o cale prin care pot sa termin * jocul cu un scor cel putin egal cu alpha. * * beta = Inseamna ca OPPONENT(player) a gasit o cale prin care sa-l forteze pe * player sa termine jocul cu un scor cel mult egal cu beta (cu alte * cuvinte daca player gaseste o modalitate sa castige mai mult de beta, * cel mai probabil analizeaza un scenariu nerealist in care a presupus * ca OPPONENT(player) a fost prost la un moment dat si a facut o * greseala. */ std::pair<int, XOBoard> negaMax(XOBoard::Player player, XOBoard board, int alpha, int beta) { /* Daca s-a terminat jocul, scorul este cel raportat. */ if (board.game_over()) { int myScore = board.get_score(player) - board.get_score(OPPONENT(player)); return std::pair<int, XOBoard>(myScore, board); } /* Generam lista de expansiuni ale tablei (toate mutarile viitoare). */ std::vector<XOBoard> expansions; for (unsigned int i = 0; i < 3; ++i) { for (unsigned int j = 0; j < 3; ++j) { if (board.get(i, j) == '_') { board.put(player, i, j); expansions.push_back(board); board.erase(i, j); } } } /* Verificam care este mutarea cea mai inteleapta. */ XOBoard nextMove; for (unsigned int i = 0; i < expansions.size(); ++i) { /* Fiindca urmatorul nivel de negaMax este privit din partea oponentului, * cand apelez functia trebuie sa neg pe alfa si sa i-l servesc drept * beta pentru ca asta inseamna ca il "avertizez" ca nu sunt fraier si ca * deja stiu un mod prin care el nu poate sa faca mai mult decat -alpha. * * Pe de alta parte, desi eu il limitez pe el superior, din punct de vedere * inferior nu am nici un motiv sa-l limitez, asa ca ii voi servi un alpha * egal cu -INF (nu stiu cat de prost poate el sa joace, n-am cum sa-mi dau * seama). */ /* Acum ne gandim cum s-ar descurca el in situatia asta. */ std::pair<int, XOBoard> outcome = negaMax( OPPONENT(player), expansions[i], -INF, -alpha); /* Vedem ce miscare a reusit sa scoata el in conditiile date. */ int myScore = -outcome.first; /* Analizam jocul din perspectiva taierii alfa-beta. */ if (myScore > beta) { /* Inseamna ca asta e un scenariu in care el ar fi facut o greseala. Noi * stim ca el nu e prost, asa ca din moment ce a gasit deja mai sus in * arbore o modalitate prin care sa ma faca sa termin jocul cu cel mult * beta, n-o sa joace in asa fel incat sa ma puna pe mine in situatia * asta de acum. Aplicam deci, taierea beta. */ return std::pair<int, XOBoard>(beta, nextMove); } else if (myScore > alpha) { /* Inseamna ca tocmai am gasit o miscare prin care eu sa castig la sigur * mai mult decat stiam inainte ca pot sa castig (daca vreti, un fel de * plan "la sigur" mai bun). */ alpha = myScore; nextMove = expansions[i]; } } /* Raportam mutarea aleasa ca fiind cea mai buna. */ return std::pair<int, XOBoard>(alpha, nextMove); }