3
\$\begingroup\$

I'm beginner on doing programming and just started learning C language. Then, i decide to make Tic-Tac-Toe as my first program.

My Tic-Tac-Toe summary: User can choose whether want to play 2 player or against computer. For computer player, i use Minimax Algorithm so it's not easy for user to win, whether they choose to be X or O. ​ ​

Can you guys review my code? Is it good and clean enough or maybe there is something i can do better? Thank you!

#include <ctype.h>
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef struct
{
    char name[30];
    char symbol;
}
player;

// GRID TOTAL BOX
const int N = 3;

char board[3][3] = {
    {'1', '2', '3'},
    {'4', '5', '6'},
    {'7', '8', '9'}
};

player players[2];

char COMPUTER_AI;

int numOfplayer, winner;

void delay(int seconds);
void information();
void draw_board();
void draw_line();
void two_player_gameplay();
int check_win();
int check_minimax_win();
void show_the_winner(int result);
void human_move();
void computer_move();
int minimax(int depth, bool isMaximizing);

int main(void)
{
    printf("Welcome to Tic-Tac-Toe game!\n\n");

    information(); 
    
    if (numOfplayer == 2)
    { 
        two_player_gameplay();
    }
    else
    {
        // IF user choose to be X, move first
        if (players[0].symbol == 'X')
        {
            human_move();
        }
        else
        {
            computer_move();
        }
    }

    return 0;
}

// FUNCTION TO HANDLE HUMAN MOVE
void human_move()
{
    int choice, found, result;
    char mark, char_choice;

    draw_board();

    // Keep prompt user get the valid board number
    do
    {
        printf("\t\t\t\t");
        printf("%s : ", players[0].name);
        scanf("%i", &choice);
    
        // Get player symbol
        mark = players[0].symbol;
            
        // Always reset found to 0
        found = 0;

        for (int row = 0; row < N; row++)
        {
            for (int col = 0; col < N; col++)
            {
                // Convert user choice from int to char to compare with board array
                char_choice = choice + '0';
                    
                // Check if number user choose is still exist in board, then it's legal to change it with user symbol
                if (board[row][col] == char_choice)
                {
                    // After change it, increment found if the board valid
                    board[row][col] = mark;
                    found++;
                    
                    // Break the loop
                    row = col = N;
                    break;
                }
            }
        }
    
    }
    while ((choice < 1 || choice > 9) || found == 0);
    
    // Check if there's a winner
    // IF It's 2, game still running, computer turn to move
    // Otherwise, call show the winner function
    result = check_win();
    if (result == 2)
    {
        computer_move();
    }
    else
    {
        show_the_winner(result);
    }
}

// FUNCTION TO HANDEL COMPUTER MOVE
void computer_move()
{
    char board_number;
    int move_i, move_j;

    // Set best score to minus INFINITY
    float bestScore = -INFINITY;

    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            // CHECK IF BOARD IS EMPTY
            if (board[i][j] != 'X' && board[i][j] != 'O')
            {
                // Get the real number on board
                board_number = board[i][j];

                // Change the board temporary
                // So we can call minimax to calculate it
                board[i][j] = COMPUTER_AI;    
                float score = minimax(0, false);

                // Reset to default board number
                board[i][j] = board_number;

                // AS maximizing player, IF score GREATER Than best score,
                // Change the best score, store current board position to be placed
                if (score > bestScore)
                {
                    bestScore = score;
                    move_i = i;
                    move_j = j;
                }
            }           
        }
    }
    // Placed the best position
    board[move_i][move_j] = COMPUTER_AI;
    
    // Check win, if it's still running, human turn to move
    int result = check_win();
    if (result == 2)
    {
        human_move();
    }
    else
    {
        show_the_winner(result);
    }
}

// MINIMAX FUNCTION
int minimax(int depth, bool isMaximizing)
{
    // Base case
    // Return static evaluation after get the winner
    int result = check_minimax_win();
    if (result != 2)
    {
        return result;
    }

    // IF Maximizing player turn to analyze
    if (isMaximizing)
    {
        // SET best score to minus infinity
        float bestScore = -INFINITY;

        // ITERATE THROUGH BOARD
        for (int row = 0; row < N; row++)
        {
            for (int col = 0; col < N; col++)
            {
                // Check if the positon in board is empty
                if (board[row][col] != 'X' && board[row][col] != 'O')
                {
                    // Get the real number on board
                    char board_number = board[row][col];

                    // Change the current position in board as MAXIMIZING (COMPUTER) symbol
                    // Call minimax recursively, minimizing turn
                    board[row][col] = COMPUTER_AI;
                    float score = minimax(depth + 1, false);

                    // Reset to default board number
                    board[row][col] = board_number;
                    
                    // AS maximizing player, find the MAX score, store into best score
                    if (score > bestScore)
                    {
                        bestScore = score;
                    }
                }
            }
        }
        return bestScore;
    }
    // OTHERWISE, MINIMIZING TURN to analyze
    else
    {
        // SET best score to +INFINITY
        float bestScore = INFINITY;

        for (int row = 0; row < N; row++)
        {
            for (int col = 0; col < N; col++)
            {
                if (board[row][col] != 'X' && board[row][col] != 'O')
                {
                    char board_number = board[row][col];

                    // Change the current position in board as MAXIMIZING (COMPUTER) symbol, temporary
                    // Call minimax recursively, maximizing turn
                    board[row][col] = players[0].symbol;
                    float score = minimax(depth + 1, true);
                    
                    // Reset to default board number
                    board[row][col] = board_number;

                    // AS Minimizing, get the Minimum score, store it to best score
                    if (score < bestScore)
                    {
                        bestScore = score;
                    }
                }
            }
        }
        return bestScore;
    }
}

// TWO PLAYER GAMEPLAY
void two_player_gameplay() 
{
    int player = 0;
    int choice, found, result;
    char mark, char_choice;
 
    do 
    {
        draw_board();

        // PROMPT current player to move
        printf("\t\t\t\t");
        printf("%s : ", players[player % 2].name);
        scanf("%i", &choice);

        // Get player symbol
        mark = players[player % 2].symbol;

        // Iterate through board to check and replace the board with user symbol
        for (int row = 0; row < N; row++) 
        {
            for (int col = 0; col < N; col++)
            {
                // Convert user choice from int to char to compare with board array
                char_choice = choice + '0';
                
                // Check if number user choose is still exist in board, then it's legal to change it with user symbol
                if (board[row][col] == char_choice)
                {
                    // After change it, increment player to the next
                    board[row][col] = mark;
                    player++;

                    // Break the loop
                    row = col = 3;
                    break;
                }
            }
        }

        // Call function to check if there's already winner
        result = check_win();
    } 
    while ((choice < 1 || choice > 9) || result == 2);

    draw_board();

    show_the_winner(result);
}

// FUNCTION TO ASK USER BEFORE STARTING GAME
void information()
{
    // KEEP prompt user until enter valid number of player
    do
    {
        printf("How many player will play (MAX is 2): ");
        scanf("%i", &numOfplayer);
    }
    while (numOfplayer > 2);
    printf("\n");

    // If there's only one player
    if(numOfplayer < 2)
    {
        printf("Enter your name: ");
        scanf("%s", players[0].name);
        getchar();

        // Keep prompt user until enter valid symbol
        do
        {
            printf("X or O: ");
            scanf("%c", &players[0].symbol);
            getchar();
        }
        while (players[0].symbol != 'X' && players[0].symbol != 'O');    

        // Assign symbol to user and computer
        players[0].symbol = toupper(players[0].symbol);
        COMPUTER_AI = (players[0].symbol == 'X' ? 'O' : 'X');
    }
    // Otherwise, two player
    else
    {
        for (int i = 0; i < 2; i++)
        {
            // Get user name
            printf("Enter Player %i name: ", i + 1);
            scanf("%s", players[i].name);
            getchar();
            
            printf("\n");
        
        }

        // First player get X symbol
        // Second player get O symbol    
        players[0].symbol = toupper('X');
        players[1].symbol = ((toupper(players[0].symbol) == 'X') ? 'O' : 'X');
    }

    printf("\n\n");
}

// FUNCTION TO CHECK IF THERE IS WINNER 
/*
    Return 1 means X is winning.
    Return -1 means O is winning.
    Return 0 means game is draw.
    Return 2 means game still running.
*/
int check_win()
{
    for (int i = 0; i < N; i++)
    {
        for (int j = 1; j <= 1; j++)
        {
            // CHECK THREE in a row on HORIZONTAL Board
            if (board[i][j - 1] == board[i][j] && board[i][j] == board[i][j + 1])
            {
                if (board[i][j] == 'X')
                {
                    return 1;
                }
                else
                {
                    return -1;
                }
            }
            // CHECK THREE in a row on VERTICAL Board
            else if (board[j - 1][i] == board[j][i] && board[j][i] == board[j + 1][i])
            {
                if (board[j][i] == 'X')
                {
                    return 1;
                }
                else
                {
                    return -1;
                }
            }
            // CHECK THREE in a row on SLASH Board || 1 - 5- 9 or 3 - 5 - 7
            else if (board[j - 1][j - 1] == board[j][j] && board[j][j] == board[j + 1][j + 1])
            {
                if (board[j][j] == 'X')
                {
                    return 1;
                }
                else
                {
                    return -1;
                }
            }
            else if (board[j - 1][j + 1] == board[j][j] && board[j][j] == board[j + 1][j - 1])
            {
                if (board[j][j] == 'X')
                {
                    return 1;
                }
                else
                {
                    return -1;
                }
            }
            // CHECK IF BOARD ALREADY FULL, AND NO ONES WIN (DRAW)
            else if (board[j - 1][j - 1] != '1' && board[j - 1][j] != '2' && board[j - 1][j + 1] != '3' && 
                     board[j][j - 1] != '4' && board[j][j] != '5' && board[j][j + 1] != '6' &&
                     board[j + 1][j - 1] != '7' && board[j + 1][j] != '8' && board[j + 1][j + 1] != '9')
                     {
                         return 0;
                     }
        }
    }
    // IF there's no three symbol in a row yet, game still running
    return 2;
}

// FUNCTION TO CHECK THE WINNER || ONLY SPECIFY FOR MINIMAX FUNCTION
// HANDLE DIFFERENT RETURN BETWEEN X and O
/*
    When user decide to play as X: O is computer as maximizing player
    So, it always return 1 for best score
    When user decide to play as O: X is computer as maximizing player
    So, it ALSO always return 1 for best score

    -1 is ONLY score for minimizing player: Human.
*/ 
int check_minimax_win()
{
    for (int i = 0; i < N; i++)
    {
        for (int j = 1; j <= 1; j++)
        {
            // CHECK THREE in a row on HORIZONTAL Board
            if (board[i][j - 1] == board[i][j] && board[i][j] == board[i][j + 1])
            {
                // IF user decide to play as O
                if (players[0].symbol == 'O')
                {   
                    // Computer as maximizing player, X return 1 as best score
                    // O return -1 as minimizing score 
                    if (board[i][j] == 'X')
                    {
                        return 1;
                    }
                    else
                    {
                        return -1;
                    }
                }

                // IF user decide to play as X
                if (players[0].symbol == 'X')
                {
                    // REVERSE THE RETURN VALUE (NOT CONDITION)
                    // Computer as maximizing player, X return -1 as minimizing score
                    // O return 1 as maximizing score 
                    if (board[i][j] == 'X')
                    {
                        // X as minimizing
                        return -1;
                    }
                    else
                    {
                        // O as maximizing
                        return 1;
                    }
                }    
            }
            // CHECK THREE in a row on VERTICAL Board
            else if (board[j - 1][i] == board[j][i] && board[j][i] == board[j + 1][i])
            {
                if (players[0].symbol == 'O')
                {
                    // Computer as maximizing player, X return 1 as best score
                    // O return -1 as minimizing score 
                    if (board[j][i] == 'X')
                    {
                        return 1;
                    }
                    else
                    {
                        return -1;
                    }
                }

                if (players[0].symbol == 'X')
                {
                    // REVERSE THE RETURN VALUE (NOT CONDITION)
                    // Computer as maximizing player, X return -1 as minimizing score
                    // O return 1 as maximizing score
                    if (board[j][i] == 'X')
                    {
                        return -1;
                    }
                    else
                    {
                        return 1;
                    }
                }    
            }
            // CHECK THREE in a row on SLASH Board || 1 - 5- 9 or 3 - 5 - 7
            else if (board[j - 1][j - 1] == board[j][j] && board[j][j] == board[j + 1][j + 1])
            {
                if (players[0].symbol == 'O')
                {
                    // Computer as maximizing player, X return 1 as best score
                    // O return -1 as minimizing score 
                    if (board[j][j] == 'X')
                    {
                        return 1;
                    }
                    else
                    {
                        return -1;
                    }
                }

                if (players[0].symbol == 'X')
                {
                    // REVERSE THE RETURN VALUE (NOT CONDITION)
                    // Computer as maximizing player, X return -1 as minimizing score
                    // O return 1 as maximizing score
                    if (board[j][j] == 'X')
                    {
                        return -1;
                    }
                    else
                    {
                        return 1;
                    }
                }
            }
            else if (board[j - 1][j + 1] == board[j][j] && board[j][j] == board[j + 1][j - 1])
            {
                if (players[0].symbol == 'O')
                {
                    // Computer as maximizing player, X return 1 as best score
                    // O return -1 as minimizing score
                    if (board[j][j] == 'X')
                    {
                        return 1;
                    }
                    else
                    {
                        return -1;
                    }
                }

                if (players[0].symbol == 'X')
                {
                    // REVERSE THE RETURN VALUE (NOT CONDITION)
                    // Computer as maximizing player, X return -1 as minimizing score
                    // O return 1 as maximizing score
                    if (board[j][j] == 'X')
                    {
                        return -1;
                    }
                    else
                    {
                        return 1;
                    }
                }    
            }
            // CHECK IF BOARD ALREADY FULL, AND NO ONES WIN (DRAW)
            else if (board[j - 1][j - 1] != '1' && board[j - 1][j] != '2' && board[j - 1][j + 1] != '3' && 
                     board[j][j - 1] != '4' && board[j][j] != '5' && board[j][j + 1] != '6' &&
                     board[j + 1][j - 1] != '7' && board[j + 1][j] != '8' && board[j + 1][j + 1] != '9')
                     {
                         return 0;
                     }
        }
    }
    // IF there's no three symbol in a row yet, game still running
    return 2;
}

// FUNCTION TO SHOW THE WINNER
void show_the_winner(int result)
{
    // AS one player only
    if (numOfplayer < 2)
    {
        // CHECK IF the user symbol match with the result
        if ((players[0].symbol == 'X' && result == 1) || (players[0].symbol == 'O' && result == -1))
        {
            draw_board();
            printf("\n\t\t\t\t\t    ---THE WINNER IS %s---\n\n\n", players[0].name);
        }
        // MAKE SURE COMPUTER PLAY AS X(1) OR O (-1)
        else if (result == -1 || result == 1)
        {
            draw_board();
            printf("\n\t\t\t\t\t\t ---YOU LOSE---\n\n\n");
        }
        // TIES
        else 
        {
            draw_board();
            printf("\n\t\t\t\t\t     ---THE GAME IS DRAW---\n\n\n");
        }
    }
    // TWO PLAYER GAMEPLAY
    else
    {
        // The end of game conditions
        for (int i = 0; i < 2; i++)
        {
            if (result == 1) 
            {
                if (players[i].symbol == 'X')
                {
                    printf("\n\t\t\t\t\t    ---THE WINNER IS %s---\n\n\n", players[i].name);
                    break;
                }
            }
            if (result == -1) 
            {
                if (players[i].symbol == 'O')
                {
                    printf("\n\t\t\t\t\t    ---THE WINNER IS %s---\n\n\n", players[i].name);
                    break;
                }
            }
            if (result == 0)
            {
                printf("\n\t\t\t\t\t     ---THE GAME IS DRAW---\n\n\n");
                break;
            }
        }
    }
}

// FUNCTION TO DRAW TICTACTOE BOARD
void draw_board()
{
    system("clear");

    printf("\n\n");
    printf("\t\t\t\tEnter the number on board to choose your place!\n\n\n");

    if (numOfplayer < 2)
    {
        printf("\t\t\t\t");
        printf("%s : %c", players[0].name, toupper(players[0].symbol));
    }
    else
    {
        printf("\t\t\t\t");
        printf("(Player 1) %s : %c\n", players[0].name, toupper(players[0].symbol));
        printf("\t\t\t\t");
        printf("(Player 2) %s : %c\n", players[1].name, toupper(players[1].symbol));
    }
    printf("\n\n");

    for (int row = 0; row < N; row++)
    {
        for (int col = 0; col < N; col++)
        {
            // Print extra vertical bar on top only when for first column
            if (col == 0)
            {
                printf("\t\t\t\t\t\t");
                printf("     |     |     ");
                printf("\n");
                printf("\t\t\t\t\t\t");
            }
            
            // Print the number on each board column--Horizontally
            printf("  %c  ", board[row][col]);

            // Print vertical bar at the end of number || EXCEPT for the last number
            if (col != 2)
            {
                printf("|");
            }
            
        }
        printf("\n");

        // Print horizontal bar only for 2 column
        if (row != 2)
        {
            printf("\t\t\t\t\t\t");
            printf("_____|_____|_____");
            printf("\n");
        }

        // Print extra vertical bar on below
        if (row == 2)
        {
            printf("\t\t\t\t\t\t");
            printf("     |     |     ");
            printf("\n");  
        }
    }
    printf("\n\n");
}
\$\endgroup\$
5
  • \$\begingroup\$ Always write your main() at the bottom of your C file and try not to use global variables. \$\endgroup\$
    – paladin
    Apr 2, 2021 at 23:44
  • \$\begingroup\$ @paladin "Always write your main() at the bottom of your C file" Can you explain why? I've seen suggestions to write it at the top instead. \$\endgroup\$
    – L. F.
    Apr 3, 2021 at 1:47
  • 1
    \$\begingroup\$ When writing main() at the end of your C file, you not only save 11 lines of redundant code (void delay(int seconds); void information(); void draw_board(); void draw_line(); void two_player_gameplay(); int check_win(); int check_minimax_win(); void show_the_winner(int result); void human_move(); void computer_move(); int minimax(int depth, bool isMaximizing);), you also have less ways to create bugs in your code. \$\endgroup\$
    – paladin
    Apr 3, 2021 at 1:59
  • \$\begingroup\$ @paladin Incorrect. As written, the functions computer_move and human_move are mutually recursive. There will have to be a forward declaration or prototype for at least one of them. What's more, removing the prototypes imposes the requirement that the functions by topologically sorted, rather than sorted for the convenience of the author or maintainer. \$\endgroup\$
    – aghast
    May 3, 2021 at 20:38
  • \$\begingroup\$ @aghast I wouldn't call that "mutually recursive" I would call that less optimal programming. (It's better to use a game loop instead of a function driven program.) A C-program should be sorted chronologically, while special elements should be commented. This is my opinion. \$\endgroup\$
    – paladin
    May 4, 2021 at 0:06

2 Answers 2

3
\$\begingroup\$

General Observations

The computer plays a good game!

The visual display is pretty good, but I would normally expect to see the box bound by vertical and horizontal lines.

The program could be a little more user friendly by accepting lower case input for x and o as well.

The error checking in some cases could be improved, the game treats 0 players as 1 player.

The game isn't quite as portable as it could be, I'm using Visual Studio 2019 Professional on Windows 10 and the system function clear doesn't work in the code, it generates a run time warning the first time it is executed. On a Linux or Unix system it might be better to use the NCurses library, but that still doesn't port to windows well.

Code Review

Avoid Global Variables

It is very difficult to read, write, debug and maintain programs that use global variables. Global variables can be modified by any function within the program and therefore require each function to be examined before making changes in the code. In C and C++ global variables impact the namespace and they can cause linking errors if they are defined in multiple files. The answers in this stackoverflow question provide a fuller explanation.

Here are the global variable declarations:

char board[3][3] = {
    {'1', '2', '3'},
    {'4', '5', '6'},
    {'7', '8', '9'}
};

player players[2];

char COMPUTER_AI;

int numOfplayer, winner;

Naming Best Practices

A best practice in C programming is to make all constants upper case and all variables lower case, it would be better if COMPUTER_AI was computer_ai. User defined types such as

typedef struct
{
    char name[30];
    char symbol;
}
player;

should have the first letter capitalized.

typedef struct
{
    char name[30];
    char symbol;
}
Player;

Initialize the Variables When They are Declared

I may not have looked hard enough, but I only found one place where a local variable was initialized when it was declared. The following is from the function two_player_gameplay().

    int player = 0;
    int choice, found, result;
    char mark, char_choice;

The C programming language has no concept of default initialization, every variable declared on the stack (in a function) should be initialized when it is declared. If it isn't initialized when it is declared the memory location will contain whatever was in that memory location before the variable was declared. This can lead to all kinds of interesting side affects.

Each variable should be declared and initialized on one line of code to make the code more dependable, more readable and easier to maintain. Adding or deleting a variable then means just adding or deleting one line of code.

    int player = 0;
    int choice = 0;
    int found = 0;
    int result = 0;
    char mark = players[player % 2].symbol;
    char char_choice = 0;

Complexity

The function check_minimax_win() is too complex (does too much), it is 149 lines long including comments. Functions should be able to fit into a single screen in an editor or IDE. This is to make reading, writing, debugging and maintenance of the function easier. It would be better if each if or else if was a function or contained a function. This might also reduce the number of comments necessary if the functions are well named.

There is a programming principle called the Single Responsibility Principle that applies here. The Single Responsibility Principle states:

that every module, class, or function should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by that module, class or function.

Recursion Versus Iteration

The human versus computer game is implemented using what was once known as Figure 8 Recursion, where 2 functions alternately call each other. While the stack depth this will reach in this particular program is limited, there are costs associated with recursion that are not incurred by iterative programming. If this was a large grid rather than a 3 by 3 grid there would be the possibility of a stack overflow. Recursion repeated puts the function information for the function call on the stack, since in this implementation the function never returns from the call to human_move() or computer_move() during the execution these functions are never popped off the stack.

It would be better to have a do while or while loop in main implement the calls to human_move() and computer_move() as is implemented in two_player_gameplay(), quite possibly there should be a one_payer_gameplay() function as well.

Constants and Array Sizes

The code contains

// GRID TOTAL BOX
const int N = 3;

char board[3][3] = {
    {'1', '2', '3'},
    {'4', '5', '6'},
    {'7', '8', '9'}
};

The constant N is used in many places for accessing the board, but the board should be defined using N as well. This would allow a one line edit to change the size of the board. If you get a compiler error when using N for array size, then change the code

const int N = 3;

to

#define N 3

char board[N][N] = {
    {'1', '2', '3'},
    {'4', '5', '6'},
    {'7', '8', '9'}
};

A constant named BOARD_SIZE might be better than N.

Code Organization

As @paladin observed in their comment, the normal file organization would put the main(void) function definition at the end of the file, and in that case prototype function declarations would not be necessary, especially if the recursion mentioned above was removed.

Function prototypes are very useful in large programs that contain multiple source files, and that case they will be in header files. In a single file program like this it is better to put the main() function at the bottom of the file and all the functions that get used in the proper order above main(). Keep in mind that every line of code written is another line of code where a bug can crawl into the code.

User Input

In the function information() some variant of this code is repeated multiple times:

        scanf("%s", players[0].name);
        getchar();

I believe the code is working around the fact that the end of line character '\n' was not read in by the scanf() function. There is a better function to use instead of scanf(), that function is fgets( char *buffer, int buffer_size, FILE *stream ). fgets() is better because it reads an entire line of input into the character array buffer, therefore the getchar() call is not necessary. A second improvement is that there is no possibility of buffer overflow using this method. Using the scanf() implementation can cause additional characters to be read into the player.name string array beyond the current size of 30. If that happens the player.symbol variable will be overwritten (possibly other variables as well).

Example:

    char buffer[128] = "";
    fgets(buffer, 128, stdin);
    strncpy(players[0].name, buffer, 30);
\$\endgroup\$
0
\$\begingroup\$

regarding the posted code:

Running it through the compiler results in:

gcc -ggdb3 -Wall -Wextra -Wconversion -pedantic -std=gnu11 -c "untitled2.c" -o "untitled2.o"

untitled2.c: In function ‘human_move’:

untitled2.c:95:31: warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
   95 |                 char_choice = choice + '0';
      |                               ^~~~~~

untitled2.c: In function ‘computer_move’:

untitled2.c:150:31: warning: conversion from ‘int’ to ‘float’ may change value [-Wconversion]
  150 |                 float score = minimax(0, false);
      |                               ^~~~~~~

untitled2.c: In function ‘minimax’:

untitled2.c:212:35: warning: conversion from ‘int’ to ‘float’ may change value [-Wconversion]
  212 |                     float score = minimax(depth + 1, false);
      |                                   ^~~~~~~

untitled2.c:225:16: warning: conversion from ‘float’ to ‘int’ may change value [-Wfloat-conversion]
  225 |         return bestScore;
      |                ^~~~~~~~~

untitled2.c:244:35: warning: conversion from ‘int’ to ‘float’ may change value [-Wconversion]
  244 |                     float score = minimax(depth + 1, true);
      |                                   ^~~~~~~

untitled2.c:257:16: warning: conversion from ‘float’ to ‘int’ may change value [-Wfloat-conversion]
  257 |         return bestScore;
      |                ^~~~~~~~~

untitled2.c: In function ‘two_player_gameplay’:

untitled2.c:286:31: warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
  286 |                 char_choice = choice + '0';
      |                               ^~~~~~

untitled2.c:265:17: warning: unused variable ‘found’ [-Wunused-variable]
  265 |     int choice, found, result;
      |                 ^~~~~

untitled2.c: In function ‘information’:

untitled2.c:341:29: warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
  341 |         players[0].symbol = toupper(players[0].symbol);
      |                             ^~~~~~~

untitled2.c:360:29: warning: conversion from ‘int’ to ‘char’ may change value [-Wconversion]
  360 |         players[0].symbol = toupper('X');
      |                             ^~~~~~~

Compilation finished successfully.

regarding: Compilation finished successfully.

since there are warnings, this only means the compiler made some guess as to what you wanted, not that the produced code will do what you want.

when posting code for review, please post code that cleanly compiles.

\$\endgroup\$
4
  • \$\begingroup\$ I had never heard of -Wconversion until today. So thanks for that. But seriously, is gcc going to add -Wdouble-plus-extra for the next bunch of "warnings that should be in -Wall but aren't"? Maybe someone could submit an ENH for "-Wmany" to do what -Wall does now, and then -Wall could mean "all". \$\endgroup\$
    – aghast
    May 3, 2021 at 20:44
  • 1
    \$\begingroup\$ Without -O2, some of the warning flags do not have any effect since they are side-effects of optimizing the code. \$\endgroup\$ May 3, 2021 at 22:55
  • \$\begingroup\$ I compiled the OPs code with no optimization. so the warnings I discussed are not side effects of optimization \$\endgroup\$ May 3, 2021 at 23:52
  • \$\begingroup\$ suggest starting at gcc options to learn about the gcc options \$\endgroup\$ May 3, 2021 at 23:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.