diff --git a/Makefile b/Makefile index f8649732e840c9546f7bdbdcc11c1e239bf1fc11..6d85b74615d772d09c4e77a3011e49e0790fba21 100644 --- a/Makefile +++ b/Makefile @@ -3,15 +3,16 @@ CFLAGS = -g -Wall -Wextra -Wpedantic LDLIBS = -lm LDFLAGS = -fsanitize=address -fsanitize=leak -puissance4: puissance4.c src/board.o +puissance4: puissance4.c src/board.o src/game.o $(CC) $(CFLAGS) $^ -o $@ $(LDLIBS) $(LDFLAGS) board.o: src/board.h +game.o: src/game.h clean: @echo "this rule must clean everything up (including candidate files in testbed)" $(MAKE) -C testbed clean - $(RM) -f puissance4 *.o *.cand + $(RM) -rf puissance4 *.o *.cand run: puissance4 ./$< 3 5 6 diff --git a/puissance4.c b/puissance4.c index 9e91dbc364e1b5a22c45e941c1876046539ae704..85b0e6b03b65c3836192ea7c11d062aeb817297e 100644 --- a/puissance4.c +++ b/puissance4.c @@ -1,14 +1,13 @@ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> - +#include "src/game.h" #include "src/board.h" #define AI_SEED 0 #define MIN_COLUMN 4 #define MIN_ROW 4 -bool does_player_win(int col, int row, int rows, int cols, int** board); void flush_input(); bool is_valid_game_mode(int game_mode); bool is_valid_board_size(int rows, int cols); @@ -16,21 +15,6 @@ bool is_column_full(int col, int rows, int** board); int select_random_column(int number_of_columns, int number_of_rows, int** board); int select_smart_column(int number_of_columns, int number_of_rows, int** board); -struct coordinate { - int x; - int y; -}; - -enum GAME_MODE { - PLAYER_VS_RANDOM_AI = 1, - PLAYER_VS_SMART_AI = 2, - PLAYER_VS_PLAYER = 3 -}; - - -void place_token(int player, struct coordinate coordinate, int** board); -struct coordinate find_empty_slot(int col, int rows, int** board); - int main(int argc, char const* argv[]) { if (argc != 4 || !is_valid_game_mode(atoi(argv[1])) || !is_valid_board_size(atoi(argv[2]), atoi(argv[3]))) { printf("Usage: %s <mode> <row> <col>\n", argv[0]); @@ -41,66 +25,11 @@ int main(int argc, char const* argv[]) { return EXIT_FAILURE; } - int game_mode = atoi(argv[1]); + enum GAME_MODE game_mode = atoi(argv[1]); int rows = atoi(argv[2]); int cols = atoi(argv[3]); srand(AI_SEED); - - int** board = create_board(rows, cols); - if (board == NULL) { - fprintf(stderr, "Error: Unable to allocate memory for the board.\n"); - return EXIT_FAILURE; - } - - fill_board(rows, cols, board); - printf("Board size is %dx%d (rows x col)", rows, cols); - print_board(rows, cols, board); - - int player_selected_column, player; - struct coordinate coordinate; - - while (true) { - if (game_mode == PLAYER_VS_PLAYER || player % 2 == 0) { - printf("\nColumn number? (starts at 1):"); - scanf("%d", &player_selected_column); - } else { - if (game_mode == PLAYER_VS_RANDOM_AI) { - player_selected_column = select_random_column(cols, rows, board); - } else if (game_mode == PLAYER_VS_SMART_AI) { - player_selected_column = select_smart_column(cols, rows, board); - } - } - - if (player_selected_column < 1 || player_selected_column > cols) { - continue; - } - - coordinate = find_empty_slot(player_selected_column, rows, board); - if (coordinate.x != -1 && coordinate.y != -1) { - place_token(player % 2, coordinate, board); - } - - print_board(rows, cols, board); - if (does_player_win(coordinate.x, coordinate.y, rows, cols, board)) { - if (game_mode == PLAYER_VS_PLAYER) { - printf("\nPlayer %s won!\n", player % 2 == 0 ? "one" : "two"); - } else { - printf("\n%s won!\n", player % 2 == 0 ? "Player one" : "Computer"); - } - break; - } - if (player + 1 == rows * cols) { - printf("\nIt is a draw.\n"); - break; - } - - player++; - } - if (destroy_board(rows, board) == EXIT_FAILURE) { - fprintf(stderr, "Error: Unable to free memory for the board.\n"); - return EXIT_FAILURE; - } - return EXIT_SUCCESS; + return play_game(game_mode, rows, cols); } bool is_valid_game_mode(int game_mode) { @@ -126,10 +55,10 @@ int select_smart_column(int number_of_columns, int number_of_rows, int** board) // Check if the AI can win in the next move, if so, play it for (int col = 0; col < number_of_columns; col++) { if (!is_column_full(col + 1, number_of_rows, board)) { - struct coordinate temp_coordinate = find_empty_slot(col + 1, number_of_rows, board); + struct coordinate temp_coordinate = find_empty_slot(col, number_of_rows, board); board[temp_coordinate.x][temp_coordinate.y] = PLAYER2; - if (does_player_win(temp_coordinate.x, temp_coordinate.y, number_of_rows, number_of_columns, board)) { + if (does_player_win(temp_coordinate, number_of_rows, number_of_columns, board)) { robot_selected_column = col + 1; board[temp_coordinate.x][temp_coordinate.y] = EMPTY; break; @@ -146,11 +75,11 @@ int select_smart_column(int number_of_columns, int number_of_rows, int** board) // Check if the player can win in the next move, if so, block it for (int col = 0; col < number_of_columns; col++) { if (!is_column_full(col + 1, number_of_rows, board)) { - struct coordinate temp_coordinate = find_empty_slot(col + 1, number_of_rows, board); + struct coordinate temp_coordinate = find_empty_slot(col, number_of_rows, board); // Check if the player can win in the next move board[temp_coordinate.x][temp_coordinate.y] = PLAYER1; - if (does_player_win(temp_coordinate.x, temp_coordinate.y, number_of_rows, number_of_columns, board)) { + if (does_player_win(temp_coordinate, number_of_rows, number_of_columns, board)) { robot_selected_column = col + 1; board[temp_coordinate.x][temp_coordinate.y] = EMPTY; break; @@ -176,73 +105,7 @@ bool is_column_full(int col, int rows, int** board) { return true; } -struct coordinate find_empty_slot(int col, int rows, int** board) { - struct coordinate coordinate = { -1, -1 }; - for (int i = rows - 1; i >= 0; i--) { - if (board[i][col - 1] == EMPTY) { - coordinate.x = i; - coordinate.y = col - 1; - break; - } - } - return coordinate; -} - -void place_token(int player, struct coordinate coordinate, int** board) { - if (coordinate.x != -1 && coordinate.y != -1) { - board[coordinate.x][coordinate.y] = player == 0 ? PLAYER1 : PLAYER2; - } -} - void flush_input() { int c; while ((c = getchar()) != '\n' && c != EOF); } - -bool does_player_win(int row, int col, int rows, int cols, int** board) { - int current_piece = board[row][col]; - for (int i = 0; i < rows - 3; i++) { - - if (board[i][col] == current_piece - && board[i + 1][col] == current_piece - && board[i + 2][col] == current_piece - && board[i + 3][col] == current_piece) - { - return true; - } - - } - - for (int i = 0; i < cols - 3; i++) { - if (board[row][i] == current_piece - && board[row][i + 1] == current_piece - && board[row][i + 2] == current_piece - && board[row][i + 3] == current_piece) { - return true; - } - } - - for (int i = 0; i < rows - 3; i++) { - for (int j = 0; j < cols - 3; j++) { - if (board[i][j] == current_piece && - board[i + 1][j + 1] == current_piece && - board[i + 2][j + 2] == current_piece && - board[i + 3][j + 3] == current_piece) { - return true; - } - } - } - - // Diagonale droite à gauche - for (int i = 0; i < rows - 3; i++) { - for (int j = 3; j < cols; j++) { - if (board[i][j] == current_piece && - board[i + 1][j - 1] == current_piece && - board[i + 2][j - 2] == current_piece && - board[i + 3][j - 3] == current_piece) { - return true; - } - } - } - return false; -} diff --git a/src/board.c b/src/board.c index 4072590dc6d363adef67282e906b9cacc9be070b..f8d640fc251f252a81bf7bbdbd1717c9f1717e4a 100644 --- a/src/board.c +++ b/src/board.c @@ -1,6 +1,6 @@ -#include "board.h" #include <stdlib.h> #include <stdio.h> +#include "board.h" void fill_board(int rows, int cols, int** board) { for (int i = 0; i < rows; i++) { diff --git a/src/board.h b/src/board.h index 38ea7654c4bfb02080e145567adf7defdcfc052d..588bd0dca6a16cf4c83c9ce3ea94b7bd79a4ba32 100644 --- a/src/board.h +++ b/src/board.h @@ -1,4 +1,5 @@ #ifndef _BOARD_H_ +#define _BOARD_H_ enum BOARD_STATE { EMPTY = 0, diff --git a/src/game.c b/src/game.c new file mode 100644 index 0000000000000000000000000000000000000000..b80875a4cf01a921d8426d1fa4b8b073e5512be7 --- /dev/null +++ b/src/game.c @@ -0,0 +1,133 @@ +#include <stdio.h> +#include <stdlib.h> +#include "game.h" + +void place_token(int player, struct coordinate coordinate, int** board) { + if (coordinate.x != -1 && coordinate.y != -1) { + board[coordinate.x][coordinate.y] = player == 0 ? PLAYER1 : PLAYER2; + } +} + +struct coordinate find_empty_slot(int x, int rows, int** board) { + struct coordinate coordinate = { -1, -1 }; + if (x < 0 || x > rows) { + return coordinate; + } + for (int i = rows - 1; i >= 0; i--) { + if (board[i][x] == EMPTY) { + coordinate.x = i; + coordinate.y = x; + break; + } + } + return coordinate; +} + +int play_game(enum GAME_MODE game_mode, int rows, int cols) { + int** board = create_board(rows, cols); + if (board == NULL) { + fprintf(stderr, "Error: Unable to allocate memory for the board.\n"); + return EXIT_FAILURE; + } + + fill_board(rows, cols, board); + printf("Board size is %dx%d (rows x col)", rows, cols); + print_board(rows, cols, board); + + int player_selected_column, player = 0; + struct coordinate coordinate; + + while (true) { + if (game_mode == PLAYER_VS_PLAYER || player % 2 == 0) { + printf("\nColumn number? (starts at 1):"); + scanf("%d", &player_selected_column); + } else { + if (game_mode == PLAYER_VS_RANDOM_AI) { + player_selected_column = select_random_column(cols, rows, board); + } else if (game_mode == PLAYER_VS_SMART_AI) { + player_selected_column = select_smart_column(cols, rows, board); + } + } + + if (player_selected_column < 1 || player_selected_column > cols) { + continue; + } + + coordinate = find_empty_slot(player_selected_column - 1, rows, board); + place_token(player % 2, coordinate, board); + + print_board(rows, cols, board); + if (does_player_win(coordinate, rows, cols, board)) { + if (game_mode == PLAYER_VS_PLAYER) { + printf("\nPlayer %s won!\n", player % 2 == 0 ? "one" : "two"); + } else { + printf("\n%s won!\n", player % 2 == 0 ? "Player one" : "Computer"); + } + break; + } + if (player + 1 == rows * cols) { + printf("\nIt is a draw.\n"); + break; + } + + player++; + } + if (destroy_board(rows, board) == EXIT_FAILURE) { + fprintf(stderr, "Error: Unable to free memory for the board.\n"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +bool does_player_win(struct coordinate coordinate, int rows, int cols, int** board) { + if (coordinate.x < 0 || coordinate.y < 0) { + return false; + } + + int current_piece = board[coordinate.x][coordinate.y]; + for (int i = 0; i < rows - 3; i++) { + + if (board[i][coordinate.y] == current_piece + && board[i + 1][coordinate.y] == current_piece + && board[i + 2][coordinate.y] == current_piece + && board[i + 3][coordinate.y] == current_piece) + { + return true; + } + + } + + for (int i = 0; i < cols - 3; i++) { + if (board[coordinate.x][i] == current_piece + && board[coordinate.x][i + 1] == current_piece + && board[coordinate.x][i + 2] == current_piece + && board[coordinate.x][i + 3] == current_piece) { + return true; + } + } + + // Diagonale gauche à droite + for (int i = 0; i < rows - 3; i++) { + for (int j = 0; j < cols - 3; j++) { + if (board[i][j] == current_piece && + board[i + 1][j + 1] == current_piece && + board[i + 2][j + 2] == current_piece && + board[i + 3][j + 3] == current_piece) { + return true; + } + } + } + + // Diagonale droite à gauche + for (int i = 0; i < rows - 3; i++) { + for (int j = 3; j < cols; j++) { + if (board[i][j] == current_piece && + board[i + 1][j - 1] == current_piece && + board[i + 2][j - 2] == current_piece && + board[i + 3][j - 3] == current_piece) { + return true; + } + } + } + return false; +} \ No newline at end of file diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000000000000000000000000000000000000..f9dc934b3cfe154183c9bbd75e1d2d9bb178a2a6 --- /dev/null +++ b/src/game.h @@ -0,0 +1,23 @@ +#ifndef GAME_H +#define GAME_H + +#include <stdbool.h> +#include "board.h" + +enum GAME_MODE { + PLAYER_VS_RANDOM_AI = 1, + PLAYER_VS_SMART_AI = 2, + PLAYER_VS_PLAYER = 3 +}; + +struct coordinate { + int x; + int y; +}; + +int play_game(enum GAME_MODE game_mode, int rows, int cols); +struct coordinate find_empty_slot(int x, int rows, int** board); +bool does_player_win(struct coordinate coordinate, int rows, int cols, int** board); +void place_token(int player, struct coordinate coordinate, int** board); + +#endif