/** * Recursive MiniMax algorithm that computes the best move at the current call's * move depth, based on the player. * @param board A pointer to a representation of the board to compute scores * on. Note that this may be different than the actual current * board. * @param player Whether to evaluate the board as the player or the opponent. * @return The MAX score if player, or MIN score if Opponent. */ minimax_score_t minimax(minimax_board_t* board, bool player) { // Increment depth at the beginning of this function call depth++; // Create 2 arrays (indexed the same) to save moves and their score // at this level of recursion. minimax_move_t moves[MINIMAX_TOTALSQUARES]; minimax_score_t scores[MINIMAX_TOTALSQUARES]; // Recursion base case, there has been a win or a draw. if (minimax_isGameOver(minimax_computeBoardScore(board))) { depth--; // decrement depth as we return; // Since this call is the end case, simply return the board's score return minimax_computeBoardScore(board); } // Otherwise, the recursion is called. // This while-loop will generate all possible boards. int16_t index = 0; // used to track the number of score/move pairs. int row, col; for (row = 0; row < MINIMAX_BOARD_ROWS; row++) { // For each row for (col = 0; col < MINIMAX_BOARD_ROWS; col++) { // For each column // Find an empty square if ((*board).squares[row][col] == MINIMAX_EMPTY_SQUARE) { // Assign the square to the player or opponent. if (player) { (*board).squares[row][col] = MINIMAX_PLAYER_SQUARE; } else { (*board).squares[row][col] = MINIMAX_OPPONENT_SQUARE; } // Make the recursive call that determines the MIN or MAX score // based on the player to save to the table. minimax_score_t score = minimax(board, !player); scores[index] = score; // Add score to the score table moves[index].row = row; // Add move row to the move table moves[index].column = col; // Add move column to the move table index++; // Increment the index into the moves/scores table // Undo the change to the board (return the square to empty) // prior to next iteration of for-loops. (*board).squares[row][col] = MINIMAX_EMPTY_SQUARE; } } } #ifdef MINIMAX_DEBUG // Print out Score Matrix at Top Level if DEBUG mode is enabled. // This is useful in visually checking that the score table is correct // after all of the recursive calls have been made. if (depth == 0) { printf("Scores:\n\r"); int count; for (count = 0; count < index; count++) { printf("Move (%d, %d) Score: %d\n", moves[count].row, moves[count].column, scores[count]); } } #endif // Once here, we have iterated over empty squares at this level. // All of the scores and moves have been computed at this level. minimax_score_t score = 0; // the value of the score to return int16_t tempIndex = 0; // variable used to store the index of the MIN/MAX // Now we return the score based upon whether we are computing min or max. int i; if (player) { // If the player is X, we want to find the MAX score int16_t highScore = scores[0]; // Initialize to the first score. // Iterate over all of the move/score pairs in the table for (i = 0; i < index; i++) { if (scores[i] > highScore) { // Raise the maximum highScore found so far highScore = scores[i]; tempIndex = i; } } choice = moves[tempIndex]; // Get the choice with the MAX score score = scores[tempIndex]; // Return the highest score in the table. } else { // If we are looking at 'O', we want to find the MIN score int16_t lowScore = scores[0]; // Initialize to the first score. // Iterate over all of the move/score pairs in the table for (i = 0; i < index; i++) { if (scores[i] < lowScore) { // Lower the minimum lowScore found so far lowScore = scores[i]; tempIndex = i; } } choice = moves[tempIndex]; // Get the choice with the MIN score score = scores[tempIndex]; // Return the lowest score in the table. } // Done with the recursive call, decrement depth as we return depth--; return score; }
//checks if game is over bool ticTacToeControl_checkEndGame(minimax_board_t *board, bool player) { int16_t score = minimax_computeBoardScore(board, player); //get board score return minimax_isGameOver(score); //return if game is over }