├── Makefile ├── minimax.h ├── LICENSE ├── obstruction.h ├── README.md ├── main.c ├── minimax.c └── obstruction.c /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | 3 | main: minimax.o obstruction.o main.c 4 | $(CC) minimax.o obstruction.o main.c -o main 5 | 6 | minimax.o: minimax.c obstruction.o 7 | $(CC) -c minimax.c 8 | 9 | obstruction.o: obstruction.c 10 | $(CC) -c obstruction.c 11 | 12 | clean: 13 | rm *.o 14 | rm main 15 | 16 | run: 17 | ./main 18 | -------------------------------------------------------------------------------- /minimax.h: -------------------------------------------------------------------------------- 1 | #ifndef MINIMAX_H 2 | #define MINIMAX_H 3 | #include "obstruction.h" 4 | 5 | // Prototypes 6 | int get_max_values_index(int array[], int size); 7 | int get_min_values_index(int array[], int size); 8 | void free_2d_array(int **array, int size); 9 | int **get_moves(BoardState **board); 10 | BoardState **get_board_of_move(BoardState **board, int x, int y); 11 | int utility(BoardState **board); 12 | int max_value(BoardState **board, int alpha, int beta); 13 | int min_value(BoardState **board, int alpha, int beta); 14 | void minimax(BoardState **board); 15 | 16 | #endif // MINIMAX_H 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Muhammed Ali Dilek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /obstruction.h: -------------------------------------------------------------------------------- 1 | #ifndef OBSTRUCTION_H 2 | #define OBSTRUCTION_H 3 | 4 | #include 5 | 6 | #define MAX(x, y) (((x) > (y)) ? (x) : (y)) 7 | #define MIN(x, y) (((x) < (y)) ? (x) : (y)) 8 | 9 | #define BOARD_SIZE 6 10 | 11 | #define CLI_RESET "\033[0m" 12 | #define CLI_BOLD "\033[1m" 13 | #define CLI_UNDERLINE "\033[4m" 14 | #define CLI_CYAN "\033[36m" 15 | #define CLI_BOLD_YELLOW "\033[1;33m" 16 | #define CLI_BOLD_BLUE "\033[1;34m" 17 | #define CLI_BOLD_RED "\033[1;31m" 18 | #define CLI_BOLD_GREEN "\033[1;32m" 19 | 20 | typedef enum eBoardState { 21 | FREE, 22 | PLAYER_X, 23 | PLAYER_O, 24 | BLOCKED 25 | } BoardState; 26 | 27 | // Prototypes 28 | void clear(); 29 | void print_title(); 30 | void intro(); 31 | bool get_play_against_bot(); 32 | bool get_user_starts(); 33 | BoardState **get_board(); 34 | void free_board(BoardState **board); 35 | void reset_board(BoardState **board); 36 | void print_board(BoardState **board); 37 | bool terminal_state(BoardState **board); 38 | BoardState get_winner(BoardState **board); 39 | BoardState get_player(BoardState **board); 40 | void place_move(BoardState **board, BoardState player, int x, int y); 41 | void user_move(BoardState **board); 42 | void print_winner(BoardState **board); 43 | void print_bot_is_thinking(); 44 | bool play_again(); 45 | 46 | #endif // OBSTRUCTION_H 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # How to play 2 | 3 | The game is played on a grid. First player is X and the second player is O. Both of the players take turns writing their symbols in a cell. When a player writes their symbol in a cell, the neighbours are blocked and the other player is not able to play on the blocked cells (shown shaded in the following diagrams). The goal of the game is to block all the cells and leave no place for the other player to move. The first player unable to move loses. 4 | 5 | For example, in the following game player X wins because O has nowhere to play: 6 | 7 | ![alt text](http://www.papg.com/images/Obstruction.gif) 8 | 9 | # Build & Run 10 | 11 | For a better experience use a terminal size of 80x24 and a font size of 12. 12 | 13 | You will need a standards-compliant C compiler, the standard C library with header files, and make. On Debian GNU/Linux and derivatives, you can install these with 14 | 15 | $ apt install build-essential 16 | 17 | After that clone the repository and cd into it 18 | 19 | $ git clone https://github.com/xemeds/obstruction-game.git 20 | 21 | $ cd obstruction-game 22 | 23 | Build and run with 24 | 25 | $ make 26 | 27 | $ ./main 28 | 29 | # References 30 | 31 | Description and Images of the game: [papg.com](http://www.papg.com/) 32 | 33 | # License 34 | 35 | This project is under the [MIT](https://github.com/xemeds/obstruction-game/blob/master/LICENSE) license. 36 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | // Author: Muhammed Ali Dilek (xemeds) 2 | 3 | #include 4 | #include "obstruction.h" 5 | #include "minimax.h" 6 | 7 | int main () { 8 | 9 | BoardState **board = get_board(); 10 | 11 | // Clear screen 12 | clear(); 13 | 14 | // Print the intro 15 | intro(); 16 | 17 | // Get the options 18 | bool play_against_bot = get_play_against_bot(); 19 | bool user_starts = false; 20 | bool bot_has_started = false; 21 | if (play_against_bot) { 22 | user_starts = get_user_starts(); 23 | } 24 | 25 | // Main loop 26 | while (1) { 27 | // Game loop 28 | while (1) 29 | { 30 | // If the user is playing against the bot and does not want to start first and the bot has not started (first move for the bot) 31 | if (play_against_bot && !user_starts && !bot_has_started) 32 | { 33 | // Print the board 34 | print_board(board); 35 | 36 | // Bots move (calculating the first state takes time, so just place at 0, 0 which is a winning move for X) 37 | place_move(board, get_player(board), 0, 0); 38 | 39 | // Set bot has started to true 40 | bot_has_started = true; 41 | } 42 | 43 | // Print the board 44 | print_board(board); 45 | 46 | // Check if the user has places left to play 47 | if (terminal_state(board)) 48 | { 49 | print_winner(board); 50 | break; 51 | } 52 | 53 | // Users move 54 | user_move(board); 55 | 56 | // If the user is playing against the bot 57 | if (play_against_bot) { 58 | // Print the board 59 | print_board(board); 60 | 61 | // Check if the bot has places left to play 62 | if (terminal_state(board)) 63 | { 64 | print_winner(board); 65 | break; 66 | } 67 | 68 | print_bot_is_thinking(); 69 | 70 | // Bots move 71 | minimax(board); 72 | } 73 | } 74 | 75 | // If the user wants to play again 76 | if (play_again()) { 77 | // Reset the board 78 | reset_board(board); 79 | 80 | // If the user was playing against the bot and didnt want to start first 81 | if (play_against_bot && !user_starts) 82 | { 83 | // Set bot has started to false 84 | bot_has_started = false; 85 | } 86 | 87 | } 88 | // Else if the user does not want to play again 89 | else { 90 | // Free the board 91 | free_board(board); 92 | 93 | // Break the loop 94 | break; 95 | } 96 | } 97 | 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /minimax.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "minimax.h" 4 | 5 | // X is the maximizing player, O is the minimizing player 6 | 7 | // Returns the index of the max value of the given array 8 | int get_max_values_index(int array[], int size) { 9 | int index = 0; 10 | 11 | for (int i = 0; i < size; i++) { 12 | if (array[i] > array[index]) 13 | index = i; 14 | } 15 | 16 | return index; 17 | } 18 | 19 | // Returns the index of the min value of the given array 20 | int get_min_values_index(int array[], int size) { 21 | int index = 0; 22 | 23 | for (int i = 0; i < size; i++) { 24 | if (array[i] < array[index]) 25 | index = i; 26 | } 27 | 28 | return index; 29 | } 30 | 31 | // Frees a given 2d array 32 | void free_2d_array(int **array, int size) { 33 | for (int i = 0; i < size && array[i] != NULL; i++) { 34 | free(array[i]); 35 | } 36 | 37 | free(array); 38 | } 39 | 40 | // Returns all the possible moves available on the board 41 | int **get_moves(BoardState **board) { 42 | // Allocate the 2d array for the moves 43 | int **moves = (int **)malloc(BOARD_SIZE * BOARD_SIZE * sizeof(int *)); 44 | 45 | // Add all the positions of the free cells on the board to the array 46 | int move_index = 0; 47 | for (int y = 0; y < BOARD_SIZE; y++) 48 | { 49 | for (int x = 0; x < BOARD_SIZE; x++) 50 | { 51 | if (board[y][x] == FREE) { 52 | moves[move_index] = (int *)malloc(2 * sizeof(int)); 53 | moves[move_index][0] = x; 54 | moves[move_index][1] = y; 55 | move_index++; 56 | } 57 | } 58 | } 59 | 60 | // If the array is not full set the rest of the array to null 61 | if (move_index != BOARD_SIZE * BOARD_SIZE) { 62 | for (int i = move_index; i < BOARD_SIZE * BOARD_SIZE; i++) { 63 | moves[i] = NULL; 64 | } 65 | } 66 | 67 | return moves; 68 | } 69 | 70 | // Returns a copy of the board that results from making the given move 71 | BoardState **get_board_of_move(BoardState **board, int x, int y) { 72 | // Get a new board 73 | BoardState **new_board = get_board(); 74 | 75 | // Copy the cells of the given board to the new board 76 | for (int y = 0; y < BOARD_SIZE; y++) 77 | { 78 | for (int x = 0; x < BOARD_SIZE; x++) 79 | { 80 | new_board[y][x] = board[y][x]; 81 | } 82 | } 83 | 84 | // Place the given move on the new board 85 | place_move(new_board, get_player(new_board), x, y); 86 | 87 | return new_board; 88 | } 89 | 90 | // Returns 1 if X has won the game, -1 if O has won (there cannot be a draw) 91 | int utility(BoardState **board) { 92 | BoardState winner = get_winner(board); 93 | if (winner == PLAYER_X) 94 | return 1; 95 | else 96 | return -1; 97 | } 98 | 99 | int max_value(BoardState **board, int alpha, int beta) { 100 | if (terminal_state(board)) 101 | return utility(board); 102 | 103 | int value = -2; 104 | int **moves = get_moves(board); 105 | for (int i = 0; i < BOARD_SIZE * BOARD_SIZE && moves[i] != NULL; i++) { 106 | BoardState **new_board = get_board_of_move(board, moves[i][0], moves[i][1]); 107 | value = MAX(value, min_value(new_board, alpha, beta)); 108 | free_board(new_board); 109 | alpha = MAX(alpha, value); 110 | if (beta <= alpha) 111 | break; 112 | } 113 | 114 | free_2d_array(moves, BOARD_SIZE * BOARD_SIZE); 115 | 116 | return value; 117 | } 118 | 119 | int min_value(BoardState **board, int alpha, int beta) { 120 | if (terminal_state(board)) 121 | return utility(board); 122 | 123 | int value = 2; 124 | int **moves = get_moves(board); 125 | for (int i = 0; i < BOARD_SIZE * BOARD_SIZE && moves[i] != NULL; i++) { 126 | BoardState **new_board = get_board_of_move(board, moves[i][0], moves[i][1]); 127 | value = MIN(value, max_value(new_board, alpha, beta)); 128 | free_board(new_board); 129 | beta = MIN(beta, value); 130 | if (beta <= alpha) 131 | break; 132 | } 133 | 134 | free_2d_array(moves, BOARD_SIZE * BOARD_SIZE); 135 | 136 | return value; 137 | } 138 | 139 | // Places the optimal move for the current player on the board 140 | void minimax(BoardState **board) { 141 | int x, y; 142 | int **moves = get_moves(board); 143 | int values[BOARD_SIZE * BOARD_SIZE]; 144 | 145 | if (get_player(board) == PLAYER_X) { 146 | int number_of_moves = 0; 147 | bool found_winning_move = false; 148 | for (int i = 0; i < BOARD_SIZE * BOARD_SIZE && moves[i] != NULL; i++) { 149 | BoardState **new_board = get_board_of_move(board, moves[i][0], moves[i][1]); 150 | // printf("TRYING MOVE: (%i, %i)\n", moves[i][0], moves[i][1]); 151 | values[i] = min_value(new_board, -2, 2); 152 | // printf("VALUE: %i\n", values[i]); 153 | free_board(new_board); 154 | number_of_moves++; 155 | 156 | if (values[i] == 1) 157 | break; 158 | } 159 | 160 | /* 161 | for (int i = 0; i < number_of_moves; i++) { 162 | printf("%i, ", values[i]); 163 | } 164 | printf("\n"); 165 | */ 166 | 167 | int min_values_index = get_max_values_index(values, number_of_moves); 168 | // printf("MIN VALUES INDEX: %i\n", min_values_index); 169 | x = moves[min_values_index][0]; 170 | y = moves[min_values_index][1]; 171 | } 172 | else { 173 | int number_of_moves = 0; 174 | bool found_winning_move = false; 175 | for (int i = 0; i < BOARD_SIZE * BOARD_SIZE && moves[i] != NULL; i++) { 176 | BoardState **new_board = get_board_of_move(board, moves[i][0], moves[i][1]); 177 | // printf("TRYING MOVE: (%i, %i)\n", moves[i][0], moves[i][1]); 178 | values[i] = max_value(new_board, -2, 2); 179 | // printf("VALUE: %i\n", values[i]); 180 | free_board(new_board); 181 | number_of_moves++; 182 | 183 | if (values[i] == -1) 184 | break; 185 | } 186 | 187 | /* 188 | for (int i = 0; i < number_of_moves; i++) { 189 | printf("%i, ", values[i]); 190 | } 191 | printf("\n"); 192 | */ 193 | 194 | int min_values_index = get_min_values_index(values, number_of_moves); 195 | // printf("MIN VALUES INDEX: %i\n", min_values_index); 196 | x = moves[min_values_index][0]; 197 | y = moves[min_values_index][1]; 198 | } 199 | 200 | free_2d_array(moves, BOARD_SIZE * BOARD_SIZE); 201 | 202 | place_move(board, get_player(board), x, y); 203 | } 204 | -------------------------------------------------------------------------------- /obstruction.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "obstruction.h" 5 | 6 | // Clears the screen 7 | void clear() { 8 | system("clear"); 9 | } 10 | 11 | // Prints the ASCII art title 12 | void print_title() { 13 | printf(CLI_CYAN); 14 | printf("\n"); 15 | printf("\t █▀▀█ █▀▀▄ █▀▀ ▀▀█▀▀ █▀▀█ █░░█ █▀▀ ▀▀█▀▀ ░▀░ █▀▀█ █▀▀▄\n"); 16 | printf("\t █░░█ █▀▀▄ ▀▀█ ░░█░░ █▄▄▀ █░░█ █░░ ░░█░░ ▀█▀ █░░█ █░░█\n"); 17 | printf("\t ▀▀▀▀ ▀▀▀░ ▀▀▀ ░░▀░░ ▀░▀▀ ░▀▀▀ ▀▀▀ ░░▀░░ ▀▀▀ ▀▀▀▀ ▀░░▀\n\n"); 18 | printf(CLI_RESET CLI_BOLD); 19 | } 20 | 21 | // Introduction to the game 22 | void intro() { 23 | // ASCII art title 24 | print_title(); 25 | 26 | // Intro 27 | printf(CLI_BOLD_YELLOW CLI_UNDERLINE "\n\nDescription:\n"); 28 | printf(CLI_RESET CLI_BOLD "The game is played on a grid. "); 29 | printf("First player is " CLI_BOLD_BLUE "X" CLI_RESET CLI_BOLD " and the second player is " CLI_BOLD_RED "O" CLI_RESET CLI_BOLD ". "); 30 | printf("Both of the players take turns writing their symbols in a cell. "); 31 | printf("When a player writes their symbol in a cell, the neighbours are blocked and the other player is not able to play on the blocked cells. "); 32 | printf("The goal of the game is to block all the cells and leave no place for the other player to move.\n"); 33 | printf("Further explanation: www.papg.com/show?2XMX\n\n"); 34 | printf(CLI_BOLD_YELLOW "To write your symbol in a cell you must specify the location.\n"); 35 | printf(CLI_BOLD_YELLOW "Ex:"); 36 | printf(CLI_RESET CLI_BOLD " a1 c4 b6\n\n"); 37 | } 38 | 39 | // Asks if the user wants to play against the bot or not 40 | bool get_play_against_bot() { 41 | char input[2]; 42 | 43 | printf(CLI_BOLD); 44 | printf("Do you want to play against the bot? [Y/n]: "); 45 | 46 | scanf("%1s", input); 47 | 48 | // If the input is the letter Y 49 | if (input[0] == 'Y' || input[0] == 'y') { 50 | // Return true 51 | return true; 52 | } 53 | // Else 54 | else { 55 | // Return false 56 | return false; 57 | } 58 | } 59 | 60 | // Asks if the user is the starting player or not 61 | bool get_user_starts() { 62 | char input[2]; 63 | 64 | printf(CLI_BOLD); 65 | printf("Do you want to be the starting player? [Y/n]: "); 66 | 67 | scanf("%1s", input); 68 | 69 | // If the input is the letter Y 70 | if (input[0] == 'Y' || input[0] == 'y') { 71 | // Return true 72 | return true; 73 | } 74 | // Else 75 | else { 76 | // Return false 77 | return false; 78 | } 79 | } 80 | 81 | // Returns the starting state of the board 82 | BoardState **get_board() { 83 | // Allocate the new board 84 | BoardState **board = (BoardState **)malloc(BOARD_SIZE * sizeof(BoardState *)); 85 | 86 | // Set all the cells to free 87 | for (int y = 0; y < BOARD_SIZE; y++) 88 | { 89 | board[y] = (BoardState *)malloc(BOARD_SIZE * sizeof(BoardState)); 90 | for (int x = 0; x < BOARD_SIZE; x++) 91 | { 92 | board[y][x] = FREE; 93 | } 94 | } 95 | 96 | return board; 97 | } 98 | 99 | // Frees a given board 100 | void free_board(BoardState **board) { 101 | for (int i = 0; i < BOARD_SIZE; i++) { 102 | free(board[i]); 103 | } 104 | 105 | free(board); 106 | } 107 | 108 | // Resets a given board 109 | void reset_board(BoardState **board) { 110 | for (int y = 0; y < BOARD_SIZE; y++) 111 | { 112 | for (int x = 0; x < BOARD_SIZE; x++) 113 | { 114 | board[y][x] = FREE; 115 | } 116 | } 117 | } 118 | 119 | // Prints the board 120 | void print_board(BoardState **board) { 121 | clear(); 122 | printf(CLI_RESET); 123 | print_title(); 124 | printf("\t\t\t a b c d e f\n"); 125 | printf(CLI_BOLD_GREEN); 126 | printf("\t\t\t ╔═══╤═══╤═══╤═══╤═══╤═══╗\n"); 127 | for (int y = 0; y < BOARD_SIZE; y++) 128 | { 129 | printf(CLI_RESET CLI_BOLD); 130 | printf("\t\t\t %i ", y + 1); 131 | printf(CLI_BOLD_GREEN "║"); 132 | for (int x = 0; x < BOARD_SIZE; x++) 133 | { 134 | switch(board[y][x]) { 135 | case PLAYER_X: 136 | printf(CLI_BOLD_BLUE " X "); 137 | break; 138 | case PLAYER_O: 139 | printf(CLI_BOLD_RED " O "); 140 | break; 141 | case FREE: 142 | printf(" "); 143 | break; 144 | case BLOCKED: 145 | printf(CLI_RESET CLI_BOLD " # "); 146 | break; 147 | } 148 | if (x != BOARD_SIZE - 1) 149 | printf(CLI_BOLD_GREEN "│"); 150 | } 151 | printf(CLI_BOLD_GREEN "║\n"); 152 | if (y != BOARD_SIZE - 1) 153 | printf("\t\t\t ╟───┼───┼───┼───┼───┼───╢\n"); 154 | } 155 | printf("\t\t\t ╚═══╧═══╧═══╧═══╧═══╧═══╝\n\n"); 156 | printf(CLI_RESET CLI_BOLD); 157 | printf("\t\t ==+==+==+==+==+==+==+==+==+==+==+==+==+==\n\n"); 158 | } 159 | 160 | // Returns true if there are no more places left to play on the board, else returns false 161 | bool terminal_state(BoardState **board) { 162 | for (int y = 0; y < BOARD_SIZE; y++) 163 | { 164 | for (int x = 0; x < BOARD_SIZE; x++) 165 | { 166 | if (board[y][x] == FREE) 167 | return false; 168 | } 169 | } 170 | return true; 171 | } 172 | 173 | // Returns the winner of the board 174 | BoardState get_winner(BoardState **board) { 175 | BoardState player = get_player(board); 176 | if (player == PLAYER_X) 177 | return PLAYER_O; 178 | else 179 | return PLAYER_X; 180 | } 181 | 182 | // Returns the player who has the next turn on the board 183 | BoardState get_player(BoardState **board) { 184 | int x_count = 0, o_count = 0; 185 | for (int y = 0; y < BOARD_SIZE; y++) { 186 | for (int x = 0; x < BOARD_SIZE; x++) { 187 | if (board[y][x] == PLAYER_X) 188 | x_count++; 189 | else if (board[y][x] == PLAYER_O) 190 | o_count++; 191 | } 192 | } 193 | 194 | if (x_count > o_count) 195 | return PLAYER_O; 196 | return PLAYER_X; 197 | } 198 | 199 | // Places a move on the board 200 | void place_move(BoardState **board, BoardState player, int x, int y) { 201 | // Place the neighbors 202 | for (int i = MAX(y - 1, 0); i < MIN(y + 2, BOARD_SIZE); i++) { 203 | for (int j = MAX(x - 1, 0); j < MIN(x + 2, BOARD_SIZE); j++) { 204 | board[i][j] = BLOCKED; 205 | } 206 | } 207 | 208 | // Place the symbol 209 | board[y][x] = player; 210 | } 211 | 212 | // Gets a valid user input and places it on the board 213 | void user_move(BoardState **board) { 214 | char move[3]; 215 | int x, y; 216 | 217 | BoardState player = get_player(board); 218 | 219 | // Loop untill a valid move is given 220 | while (1) 221 | { 222 | // Get the move 223 | if (player == PLAYER_X) 224 | printf("\t\t\t Player " CLI_BOLD_BLUE "X" CLI_RESET CLI_BOLD " > "); 225 | else if (player == PLAYER_O) 226 | printf("\t\t\t Player " CLI_BOLD_RED "O" CLI_RESET CLI_BOLD " > "); 227 | scanf("%2s", move); 228 | 229 | // Check the syntax 230 | int len = strlen(move); 231 | if (!(len == 2) || !('a' <= move[0] && move[0] <= 'f') || !('1' <= move[1] && move[1] <= '6')) 232 | { 233 | print_board(board); 234 | continue; 235 | } 236 | 237 | // Convert the move into integers 238 | x = move[0] - 'a'; 239 | y = move[1] - '1'; 240 | 241 | // Check if the moves location is empty 242 | if (board[y][x] != FREE) 243 | { 244 | print_board(board); 245 | continue; 246 | } 247 | 248 | // Break the loop 249 | break; 250 | } 251 | 252 | // Place the move 253 | place_move(board, get_player(board), x, y); 254 | } 255 | 256 | // Prints the winner of the board 257 | void print_winner(BoardState **board) { 258 | BoardState winner = get_winner(board); 259 | printf(CLI_BOLD_YELLOW); 260 | if (winner == PLAYER_X) 261 | printf("\t\t\t\t PLayer " CLI_BOLD_BLUE "X" CLI_BOLD_YELLOW " wins.\n\n"); 262 | else if (winner == PLAYER_O) 263 | printf("\t\t\t\t PLayer " CLI_BOLD_RED "O" CLI_BOLD_YELLOW " wins.\n\n"); 264 | printf(CLI_RESET); 265 | } 266 | 267 | // Prints bot is thinking 268 | void print_bot_is_thinking() { 269 | printf(CLI_BOLD_YELLOW); 270 | printf("\t\t\t The bot is thinking...\n"); 271 | printf("\t (it may take a little bit of time if it's the bots first move)\n\n"); 272 | } 273 | 274 | // Checks if the user wants to play again 275 | bool play_again() { 276 | char input[2]; 277 | 278 | printf(CLI_BOLD); 279 | printf("\t\t\t\t R - Replay\n"); 280 | printf("\t\t\t\t Q - Quit\n\n"); 281 | printf("\t\t\t\t > "); 282 | 283 | scanf("%1s", input); 284 | 285 | // If the input is the letter R 286 | if (input[0] == 'R' || input[0] == 'r') { 287 | // Return true 288 | return true; 289 | } 290 | // Else 291 | else { 292 | // Return false 293 | return false; 294 | } 295 | } 296 | --------------------------------------------------------------------------------