在Tic-Tac-Toe游戏中获得MiniMax算法的最佳移动

我正在尝试在用C编写的Tic-Tac-Toe游戏中实现基于Wikipedia
pseudocode的MiniMax算法.但是,我无法获得最佳移动.这是我的代码:

#include <stdio.h>
#include <stdbool.h>
#include <string.h>

// compile and run: gcc minimax.c -std=c99 && ./a.out

int max(int x, int y) {
  return x > y ? x : y;
}

int min(int x, int y) {
  return x < y ? x : y;
}

int whoWon(char ch) {
  switch (ch) {
    case 'O':
      return -1;
      break;
    case 'X':
      return 1;
      break;
  }
}

void printArray(char array[]) {
  printf("# START\n"
         "%c | %c | %c\n"
         "--|---|--\n"
         "%c | %c | %c\n"
         "--|---|--\n"
         "%c | %c | %c\n"
         "# END\n\n", array[0], array[1], array[2], array[3], array[4], array[5], array[6], array[7], array[8]);
}

int anyWinners(char board[])
{
  int i;

  /* check every row */
  for(i = 0; i < 7; i += 3)
    if(board[i] != ' ' && board[i] == board[i+1] && board[i] == board[i+2])
      return whoWon(board[i]);

  /* check every column */
  for(i = 0; i < 3; i++)
    if(board[i] != ' ' && board[i] == board[i+3] && board[i] == board[i+6])
      return whoWon(board[i]);

  /* check diagonals */
  if(board[4] != ' ' && ((board[0] == board[4] && board[0] == board[8]) || (board[2] == board[4] && board[2] == board[6])))
    return whoWon(board[4]);

  return 0;
}

int fullBoard(char board[]) {
  for (int i = 0; i < 9; ++i) {
    if (board[i] == ' ')
      return 0;
  }
  return 1;
}

int minimax(char node[], int depth, bool maximizingPlayer, int * move) {
  int terminalNode = anyWinners(node);
  if (depth == 0 || terminalNode || fullBoard(node)) {
    printf("################## END OF SUBTREE ##################\n");
    return terminalNode;
  }

  int bestValue, val;
  if (maximizingPlayer) {
    bestValue = -2;
    for (int i = 0; i < 9; ++i) {
      if (node[i] == ' ') {
        char child[9];
        strcpy(child, node);
        child[i] = 'X';

        // debug
        printArray(child);

        val = minimax(child, depth - 1, false, move);

        // debug
        printf("X: ^^ i = %d ^^ depth = %d ^^ val = %d\n", i, depth, val);

        //bestValue = max(bestValue, val);
        if (val > bestValue) {
          bestValue = val;
          if (depth == 9) *move = i;
        }
      }
    }
    return bestValue;
  } else {
    bestValue = 2;
    for (int i = 0; i < 9; ++i) {
      if (node[i] == ' ') {
        char child[9];
        strcpy(child, node);
        child[i] = 'O';

        // debug
        printArray(child);

        val = minimax(child, depth - 1, true, move);

        // debug
        printf("O: ^^ i = %d ^^ depth = %d ^^ val = %d\n", i, depth, val);

        bestValue = min(bestValue, val);
      }
    }
    return bestValue;
  }
}

int main() {
  int move = -999; // initialize only for debug

  // X will always win no matter what, first best move for X is 8
  // char board[] = {'O', ' ', ' ',
  //                 ' ', ' ', ' ',
  //                 'X', 'X', ' '};

  // best move for X is 3
  char board[] = {'O', 'O', ' ',
                  ' ', 'X', 'X',
                  ' ', ' ', ' '};

  // Initial call for maximizing player
  int result = minimax(board, 9, true, &move);
  printf("minimax returned: %d\n", result);
  printf("chosen move: %d\n", move);

  return 0;
}

代码使用所有变量的状态为每次移动打印板.主要还有两个失败的测试.现在算法返回坏动作,我找不到错误.

最佳答案 我看到两个问题:

>启发式是错误的
> strcpy有问题.

启发式是错误的

维基百科的伪代码说:

if depth = 0 or node is a terminal node
    return the heuristic value of node

您的实现是这样做的:

if depth = 0 or node is a terminal node
    return 1 if X wins, -1 if O wins, 0 if it is a draw

但这不是一个非常好的启发式方法.有了这种启发式方法,X可以赢得的所有可能方式都具有相同的权重.因此,如果X在3次移动中找到了获胜的方法,那么加权就像X在2次移动中找到获胜的方式一样,并且加权就像X在1次移动中找到获胜的方式一样.

那么,以下是您的测试用例中发生的情况:

> X尝试位置2.
> O尝试位置3.
> X尝试位置6.
>这是一个终端节点. X获胜.所以回归正1.

该决策路径的启发式= 1

它遇到的另一种可能性是:

> X尝试位置3.
>这是一个终端节点. X获胜.所以回归正1.

该决策路径的启发式= 1

由于这两种解决方案都具有相同的启发式算法,因此它们具有相同的价值.你可能认为这个解决方案不是最理想的,因为它需要太多的动作才能获胜.我建议根据到达这里所采取的动作数量进行启发式算法,乘以胜者是谁.因此,如果X在1次移动中获胜,则启发式为5000.如果X在2次移动中获胜,则启发式为2500.如果O在2次移动中获胜,则启发式为-2500.这样的事情.

strcpy有问题

这一行:

strcpy(child, node);

应该:

memcpy(child, node, 9*sizeof(char));

因为“node”不是以空字符结尾的字符串.当我在VS2013 / Windows 8.1上运行它时,我的输出是垃圾,因为.你的平台可能会很幸运.

点赞