diff --git a/tictactoe_game.c b/tictactoe_game.c index d1ca380..fa61e0d 100644 --- a/tictactoe_game.c +++ b/tictactoe_game.c @@ -4,6 +4,9 @@ #include #include +/* + * Initializes a new game instance. + */ int tictactoe_game_init(ttt_game_t *game) { game->next_turn = 'X'; @@ -17,69 +20,74 @@ int tictactoe_game_init(ttt_game_t *game) return 0; } -// returns 0 if there is no winner yet, -1 if it's a tie, and the respective player if they win -char tictactoe_game_check_winner(ttt_game_t *game) +/* + * Steps over the board, starting from position (start_x, start_y) and ending + * at the board's boundaries, checking if all cells are occupied by the same + * player. + * + * start_x and start_y must lie within the board's boundaries. + * + * Returns the respective player, or '\x00' if a cell isn't occupied. + */ +static char tictactoe_game_check_streak(ttt_game_t *game, size_t start_x, + size_t start_y, ssize_t step_x, + ssize_t step_y) +{ + char winner; + size_t x; + size_t y; + + winner = game->board[start_x][start_y]; + x = start_x + step_x; + y = start_y + step_y; + + if (winner == ' ') + return 0; + + while (x >= 0 && x < 3 && y >= 0 && y < 3) { + if (game->board[x][y] != winner) + return 0; + x += step_x; + y += step_y; + } + + return winner; +} + +/* + * Checks for a winner with the current board configuration. + * + * Returns + * '\x00' if the game has not ended, + * '\xFF' if the game is tied, or + * the winner of the game. + */ +static char tictactoe_game_check_winner(ttt_game_t *game) { char winner; // check columns for (int x = 0; x < 3; x++) { - winner = game->board[x][0]; - if (winner != ' ') { - for (int y = 1; y < 3; y++) { - if (game->board[x][y] != winner) { - winner = 0; - break; - } - } - if (winner != 0) - return winner; - } + if ((winner = tictactoe_game_check_streak(game, x, 0, 0, 1))) + return winner; } // check rows for (int y = 0; y < 3; y++) { - winner = game->board[0][y]; - if (winner != ' ') { - for (int x = 1; x < 3; x++) { - if (game->board[x][y] != winner) { - winner = 0; - break; - } - } - if (winner != 0) - return winner; - } + if ((winner = tictactoe_game_check_streak(game, 0, y, 1, 0))) + return winner; } // check diagonal top-left <-> bottom-right - winner = game->board[0][0]; - if (winner != ' ') { - for (int i = 0; i < 3; i++) { - if (game->board[i][i] != winner) { - winner = 0; - break; - } - } - if (winner != 0) - return winner; - } + if ((winner = tictactoe_game_check_streak(game, 0, 0, 1, 1))) + return winner; // check diagonal bottom-left <-> top-right - winner = game->board[2][0]; - if (winner != ' ') { - for (int i = 0; i < 3; i++) { - if (game->board[2 - i][i] != winner) { - winner = 0; - break; - } - } - if (winner != 0) - return winner; - } + if ((winner = tictactoe_game_check_streak(game, 0, 2, 1, -1))) + return winner; // check for tie - winner = -1; + winner = '\xFF'; for (int x = 0; x < 3; x++) { for (int y = 0; y < 3; y++) { if (game->board[x][y] == ' ') { @@ -92,6 +100,16 @@ char tictactoe_game_check_winner(ttt_game_t *game) return winner; } +/* + * Occupies the given board position for the player whose turn it is. + * + * This function does *not* check whether there is a winner first. + * x and y must lie within the board's boundaries. + * + * Returns + * -1 if the given position is already occupied, or + * 0 on success. + */ int tictactoe_game_make_turn(ttt_game_t *game, size_t x, size_t y) { if (game->board[x][y] != ' ') { @@ -110,6 +128,9 @@ int tictactoe_game_make_turn(ttt_game_t *game, size_t x, size_t y) return 0; } +/* + * Prints a human-readable visualization of the game state to buf. + */ int tictactoe_game_snprint(ttt_game_t *game, char *buf, size_t count) { int pos; @@ -124,10 +145,10 @@ int tictactoe_game_snprint(ttt_game_t *game, char *buf, size_t count) goto fail; pos += ret; - if (game->winner == 0) { + if (!game->winner) { ret = snprintf(buf + pos, count - pos, "It's %c's turn!\n", game->next_turn); - } else if (game->winner == -1) { + } else if (game->winner == '\xFF') { ret = snprintf(buf + pos, count - pos, "It's a tie!\n"); } else { ret = snprintf(buf + pos, count - pos, "Player %c won!\n",