diff --git a/main.c b/main.c index 209af01f8fd18e3d0bc26d8dbe4943d1da6f2faf..801fe257e881c2b2581a5305d219429edd1bb61e 100644 --- a/main.c +++ b/main.c @@ -7,49 +7,36 @@ #include <stdlib.h> #include "puissance.h" -void play_against_random_ai(puissance game) { - int selected_col_index = -1; - - while(game.state == ONGOING) { - if (game.current_player == PLAYER_ONE) { - printf("\nColumn number? (starts at 1):"); - do { - scanf("%d", &selected_col_index); - selected_col_index -= 1; - } while (manual_play(&game, selected_col_index) == false); - } else { - random_play(&game); - } - print_game(game); - } +void print_help() { + printf("Usage: puissance4 <mode> <row> <col>\n"); + printf(" mode specifies the mode:\n"); + printf(" 1 = single player game (random),\n"); + printf(" 2 = single player game (AI),\n"); + printf(" 3 = two players game\n"); + printf(" row specifies the number of rows (>= 4)\n"); + printf(" col specifies the number of columns (>= 4)\n"); } -void play_against_smart_ai(puissance game) { +void play_game(puissance game) { int selected_col_index = -1; while(game.state == ONGOING) { - if (game.current_player == PLAYER_ONE) { + // First player action (always a human) + if (game.current_player == PLAYER_ONE || game.mode == TWO_PLAYERS) { printf("\nColumn number? (starts at 1):"); do { scanf("%d", &selected_col_index); selected_col_index -= 1; } while (manual_play(&game, selected_col_index) == false); - } else { - smart_play(&game); } - print_game(game); - } -} - -void play_against_human(puissance game) { - int selected_col_index = -1; - - while(game.state == ONGOING) { - do { - printf("\nColumn number? (starts at 1):"); - scanf("%d", &selected_col_index); - selected_col_index -= 1; - } while (manual_play(&game, selected_col_index) == false); + // Second player action (can be the computer) + else { + if (game.mode == RAND_AI) { + random_play(&game); + } else { + smart_play(&game); + }; + } print_game(game); } } @@ -58,32 +45,24 @@ int main(int argc, char *argv[]) { srand(0); puissance game; + // Get game arguments if (argc != 4) { - printf("The program must have 3 arguments.\n"); + print_help(); return EXIT_FAILURE; } - GameMode mode = atoi(argv[1]) - 1; - int nb_rows = atoi(argv[2]); - int nb_cols = atoi(argv[3]); + int rows = atoi(argv[2]); + int cols = atoi(argv[3]); - game_init(&game, mode, nb_rows, nb_cols); - printf("Board size is %dx%d (rows x col)", nb_rows, nb_cols); + // Initialize the game + game_init(&game, mode, rows, cols); + printf("Board size is %dx%d (rows x col)", rows, cols); print_game(game); - switch (mode) { - case RAND_AI: - play_against_random_ai(game); - break; - case SMART_AI: - play_against_smart_ai(game); - break; - case TWO_PLAYERS: - play_against_human(game); - break; - } + // Start playing + play_game(game); + // Free the memory and exit the program game_destroy(&game); - return EXIT_SUCCESS; } diff --git a/puissance.c b/puissance.c index 9026ba1109a0f037e7e63018f644bc540a96ca5f..4df082382b7bb4c259d796c920319b2c8da169c4 100644 --- a/puissance.c +++ b/puissance.c @@ -142,7 +142,6 @@ GameResult get_winning_player(puissance *p) { p->state = PLAYER_TWO_WIN; } return p->state; - } GameResult vertical_game_check(puissance *p, int last_col_index_played) { @@ -253,6 +252,10 @@ GameResult diagonal_game_check(puissance *p, int last_col_index_played) { if (diagonal_parse(p, 1, 1, last_row_index_played, last_col_index_played)) { return get_winning_player(p); } + // down left + if (diagonal_parse(p, 1, -1, last_row_index_played, last_col_index_played)) { + return get_winning_player(p); + } // up left if (diagonal_parse(p, -1, -1, last_row_index_played, last_col_index_played)) { return get_winning_player(p); @@ -261,10 +264,6 @@ GameResult diagonal_game_check(puissance *p, int last_col_index_played) { if (diagonal_parse(p, -1, 1, last_row_index_played, last_col_index_played)) { return get_winning_player(p); } - // down left - if (diagonal_parse(p, 1, -1, last_row_index_played, last_col_index_played)) { - return get_winning_player(p); - } return ONGOING; } @@ -274,12 +273,16 @@ GameResult verify_space_remaining(puissance *p) { return p->state; } bool space_available = false; + int top_row_index = 0; + + // Verify that at least one cell at the top row is empty. for (int i = 0; i < p->col; i++) { - if (p->data[0][i] == EMPTY_CELL_VALUE) { + if (p->data[top_row_index][i] == EMPTY_CELL_VALUE) { space_available = true; break; } } + // If no cells at top is empty, then the game can't continue, so it's a draw. if (space_available == false) { p->state = DRAW; } @@ -368,38 +371,42 @@ bool random_play(puissance *p) { return true; } -bool smart_play(puissance *p) { +bool search_optimal_action(puissance *p, bool simulate_player_one) { bool move_validated = false; - // Search for a wining action + + // Search the optimal action by simulating each possible action + // Not the must memory efficient but this method is easily implemented and should work without problems for (int i = 0; i < p->col; i++) { puissance copy = game_copy(p); + if (simulate_player_one) { + copy.current_player = PLAYER_ONE; + } if (manual_play(©, i)) { if (copy.state != ONGOING) { move_validated = manual_play(p, i); } } + game_destroy(©); if (move_validated) { return move_validated; } } + return move_validated; +} - // Try to prevent the user to win - for (int i = 0; i < p->col; i++) { - puissance copy = game_copy(p); - copy.current_player = PLAYER_ONE; +bool smart_play(puissance *p) { + // Search for a wining action + if (search_optimal_action(p, false)) { + return true; + } - if (manual_play(©, i)) { - if (copy.state != ONGOING) { - move_validated = manual_play(p, i); - } - } - game_destroy(©); - if (move_validated) { - return move_validated; - } + // Try to prevent the user to win + if (search_optimal_action(p, true)) { + return true; } + // If nothing has been done, do something random return random_play(p); }