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,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;
}
......@@ -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(&copy, i)) {
if (copy.state != ONGOING) {
move_validated = manual_play(p, i);
}
}
game_destroy(&copy);
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(&copy, i)) {
if (copy.state != ONGOING) {
move_validated = manual_play(p, i);
}
}
game_destroy(&copy);
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);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment