Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>In the simplest version of minimax, the first player wishes to maximize his score, and the second player wishes to minimize the first player's score. Since both first and second player only care about the first player's score, <code>EvaluateStaticPosition</code> should return a value indicating how good the board state is for the first player. Whose turn it is is not relevant.</p> <pre><code>int EvaluateStaticPosition(stateT state) { if(CheckForWin(state, FIRST_PLAYER)) { return WINNING_POSITION; } if(CheckForWin(state, Opponent(FIRST_PLAYER))) { return LOSING_POSITION; } return NEUTRAL_POSITION; } </code></pre> <p>Now, when you want the move that's best for the first player, call MaxMove. When you want the move that's best for the second player, call MinMove.</p> <pre><code>moveT MiniMax(stateT state) { moveT bestMove; int i = 0; if (state.whoseTurn == FIRST_PLAYER){ i = MaxMove(state, bestMove); } else{ i = MinMove(state,bestMove); } cout&lt;&lt;"i is "&lt;&lt;i&lt;&lt;endl; return bestMove; } </code></pre> <p>Finally, you have some problems inside of <code>MinMove</code> and <code>MaxMove</code>. when you assign <code>curRating</code> in either one, you shouldn't pass in <code>bestMove</code> as the second argument to <code>MaxMove</code> or <code>MinMove</code>. It will then put the <em>opponent's</em> best move into <code>bestMove</code>, which doesn't make sense. Instead, declare an <code>opponentsBestMove</code> object and pass that as the second argument. (You won't actually be using the object or even looking at its value afterwards, but that's ok). With that change, you never assign anything to <code>bestMove</code> within <code>MinMove</code>, so you should do so inside the <code>if(curRating &lt; v)</code> block.</p> <pre><code>int MaxMove(stateT state, moveT &amp;bestMove) { if(GameIsOver(state)) { return EvaluateStaticPosition(state); } vector&lt;moveT&gt; moveList; GenerateMoveList(state, moveList); int nMoves = moveList.size(); int v = -1000; for(int i = 0 ;i&lt;nMoves; i++) { moveT move = moveList[i]; MakeMove(state, move); moveT opponentsBestMove; int curRating = MinMove(state, opponentsBestMove); if (curRating &gt; v) { v = curRating; bestMove = move; } RetractMove(state, move); } return v; } int MinMove(stateT state, moveT &amp;bestMove) { if(GameIsOver(state)) { return EvaluateStaticPosition(state); } vector&lt;moveT&gt;moveList; GenerateMoveList(state, moveList); int nMoves = moveList.size(); int v = 1000; for(int i = 0 ; i&lt;nMoves; i++) { moveT move = moveList[i]; MakeMove(state , move); moveT opponentsBestMove; int curRating = MaxMove(state,opponentsBestMove); if(curRating &lt; v) { v = curRating; bestMove = move; } RetractMove(state, move); } return v; } </code></pre> <p>At this point you should have an unbeatable AI!</p> <pre><code>The final position looks like this: O | O | X ---+---+--- X | X | O ---+---+--- O | X | X Cat's game. </code></pre> <hr> <p><strong>An alternative method</strong> takes advantage of the fact that tic-tac-toe is a zero-sum game. In other words, at the end of the game, the sum of the scores of the players will equal zero. For a two player game, this means that one player's score will always be the negative of the other player's. This is convenient for us, since minimizing the other player's score is then identical to maximizing one's own score. So instead of one player maximizing his score and one player minimizing the other player's score, we can just have both players attempt to maximize their own score.</p> <p>Change <code>EvaluateStaticPosition</code> back to its original form, so that it gives a score based on how good the board state is for the current player.</p> <pre><code>int EvaluateStaticPosition(stateT state) { if(CheckForWin(state, state.whoseTurn)) { return WINNING_POSITION; } if(CheckForWin(state, Opponent(state.whoseTurn))) { return LOSING_POSITION; } return NEUTRAL_POSITION; } </code></pre> <p>Delete <code>MinMove</code>, since we only care about maximizing. Rewrite <code>MaxMove</code> so that it chooses the move that gives the opponent the worst possible score. The score for the best move is the negative of the other player's worst score.</p> <pre><code>int MaxMove(stateT state, moveT &amp;bestMove) { if(GameIsOver(state)) { return EvaluateStaticPosition(state); } vector&lt;moveT&gt; moveList; GenerateMoveList(state, moveList); int nMoves = moveList.size(); int v = -1000; for(int i = 0 ;i&lt;nMoves; i++) { moveT move = moveList[i]; MakeMove(state, move); moveT opponentsBestMove; int curRating = -MaxMove(state, opponentsBestMove); if (curRating &gt; v) { v = curRating; bestMove = move; } RetractMove(state, move); } return v; } </code></pre> <p>Since <code>MaxMove</code> is used for both players, we no longer need to distinguish among players in the <code>MiniMax</code> function.</p> <pre><code>moveT MiniMax(stateT state) { moveT bestMove; int i = 0; i = MaxMove(state, bestMove); cout&lt;&lt;"i is "&lt;&lt;i&lt;&lt;endl; return bestMove; } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload