【发布时间】:2020-12-09 09:46:52
【问题描述】:
注意 #1 - 有一个类似的问题,但那是在 Python 中,我无法在 C 中解决这个问题。
注意 #2 - 这是人类与 AI 的游戏,我将 AI 称为“cpu”。 cpu 符号为“O”,人类符号始终为“X”。
注意 #3 - 我想设计游戏,使 cpu 永远不会输(赢或平)。
我希望用户先选择 9 个方格中的任何一个。所以基本上我希望 cpu 蛮力计算游戏可能进行的每一种方式的结果,并基于此将“分数”分配给它可能的移动(剩下的 8 个选择)。这样它就会回溯并知道该走哪条路,以免迷路。
我也想使用 minimax 算法和 C 语言来做到这一点。
我的问题 - 我认为我缺少一个实际上利用返回的“分数”的函数。我不想要代码,但如果我能了解如何实现该功能,我将不胜感激。 nextMove() 函数也让我感到困惑,我希望它检查是否没有未填充的框,然后用“O”填充它,然后轮到玩家了。但由于它会在递归中再次被调用,它可能无法正常工作。有什么建议吗?
编辑 - 赏金用于指导我如何使用 minimax 算法实现此游戏的答案,如下所述:https://en.wikipedia.org/wiki/Minimax#Pseudocode
#include <stdio.h>
#define FALSE 0
#define TRUE 1
#define NEGINF -10000
#define POSINF +10000
struct node { // each node is like a snapshot of the game at that point in time
char board[9]; // the 3x3 square is implemented as an array of 9 chars
int value; // the value of that snapshot (+1 if cpu wins, -1 if cpu loses, 0 for draw)
int position; // 0 to 8 inclusive with 0 being [0][0] and 8 being [2][2] (row major fashion) and indicates position where the next 'X' or 'O' would be assigned
};
int max(int value, int returnedValue) {
return value > returnedValue ? value : returnedValue;
}
int min(int value, int returnedValue) {
return value < returnedValue ? value : returnedValue;
}
int someoneHasWon(struct node someNode) {
if(someNode.board[0] == someNode.board[3] && someNode.board[3] == someNode.board[6] && someNode.board[6] == 'X')
return TRUE;
if(someNode.board[0] == someNode.board[3] && someNode.board[3] == someNode.board[6] && someNode.board[6] == 'O')
return TRUE;
//someNode.value = -1;
if(someNode.board[1] == someNode.board[4] && someNode.board[4] == someNode.board[7] && someNode.board[7] == 'X')
return TRUE;
//someNode.value = 1;//return 1;
if(someNode.board[1] == someNode.board[4] && someNode.board[4] == someNode.board[7] && someNode.board[7] == 'O')
return TRUE;
//someNode.value = -1;//return -1;
if(someNode.board[2] == someNode.board[5] && someNode.board[5] == someNode.board[8] && someNode.board[8] == 'X')
return TRUE;
//someNode.value = 1;//return 1;
if(someNode.board[2] == someNode.board[5] && someNode.board[5] == someNode.board[8] && someNode.board[8] == 'O')
return TRUE;
//someNode.value = -1;//return -1;
if(someNode.board[0] == someNode.board[1] && someNode.board[1] == someNode.board[2] && someNode.board[2] == 'X')
return TRUE;
////someNode.value = 1;//return 1;
if(someNode.board[0] == someNode.board[1] && someNode.board[1] == someNode.board[2] && someNode.board[2] == 'O')
return TRUE;
//someNode.value = -1;//return -1;
if(someNode.board[3] == someNode.board[4] && someNode.board[4] == someNode.board[5] && someNode.board[5] == 'X')
return TRUE;
//someNode.value = 1;//return 1;
if(someNode.board[3] == someNode.board[4] && someNode.board[4] == someNode.board[5] && someNode.board[5] == 'O')
return TRUE;
//someNode.value = -1;//return -1;
if(someNode.board[6] == someNode.board[7] && someNode.board[7] == someNode.board[8] && someNode.board[8] == 'X')
return TRUE;
//someNode.value = 1;//return 1;
if(someNode.board[6] == someNode.board[7] && someNode.board[7] == someNode.board[8] && someNode.board[8] == 'O')
return TRUE;
//someNode.value = -1;//return -1;
if(someNode.board[0] == someNode.board[4] && someNode.board[4] == someNode.board[8] && someNode.board[8] == 'X')
return TRUE;
//someNode.value = 1;//return 1;
if(someNode.board[0] == someNode.board[4] && someNode.board[4] == someNode.board[8] && someNode.board[8] == 'O')
return TRUE;
//someNode.value = -1;//return -1;
if(someNode.board[6] == someNode.board[4] && someNode.board[4] == someNode.board[2] && someNode.board[2] == 'X')
return TRUE;
//someNode.value = 1;//return 1;
if(someNode.board[6] == someNode.board[4] && someNode.board[4] == someNode.board[2] && someNode.board[2] == 'O')
return TRUE;
return FALSE;
}
int isTerminal(struct node someNode) {
for(int i = 0; i < 9; i++)
if(someNode.board[i] == ' ') // if square left to fill then game is yet incomplete
return FALSE;
if(someoneHasWon(someNode) == TRUE) // if any player has won earlier with squares left
return TRUE;
return TRUE; // if it's a draw with no squares left to fill
}
struct node nextMove(struct node someNode) {
for(int i = 0; i < 9; i++)
if(someNode.board[i] == ' ') {
someNode.board[i] = 'O';
break;
}
return someNode;
}
int minimax(struct node someNode, int depth, int maximizingPlayer) {
if(depth == 0 || isTerminal(someNode) == TRUE) {
if(someNode.board[0] == someNode.board[3] && someNode.board[3] == someNode.board[6] && someNode.board[6] == 'X')
someNode.value = 1;
if(someNode.board[0] == someNode.board[3] && someNode.board[3] == someNode.board[6] && someNode.board[6] == 'O')
someNode.value = -1;
if(someNode.board[1] == someNode.board[4] && someNode.board[4] == someNode.board[7] && someNode.board[7] == 'X')
someNode.value = 1;//return 1;
if(someNode.board[1] == someNode.board[4] && someNode.board[4] == someNode.board[7] && someNode.board[7] == 'O')
someNode.value = -1;//return -1;
if(someNode.board[2] == someNode.board[5] && someNode.board[5] == someNode.board[8] && someNode.board[8] == 'X')
someNode.value = 1;//return 1;
if(someNode.board[2] == someNode.board[5] && someNode.board[5] == someNode.board[8] && someNode.board[8] == 'O')
someNode.value = -1;//return -1;
if(someNode.board[0] == someNode.board[1] && someNode.board[1] == someNode.board[2] && someNode.board[2] == 'X')
someNode.value = 1;//return 1;
if(someNode.board[0] == someNode.board[1] && someNode.board[1] == someNode.board[2] && someNode.board[2] == 'O')
someNode.value = -1;//return -1;
if(someNode.board[3] == someNode.board[4] && someNode.board[4] == someNode.board[5] && someNode.board[5] == 'X')
someNode.value = 1;//return 1;
if(someNode.board[3] == someNode.board[4] && someNode.board[4] == someNode.board[5] && someNode.board[5] == 'O')
someNode.value = -1;//return -1;
if(someNode.board[6] == someNode.board[7] && someNode.board[7] == someNode.board[8] && someNode.board[8] == 'X')
someNode.value = 1;//return 1;
if(someNode.board[6] == someNode.board[7] && someNode.board[7] == someNode.board[8] && someNode.board[8] == 'O')
someNode.value = -1;//return -1;
if(someNode.board[0] == someNode.board[4] && someNode.board[4] == someNode.board[8] && someNode.board[8] == 'X')
someNode.value = 1;//return 1;
if(someNode.board[0] == someNode.board[4] && someNode.board[4] == someNode.board[8] && someNode.board[8] == 'O')
someNode.value = -1;//return -1;
if(someNode.board[6] == someNode.board[4] && someNode.board[4] == someNode.board[2] && someNode.board[2] == 'X')
someNode.value = 1;//return 1;
if(someNode.board[6] == someNode.board[4] && someNode.board[4] == someNode.board[2] && someNode.board[2] == 'O')
someNode.value = -1;//return -1;
someNode.value = 0;//return 0;
}
if(maximizingPlayer == TRUE) { //maximizing player is cpu i.e. 'O'
someNode.value = NEGINF;
while(isTerminal(someNode) != TRUE)
someNode.value = max(someNode.value, minimax(nextMove(someNode), depth - 1, FALSE));
return someNode.value;
}
else { //minimizing player is me i.e. 'X'
int boxNumber = 0;
scanf("%d", &boxNumber);
someNode.position = boxNumber;
someNode.board[someNode.position] = 'X';
someNode.value = POSINF;
while(isTerminal(someNode) != TRUE)
someNode.value = min(someNode.value, minimax(nextMove(someNode), depth - 1, TRUE));
return someNode.value;
}
}
int main() {
int boxNumber = 0;
printf("Assume you're X and cpu is O \nInput any box number you like \n(0 to 8 both inclusive) \n...you'll be defeated anyways lol :\n");
scanf("%d", &boxNumber);
struct node origin;
for(int i = 0; i < 9; i++)
origin.board[i] = ' ';
origin.position = boxNumber;
origin.board[origin.position] = 'X';
origin.value = 0;
minimax(origin, 8, TRUE);
}
代码版本 #2 -
#include <stdio.h>
#define FALSE 0
#define TRUE 1
#define NEGINF -10000
#define POSINF +10000
struct node
{
char board[9];
int value;
int position;
};
//char someNode.board[9] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
int max(int value, int returnedValue)
{
return value > returnedValue ? value : returnedValue;
}
int min(int value, int returnedValue)
{
return value < returnedValue ? value : returnedValue;
}
int someoneHasWon(struct node someNode)
{
if (someNode.board[0] == someNode.board[3] && someNode.board[3] == someNode.board[6] && someNode.board[6] == 'x')
return TRUE;
if (someNode.board[0] == someNode.board[3] && someNode.board[3] == someNode.board[6] && someNode.board[6] == 'o')
return TRUE;
//someNode.value = -1;
if (someNode.board[1] == someNode.board[4] && someNode.board[4] == someNode.board[7] && someNode.board[7] == 'x')
return TRUE;
//someNode.value = 1;//return 1;
if (someNode.board[1] == someNode.board[4] && someNode.board[4] == someNode.board[7] && someNode.board[7] == 'o')
return TRUE;
//someNode.value = -1;//return -1;
if (someNode.board[2] == someNode.board[5] && someNode.board[5] == someNode.board[8] && someNode.board[8] == 'x')
return TRUE;
//someNode.value = 1;//return 1;
if (someNode.board[2] == someNode.board[5] && someNode.board[5] == someNode.board[8] && someNode.board[8] == 'o')
return TRUE;
//someNode.value = -1;//return -1;
if (someNode.board[0] == someNode.board[1] && someNode.board[1] == someNode.board[2] && someNode.board[2] == 'x')
return TRUE;
////someNode.value = 1;//return 1;
if (someNode.board[0] == someNode.board[1] && someNode.board[1] == someNode.board[2] && someNode.board[2] == 'o')
return TRUE;
//someNode.value = -1;//return -1;
if (someNode.board[3] == someNode.board[4] && someNode.board[4] == someNode.board[5] && someNode.board[5] == 'x')
return TRUE;
//someNode.value = 1;//return 1;
if (someNode.board[3] == someNode.board[4] && someNode.board[4] == someNode.board[5] && someNode.board[5] == 'o')
return TRUE;
//someNode.value = -1;//return -1;
if (someNode.board[6] == someNode.board[7] && someNode.board[7] == someNode.board[8] && someNode.board[8] == 'x')
return TRUE;
//someNode.value = 1;//return 1;
if (someNode.board[6] == someNode.board[7] && someNode.board[7] == someNode.board[8] && someNode.board[8] == 'o')
return TRUE;
//someNode.value = -1;//return -1;
if (someNode.board[0] == someNode.board[4] && someNode.board[4] == someNode.board[8] && someNode.board[8] == 'x')
return TRUE;
//someNode.value = 1;//return 1;
if (someNode.board[0] == someNode.board[4] && someNode.board[4] == someNode.board[8] && someNode.board[8] == 'o')
return TRUE;
//someNode.value = -1;//return -1;
if (someNode.board[6] == someNode.board[4] && someNode.board[4] == someNode.board[2] && someNode.board[2] == 'x')
return TRUE;
//someNode.value = 1;//return 1;
if (someNode.board[6] == someNode.board[4] && someNode.board[4]
== someNode.board[2] && someNode.board[2] == 'o')
return TRUE;
return FALSE;
}
int isTerminal(struct node someNode)
{
for (int i = 0; i < 9; i++)
if (someNode.board[i] == ' ') // if square left to fill then game is yet incomplete
return FALSE;
if (someoneHasWon(someNode) == TRUE) // if any player has won earlier with squares left
return TRUE;
return TRUE; // if it's a draw with no squares left to fill
}
struct node nextMove(struct node someNode)
{
for (int i = 0; i < 9; i++)
if (someNode.board[i] == ' ')
{
someNode.board[i] = 'o';
break;
}
return someNode;
}
int minimax(struct node someNode, int depth, int maximizingPlayer)
{
if (depth == 8 || isTerminal(someNode) == TRUE)
{
if (someNode.board[0] == someNode.board[3] && someNode.board[3] == someNode.board[6] && someNode.board[6] == 'x')
someNode.value = 1;
if (someNode.board[0] == someNode.board[3] && someNode.board[3] == someNode.board[6] && someNode.board[6] == 'o')
someNode.value = -1;
if (someNode.board[1] == someNode.board[4] && someNode.board[4] == someNode.board[7] && someNode.board[7] == 'x')
someNode.value = 1; //return 1;
if (someNode.board[1] == someNode.board[4] && someNode.board[4] == someNode.board[7] && someNode.board[7] == 'o')
someNode.value = -1; //return -1;
if (someNode.board[2] == someNode.board[5] && someNode.board[5] == someNode.board[8] && someNode.board[8] == 'x')
someNode.value = 1; //return 1;
if (someNode.board[2] == someNode.board[5] && someNode.board[5] == someNode.board[8] && someNode.board[8] == 'o')
someNode.value = -1; //return -1;
if (someNode.board[0] == someNode.board[1] && someNode.board[1] == someNode.board[2] && someNode.board[2] == 'x')
someNode.value = 1; //return 1;
if (someNode.board[0] == someNode.board[1] && someNode.board[1] == someNode.board[2] && someNode.board[2] == 'o')
someNode.value = -1; //return -1;
if (someNode.board[3] == someNode.board[4] && someNode.board[4] == someNode.board[5] && someNode.board[5] == 'x')
someNode.value = 1; //return 1;
if (someNode.board[3] == someNode.board[4] && someNode.board[4] == someNode.board[5] && someNode.board[5] == 'o')
someNode.value = -1; //return -1;
if (someNode.board[6] == someNode.board[7] && someNode.board[7] == someNode.board[8] && someNode.board[8] == 'x')
someNode.value = 1; //return 1;
if (someNode.board[6] == someNode.board[7] && someNode.board[7] == someNode.board[8] && someNode.board[8] == 'o')
someNode.value = -1; //return -1;
if (someNode.board[0] == someNode.board[4] && someNode.board[4] == someNode.board[8] && someNode.board[8] == 'x')
someNode.value = 1; //return 1;
if (someNode.board[0] == someNode.board[4] && someNode.board[4] == someNode.board[8] && someNode.board[8] == 'o')
someNode.value = -1; //return -1;
if (someNode.board[6] == someNode.board[4] && someNode.board[4] == someNode.board[2] && someNode.board[2] == 'x')
someNode.value = 1; //return 1;
if (someNode.board[6] == someNode.board[4] && someNode.board[4] == someNode.board[2] && someNode.board[2] == 'o')
someNode.value = -1; //return -1;
someNode.value = 0; //return 0;
}
if (maximizingPlayer == TRUE)
{ //maximizing player is cpu i.e. 'o'
someNode.value = NEGINF;
while (isTerminal(someNode) != TRUE)
someNode.value = max(someNode.value, minimax(nextMove(someNode), depth + 1, FALSE));
if (someNode.value == -1) {
printf("o %d \n", someNode.value);
return someNode.value;
}
}
else
{ //minimizing player is me i.e. 'x'
int boxNumber = 0;
printf("Enter your move :\n");
scanf("%d", &boxNumber);
someNode.position = boxNumber;
someNode.board[someNode.position] = 'x';
someNode.value = POSINF;
while (isTerminal(someNode) != TRUE)
someNode.value = min(someNode.value, minimax(nextMove(someNode), depth + 1, TRUE));
if (someNode.value == 1)
return someNode.value;
}
}
int main()
{
int boxNumber = 0;
printf("Assume you're x and cpu is O \nInput any box number you like \n(0 to 8 both inclusive) \n...you'll be defeated anyways lol :\n");
scanf("%d", &boxNumber);
struct node origin;
origin.position = boxNumber;
origin.board[origin.position] = 'x';
origin.value = 0;
printf("%c %d \n", origin.board[origin.position], origin.position);
int val = minimax(origin, 1, TRUE);
if(val > 0)
printf("You lose");
else if(val < 0)
printf("You win");
else
printf("It's a draw");
return 0;
}
【问题讨论】:
-
你想以特定的方式实现(你确实描述了很多关于你的想法和你想要的东西)还是你想要程序让人工智能永远不会失败?我问是因为我怀疑你没有得到答案(尽管有赏金),因为可能存在冲突。 IE。是什么让您认为按照您描述的方式制作永不丢失的 AI 实际上是可能的?考虑写一些类似“我将奖励实现永不丢失的人工智能的答案;最接近我打破关系的方法的答案。”或“根据我的方法获得最佳(失败)人工智能的答案。”
-
是的,我希望 AI 永远不会输(赢或平)。另外,我这样做是为了自学 Minimax,如下所述:en.wikipedia.org/wiki/Minimax#Pseudocode
标签: c tic-tac-toe minimax