Tic-Tac-Toe game requiring two players I have a working Tic-Tac-Toe game, which requires two players. The game also keeps track of the result of each game and assigns score to each player based on the result of the game.

For this, I have made use of structures:

Player Structure

typedef struct
{   
    int score[10];
    char choice;
    char name[10];
    bool turn;

}Player;


Game Structure

I have made use of another structure for the game setup, which contains the game board, the valid positions available, and also the number of games played:

typedef struct
{
    char board[3][3];
    bool validPos[10];
    int games;

}Game;


main function

 // included only a part of the main function that I am concerned about;
int main(void)
{
    Game *game = malloc(sizeof(Game));
    Player *player_1 = malloc(sizeof(Player));
    Player *player_2 = malloc(sizeof(Player));
}


Functions used in the game:

void assignXO(Player *, Player *);
void startGame(Game *, Player *, Player *);
void display(Game *, Player *, Player *);
bool victoryCheck(Game *);
bool isComplete(Game *);
void playerMove(Game *, Player *);
void declareWinner(Game *, Player *, Player *);
void gameReset(Game *);
void scoreBoard(Player *, Player *, int);


Queries


As you can observe the number of arguments passed to the functions are dependent on the purpose of the function. Is it wise to declare global variables for the structure so that potentially no arguments are needed to be passed to any of the functions?
Is it good practice to have more than 2 arguments passed to the functions? Does it help with conveying the logic to the reader?
If I used the structure Player within the Game structure, will it make the code any better in terms of readability and how well the code is organised?
Well, it's a good idea to minimize dependency on global state.
Doing so enhances testability and reduces dependencies, especially the hidden type.

Still, if you have such pervasively used state as Game, it's a valid thought to make it global instead of passing it around everywhere.
Pass the arguments needed, no more, no less.
It could be. Or not. In any case, it would reduce your flexibility in using your code for example for a tic-tac-toe contest.


Now lets critic your code:


validPos seems to be redundant, being a simple function of board. Or is there some extra magic?
Avoid dynamic allocation where it doesn't help. That means the memory-used is scoped and the block is reasonably small, like those allocations in main.
Anyway, avoid repeating your types to avoid mismatches. It should have been:

Game *game = malloc(sizeof *game);

Parameter names are valuable space for documenting an arguments use.
At least scoreBoard and declareWinner could use a bit more:

void declareWinner(Game *, Player* winner, Player* looser);
void scoreBoard(Player *, Player *, int round);
// documented my guesses what the pertinent arguments mean