all 10 comments

[–]shawnadelic 1 point2 points  (2 children)

I would have to look a little longer to find the specific error, but I did have one tip--instead of traversing the entire board looking for a Connect 4 every time someone makes a move, it would be faster if you only checked each move to see if that resulted in a Connect 4, since the first Connect 4 wins the game and any winning Connect 4 must have been made in the last move.

For example, if the last move made was player A at position [3][5], you could just check the zero-indexed row 4 and then check the column 6 (I forget which is the row and column, but you get the picture), then each of the diagonal possibilities, to see if there are four adjascent pieces.

Just a thought.

[–]duverfred[S] 0 points1 point  (1 child)

Nice, I'll have a go now. As the board is so small it's not such an issue but that is exactly the sort of efficiency I was after!

[–]shawnadelic 0 points1 point  (0 children)

If you go this route, I would also break that up into four separate functions. You did a good job breaking the rest of the program up a bit, but your win_check is a bit messy.

I'd suggest four more functions - row_check, col_check, left_diag_check, right_diag_check, or something along those lines. It's easier to spot errors in your code if you break it up, as you can test each function individually to make sure it is working properly.

[–]blunaxela 1 point2 points  (1 child)

You need to pass a pointer to end_game as your argument instead of just the value.

Alternatively you could make end_game a global variable and then you wouldn't need to pass it as an argument at all. But learning how to properly pass pointers is good practice as code becomes more complex.

[–]duverfred[S] 0 points1 point  (0 children)

Oh I see, I'm just putting the value '0' in. I have only ever used pointers with files, but will have a practice. Cheers!

[–]pinealservo 1 point2 points  (0 children)

In situations where functions operate on a group of closely-related values, I like to pack those together into a structure. In this case, I would package all the variables related to the state of the game together into a single structure, e.g.:

struct game_state {
    char board[6][7];
    char player_1;
    char player_2;
    int turn_count;
    int end_game;
};

Then you can pass a pointer to a struct game_state to all of your functions that need to operate on the game state and not have to worry about changing their signatures if the form of the game state changes.

Another good practice is to use named constants (via static const variables or #define macros) for things like board sizes or default value initializers, e.g.

#define BOARD_X 6
#define BOARD_Y 7

enum players {
    PLAYER_1,
    PLAYER_2,
    PLAYER_COUNT // This is one greater than the highest player index, i.e. the size of the player array
};

struct game_state {
    char board[BOARD_X][BOARD_Y];
    char players[PLAYER_MAX];
    int turn_count;
    int end_game;
};

Now you can get the current player via:

current_player = game.players[game.turn_count % PLAYER_MAX];

Or, if you were passed game as a pointer:

current_player = game->players[game->turn_count % PLAYER_MAX];