2012-11-23 11 views
6

Ho provato a codificare l'algoritmo minimax per tic-tac-toe indicato nel libro di Russel Norvig sull'intelligenza artificiale. Aveva tutto tranne che il modo di restituire il meglio all'utente. Sto cercando di restituire il Move migliore, ma non riesco a decidere quando scegliere il migliore. Aiuto, chiunque?Restituisce bestMove nell'algoritmo minimax per tictactoe

moveT MiniMax(stateT state) 
{ 
    moveT bestMove; 

    max_move(state,bestMove); 

    return bestMove; 

} 

int max_move(stateT state,int & bestMove) 
{ 
    int v = -10000; 
    if(GameIsOver(state)) 
    { 
     return EvaluateStaticPosition(state); 

    } 

    vector<moveT> moveList; 
    GenerateMoveList(state, moveList); 
    int nMoves = moveList.size(); 

    for(int i = 0 ; i < nMoves ; i++) 
    { 
     moveT move = moveList[i]; 
     MakeMove(state, move); 

     int curValue = min_move(state,bestMove); 

      if(curValue > v) 
      { 
       v = curValue; 
       bestMove = move; 
      } 
     RetractMove(state, move); 

    } 

    return v; 

} 

int min_move(stateT state, int &bestMove) 
{ 
    int v = 10000; 
    if(GameIsOver(state)) 
    { 
     return EvaluateStaticPosition(state); 

    } 
    vector<moveT> moveList; 
    GenerateMoveList(state, moveList); 

    int nMoves = moveList.size(); 

    for(int i = 0 ; i < nMoves; i++) 
    { 
     moveT move = moveList[i]; 
     MakeMove(state, move); 

     int curValue = max_move(state,depth+1,bestMove); 

      if(curValue < v) 
      { 
       curValue = v; 
      } 
     RetractMove(state, move); 

    } 
    return v; 
} 

P.S .: Ci sono altri pseudocodice per trovare il valore MinMax. Tuttavia, si concentrano solo su tic-tac-toe, sto cercando di estenderlo ad altri giochi. Grazie.

Update: L'intero codice può essere trovato qui: http://ideone.com/XPswCl

+0

Il codice che hai postato sopra è aggiornato? Perché non sembra che dovrebbe compilare. In 'min_move' chiami' max_move' con tre argomenti, ma max_move può solo prendere due argomenti. – Kevin

+0

@ Kevin: Oops, è ora aggiornato. Ho provato a limitare la profondità ad un certo punto. – motiur

+0

Grazie per l'aggiornamento, ma la linea errata è ancora lì: 'int curValue = max_move (stato, profondità + 1, bestMove);' Questo mi fa preoccupare; mi fa sospettare che il codice che stai postando non sia il codice che stai compilando. Ciò rende doppiamente difficile per i potenziali rispondenti individuare il problema. Identificheremo i bug nel codice pubblicato che non sono presenti nel codice reale e non saremo in grado di trovare bug nel codice reale se non sono nel codice pubblicato. – Kevin

risposta

7

Nella versione più semplice di Minimax, il primo giocatore vuole massimizzare il suo punteggio, ed il secondo giocatore vuole ridurre al minimo il punteggio del primo giocatore. Dal momento che sia primo e il secondo giocatore si preoccupano solo di segnare il primo giocatore, EvaluateStaticPosition dovrebbe restituire un valore che indica quanto è buono il Consiglio di Stato è per il primo giocatore. Di chi è il turno non è rilevante.

int EvaluateStaticPosition(stateT state) 
{ 
     if(CheckForWin(state, FIRST_PLAYER)) 
     { 
       return WINNING_POSITION; 
     } 
     if(CheckForWin(state, Opponent(FIRST_PLAYER))) 
     { 
       return LOSING_POSITION; 
     } 
     return NEUTRAL_POSITION; 
} 

Ora, quando si desidera che la mossa che è meglio per il primo giocatore, chiamare MaxMove. Quando vuoi la mossa migliore per il secondo giocatore, chiama MinMove.

moveT MiniMax(stateT state) 
{ 
    moveT bestMove; 
    int i = 0; 
    if (state.whoseTurn == FIRST_PLAYER){ 
     i = MaxMove(state, bestMove); 
    } 
    else{ 
     i = MinMove(state,bestMove); 
    } 
    cout<<"i is "<<i<<endl; 
    return bestMove; 
} 

Infine, avete alcuni problemi all'interno della MinMove e MaxMove. quando si assegna curRating in uno dei due, non si dovrebbe passare nel bestMove come secondo argomento al MaxMove o MinMove. Sarà quindi messo mossa migliore della dell'avversario in bestMove, che non ha senso. Invece, dichiarare un oggetto opponentsBestMove e passarlo come secondo argomento. (In realtà non utilizzerai l'oggetto o guarderai il suo valore in seguito, ma va bene). Con questo cambiamento, non hai mai nulla per assegnare bestMove all'interno MinMove, così si dovrebbe fare in modo all'interno del blocco if(curRating < v).

int MaxMove(stateT state, moveT &bestMove) 
{ 
     if(GameIsOver(state)) 
     { 
      return EvaluateStaticPosition(state); 
     } 
     vector<moveT> moveList; 
     GenerateMoveList(state, moveList); 
     int nMoves = moveList.size(); 
     int v = -1000; 
     for(int i = 0 ;i<nMoves; i++) 
     { 
       moveT move = moveList[i]; 
       MakeMove(state, move); 
       moveT opponentsBestMove; 
       int curRating = MinMove(state, opponentsBestMove); 
       if (curRating > v) 
       { 
         v = curRating; 
         bestMove = move; 
       } 
       RetractMove(state, move); 
     } 
     return v; 

} 
int MinMove(stateT state, moveT &bestMove) 
{ 
     if(GameIsOver(state)) 
     { 
       return EvaluateStaticPosition(state); 
     } 
     vector<moveT>moveList; 
     GenerateMoveList(state, moveList); 
     int nMoves = moveList.size(); 
     int v = 1000; 
     for(int i = 0 ; i<nMoves; i++) 
     { 
       moveT move = moveList[i]; 
       MakeMove(state , move); 
       moveT opponentsBestMove; 
       int curRating = MaxMove(state,opponentsBestMove); 
       if(curRating < v) 
       { 
         v = curRating; 
         bestMove = move; 
       } 
       RetractMove(state, move); 
     } 
     return v; 
} 

A questo punto si dovrebbe avere un'IA imbattibile!

The final position looks like this: 

O | O | X 
---+---+--- 
X | X | O 
---+---+--- 
O | X | X 

Cat's game. 

Un metodo alternativo sfrutta il fatto che tic-tac-toe è un gioco a somma zero. In altre parole, alla fine del gioco, la somma dei punteggi dei giocatori sarà uguale a zero. Per una partita a due giocatori, questo significa che il punteggio di un giocatore sarà sempre il negativo dell'altro giocatore. Questo è conveniente per noi, poiché minimizzare il punteggio dell'altro giocatore è quindi identico a massimizzare il proprio punteggio. Quindi, invece di un giocatore che massimizza il suo punteggio e un giocatore che riduce al minimo il punteggio dell'altro giocatore, possiamo semplicemente fare in modo che entrambi i giocatori tentino di massimizzare il proprio punteggio.

Sostituire EvaluateStaticPosition nella sua forma originale, in modo da fornire un punteggio in base a quanto è buono lo stato della scheda per il lettore corrente.

int EvaluateStaticPosition(stateT state) 
{ 
     if(CheckForWin(state, state.whoseTurn)) 
     { 
       return WINNING_POSITION; 
     } 
     if(CheckForWin(state, Opponent(state.whoseTurn))) 
     { 
       return LOSING_POSITION; 
     } 
     return NEUTRAL_POSITION; 
} 

Elimina MinMove, poiché ci interessa solo massimizzare. Riscrivere MaxMove in modo che sceglie la mossa che dà l'avversario peggiore punteggio possibile. Il punteggio per la mossa migliore è il negativo del peggior punteggio dell'altro giocatore.

int MaxMove(stateT state, moveT &bestMove) 
{ 
     if(GameIsOver(state)) 
     { 
       return EvaluateStaticPosition(state); 
     } 
     vector<moveT> moveList; 
     GenerateMoveList(state, moveList); 
     int nMoves = moveList.size(); 
     int v = -1000; 
     for(int i = 0 ;i<nMoves; i++) 
     { 
       moveT move = moveList[i]; 
       MakeMove(state, move); 
       moveT opponentsBestMove; 
       int curRating = -MaxMove(state, opponentsBestMove); 
       if (curRating > v) 
       { 
         v = curRating; 
         bestMove = move; 
       } 
       RetractMove(state, move); 
     } 
     return v; 

} 

Dal MaxMove viene utilizzato per entrambi i giocatori, non abbiamo più bisogno di distinguere tra i giocatori nella funzione MiniMax.

moveT MiniMax(stateT state) 
{ 
    moveT bestMove; 
    int i = 0; 
    i = MaxMove(state, bestMove); 
    cout<<"i is "<<i<<endl; 
    return bestMove; 
} 
+0

Se non sbaglio, hai inserito bestMove = spostati intenzionalmente in MinMove quando si esegue la correzione di motiur

+0

Sì, ho intenzionalmente messo 'bestMove = move' nella funzione' MinMove', all'interno del blocco 'curRating Kevin

+0

L'uomo che lavora, vorrei poterti abbracciare; ovunque tu sia, grazie. Lasciami testare ulteriormente, aspetta. – motiur

4

Beh, sembra che MiniMax sceglie correttamente per voi, basta chiamare con uno stato iniziale e una profondità. (A meno che il primo giocatore in base allo stato sia il secondo giocatore, dovresti chiamare min_move in MiniMax.)

MODIFICA: sì, ho trascurato qualcosa, bestMove al momento non ha molto senso. Nel programma entro max_move si modifica il ciclo come questo:

for(int i = 0 ; i < nMoves ; i++) 
{ 
    moveT move = moveList[i]; 
    MakeMove(state, move); 

    int new_value = min_move(state, depth+1); 
    if(new_value > v) 
    { 
     v=new_value; 
    } 
    RetractMove(state, move); 

} 

Dopo di che si può pensare a che cosa significa bestMove? La mia idea è che sei interessato a trovare una delle serie di mosse "migliori possibili" per tic-tac-toe. Per questo è necessario un vettore o anche meglio un stack. Ma ciò significa anche avere std::stack<int>* best_moves come ultimo parametro.

Per l'implementazione dello stack, in min_move si restituiscono le mosse successive e se il loro valore è il migliore, si sposterà il tuo move nella parte superiore dello stack best_moves. Ovviamente alla fine del gioco devi solo restituire la pila vuota. Ci vuole un approccio OOP per rimuoverlo correttamente, lo farò quando avrò un po 'di tempo.

Se tutto ciò che serve è solo la prossima mossa migliore allora ti suggerisco di cambiare il tipo di ritorno di min_move e max_moe a qualche struct in questo modo:

struct Value_move{ 
    int value; 
    moveT best_move; 
}; 

Poi la nuova implementazione di max_move si presenta come il seguente:

const int MOVE_INVALID = -12345; 
const int MOVE_NOTHING = -12346; 

Value_move max_move(stateT state, int depth) 
{ 
    Value_move best; 
    best.value = -10000; best.best_move = MOVE_INVALID; 

    if(GameIsOver(state)) 
    { 
     best.value = EvaluateStaticPosition(state); 
     best.best_move = MOVE_NOTHING; 
     return best; 
    } 

    vector<moveT> moveList; 
    GenerateMoveList(state, moveList); 
    int nMoves = moveList.size(); 

    for(int i = 0 ; i < nMoves ; i++) 
    { 
     moveT move = moveList[i]; 
     MakeMove(state, move); 
     Value_move curr = min_move(state, depth+1); 
     if(curr.value > best.value) 
     { 
      best.value = curr.value; 
      best.best_move = move; 
     } 
     RetractMove(state, move); 

    } 

    return v; 

} 

Hai solo bisogno di prendere il campo best_move nella struttura restituita nella funzione MiniMax.

NOTA:
Bisogna ammettere che questo non assomigli ad un programma C++ sotto molti aspetti, ma piuttosto ad un programma c. Altrimenti, tutte le funzioni in CapitalCamelCase dovrebbero essere metodi di classe, dovresti passare gli stati da (const) ref anziché value - ma questo intero codice ha senso solo se lo stato è realmente un puntatore dietro un typedef.

+2

... ma cout e vettori non hanno senso come codice C, solo C++. – Mike

+0

vero, corretto, mi dispiace, +1. Spero che siamo d'accordo su cattive pratiche per un programma C++ qui, però. Questo codice è da qualche parte nel mezzo. Ha anche iniziato a usare riferimenti in alcuni punti. :) –

+0

@BarnabasSzabolcs Sono preoccupato per curValue> v, è giustamente collocato nel ciclo for. curValue non è inizializzato, non è possibile che sia maggiore di v, il valore massimo ottenuto finora, diciamo +10. Come, posso cambiare il codice in modo che il 'curValue' rappresenti v = max (v, min_move (state, depth + 1, bestMove)) e anche un modo per memorizzare e confrontare il miglior valore ottenuto finora con curValue. Sono un po 'confuso qui. – motiur

0

Il codice trova il valore corretto ma poi lo sovrascrive passando lo stesso riferimento in basso.

int curValue = min_move(state,bestMove); 

dovrebbe diventare

moveT nextMove; // No need to actually do anything with this value 
int curValue = min_move(state,nextMove); 

È inoltre necessario fare lo stesso tipo di cambiamento nella funzione min_move.

NB: nel min_move il codice chiama max_move con più argomenti di quelli definiti per la funzione.