Skip to content
Snippets Groups Projects
Commit 8016548c authored by dario.genga's avatar dario.genga
Browse files

Refactor the code

- Created the search_optimal_action method to avoid duplicated code in
the smart_play method.
- Added a print_help method to help the user when using the program.
- Simplified the main.c file in order to use only one method when
playing instead of three.
- Added some comments.
- Performed various small adjustement for more code clarity.
parent 28015cae
Branches
No related tags found
No related merge requests found
...@@ -7,83 +7,62 @@ ...@@ -7,83 +7,62 @@
#include <stdlib.h> #include <stdlib.h>
#include "puissance.h" #include "puissance.h"
void play_against_random_ai(puissance game) { void print_help() {
int selected_col_index = -1; printf("Usage: puissance4 <mode> <row> <col>\n");
printf(" mode specifies the mode:\n");
while(game.state == ONGOING) { printf(" 1 = single player game (random),\n");
if (game.current_player == PLAYER_ONE) { printf(" 2 = single player game (AI),\n");
printf("\nColumn number? (starts at 1):"); printf(" 3 = two players game\n");
do { printf(" row specifies the number of rows (>= 4)\n");
scanf("%d", &selected_col_index); printf(" col specifies the number of columns (>= 4)\n");
selected_col_index -= 1;
} while (manual_play(&game, selected_col_index) == false);
} else {
random_play(&game);
}
print_game(game);
}
} }
void play_against_smart_ai(puissance game) { void play_game(puissance game) {
int selected_col_index = -1; int selected_col_index = -1;
while(game.state == ONGOING) { 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):"); printf("\nColumn number? (starts at 1):");
do { do {
scanf("%d", &selected_col_index); scanf("%d", &selected_col_index);
selected_col_index -= 1; selected_col_index -= 1;
} while (manual_play(&game, selected_col_index) == false); } 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 { } else {
smart_play(&game); smart_play(&game);
};
} }
print_game(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);
print_game(game);
}
}
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
srand(0); srand(0);
puissance game; puissance game;
// Get game arguments
if (argc != 4) { if (argc != 4) {
printf("The program must have 3 arguments.\n"); print_help();
return EXIT_FAILURE; return EXIT_FAILURE;
} }
GameMode mode = atoi(argv[1]) - 1; GameMode mode = atoi(argv[1]) - 1;
int nb_rows = atoi(argv[2]); int rows = atoi(argv[2]);
int nb_cols = atoi(argv[3]); int cols = atoi(argv[3]);
game_init(&game, mode, nb_rows, nb_cols); // Initialize the game
printf("Board size is %dx%d (rows x col)", nb_rows, nb_cols); game_init(&game, mode, rows, cols);
printf("Board size is %dx%d (rows x col)", rows, cols);
print_game(game); print_game(game);
switch (mode) { // Start playing
case RAND_AI: play_game(game);
play_against_random_ai(game);
break;
case SMART_AI:
play_against_smart_ai(game);
break;
case TWO_PLAYERS:
play_against_human(game);
break;
}
// Free the memory and exit the program
game_destroy(&game); game_destroy(&game);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
...@@ -142,7 +142,6 @@ GameResult get_winning_player(puissance *p) { ...@@ -142,7 +142,6 @@ GameResult get_winning_player(puissance *p) {
p->state = PLAYER_TWO_WIN; p->state = PLAYER_TWO_WIN;
} }
return p->state; return p->state;
} }
GameResult vertical_game_check(puissance *p, int last_col_index_played) { 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) { ...@@ -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)) { if (diagonal_parse(p, 1, 1, last_row_index_played, last_col_index_played)) {
return get_winning_player(p); 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 // up left
if (diagonal_parse(p, -1, -1, last_row_index_played, last_col_index_played)) { if (diagonal_parse(p, -1, -1, last_row_index_played, last_col_index_played)) {
return get_winning_player(p); return get_winning_player(p);
...@@ -261,10 +264,6 @@ GameResult diagonal_game_check(puissance *p, int last_col_index_played) { ...@@ -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)) { if (diagonal_parse(p, -1, 1, last_row_index_played, last_col_index_played)) {
return get_winning_player(p); 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; return ONGOING;
} }
...@@ -274,12 +273,16 @@ GameResult verify_space_remaining(puissance *p) { ...@@ -274,12 +273,16 @@ GameResult verify_space_remaining(puissance *p) {
return p->state; return p->state;
} }
bool space_available = false; 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++) { 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; space_available = true;
break; break;
} }
} }
// If no cells at top is empty, then the game can't continue, so it's a draw.
if (space_available == false) { if (space_available == false) {
p->state = DRAW; p->state = DRAW;
} }
...@@ -368,38 +371,42 @@ bool random_play(puissance *p) { ...@@ -368,38 +371,42 @@ bool random_play(puissance *p) {
return true; return true;
} }
bool smart_play(puissance *p) { bool search_optimal_action(puissance *p, bool simulate_player_one) {
bool move_validated = false; 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++) { for (int i = 0; i < p->col; i++) {
puissance copy = game_copy(p); puissance copy = game_copy(p);
if (simulate_player_one) {
copy.current_player = PLAYER_ONE;
}
if (manual_play(&copy, i)) { if (manual_play(&copy, i)) {
if (copy.state != ONGOING) { if (copy.state != ONGOING) {
move_validated = manual_play(p, i); move_validated = manual_play(p, i);
} }
} }
game_destroy(&copy); game_destroy(&copy);
if (move_validated) { 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;
if (manual_play(&copy, i)) {
if (copy.state != ONGOING) {
move_validated = manual_play(p, i);
}
}
game_destroy(&copy);
if (move_validated) {
return move_validated; return move_validated;
} }
bool smart_play(puissance *p) {
// Search for a wining action
if (search_optimal_action(p, false)) {
return true;
} }
// Try to prevent the user to win
if (search_optimal_action(p, true)) {
return true;
}
// If nothing has been done, do something random // If nothing has been done, do something random
return random_play(p); return random_play(p);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment