Note #1 - There is a similar question but that is in Python, and I can't figure out this in C.
Note #2 - This is a human vs. AI game and I will call the AI as 'cpu'. The cpu symbol is 'O' and human symbol is 'X' always.
Note #3 - I want to design the game so that the cpu never loses (either win or draw).
I want the user to go first and choose any of the 9 squares. So basically I want the cpu to brute force calculate the result of every way the game can go, and based on that assign 'scores' to its possible moves (8 remaining choices). This way it backtracks and knows which way to go so as to not lose.
Also I want to do that using the minimax algorithm, and in C.
My problem - What I think is that I'm missing a function that actually makes use of the returned 'scores'. I do not want the code, but I would appreciate it if I could get an idea of how to implement that function. Also the nextMove() function confuses me, I want it to check if there is no unfilled box, then fill it with 'O' and then it's the player's turn. But since it will be called again in the recursion, it might not work correctly. Any suggestions?
Edit - The bounty goes to the answer that guides me on how to implement this game using minimax algorithm as described here: 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);
}
Code version #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;
}