From 811d4abe716c4d14629cee5beda1e1f3cff979bc Mon Sep 17 00:00:00 2001 From: =?utf8?q?J=C3=A9r=C3=B4me=20Benoit?= Date: Wed, 22 Mar 2017 23:24:34 +0100 Subject: [PATCH] TP_13 exo2: Finish the implementation of a basic tic-tac-toe game. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Next will be the AI and the play again feature. Signed-off-by: Jérôme Benoit --- .gitignore | 1 + README | 2 +- TP_13/exo1/lib/macros.h | 2 +- TP_13/exo2/Makefile | 1 + TP_13/exo2/README | 2 +- TP_13/exo2/lib/coordinates.c | 92 +++++++++++++++++++++++-- TP_13/exo2/lib/coordinates.h | 2 +- TP_13/exo2/lib/display.c | 26 ++++++- TP_13/exo2/lib/display.h | 6 +- TP_13/exo2/lib/macros.h | 2 +- TP_13/exo2/src/main.c | 130 +++++++++++++++++++++-------------- 11 files changed, 203 insertions(+), 63 deletions(-) diff --git a/.gitignore b/.gitignore index fe389eb..2882444 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +exo? *.static *.dynamic # for cygwin diff --git a/README b/README index d78fddc..80f8fbc 100644 --- a/README +++ b/README @@ -32,5 +32,5 @@ It's the directory exo_skel with a basic Makefile inside To use it: $ cp -a exo_skel TP_#/exo? (where # and ? are digits) $ cd TP_#/exo? && cp exo_skel.c exo?.c - Edit the Makefile to change the TARGET variable to the + Edit the Makefile to change the BINARY_NAME variable to the exercice name desired diff --git a/TP_13/exo1/lib/macros.h b/TP_13/exo1/lib/macros.h index 3c0ad02..f485a9a 100644 --- a/TP_13/exo1/lib/macros.h +++ b/TP_13/exo1/lib/macros.h @@ -29,4 +29,4 @@ /* FIXME: ensure we manipulate real array */ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) -#endif /* MACROS_H */ +#endif /* MACROS_H */ diff --git a/TP_13/exo2/Makefile b/TP_13/exo2/Makefile index 88bc97f..0a1ce92 100644 --- a/TP_13/exo2/Makefile +++ b/TP_13/exo2/Makefile @@ -79,6 +79,7 @@ GOLD_SUPPORT = no LDLIBS := $(filter-out -ltinfo,$(LDLIBS)) BINARY_SUFFIX := .exe DLIB_SUFFIX := .dll +# do not archive external libraries CYGWIN_LDLIBS_NOARCHIVE := $(LDLIBS) CYGWIN_LIBLDFLAGS := -Wl,--out-implib=$(LIBRARY_PATH)/$(DLIB_PREFIX)$(LIBRARY_NAME)$(DLIB_SUFFIX).a \ -Wl,--export-all-symbols \ diff --git a/TP_13/exo2/README b/TP_13/exo2/README index 10090fe..5d2d3c3 100644 --- a/TP_13/exo2/README +++ b/TP_13/exo2/README @@ -1,7 +1,7 @@ Tic-tac-toe ----------- -Touches correspondantes au case de la grille: +Touches correspondantes aux cases de la grille: a|z|e -+-+- diff --git a/TP_13/exo2/lib/coordinates.c b/TP_13/exo2/lib/coordinates.c index a9d8159..0a2d5e0 100644 --- a/TP_13/exo2/lib/coordinates.c +++ b/TP_13/exo2/lib/coordinates.c @@ -18,7 +18,10 @@ #include "coordinates.h" +#include + void init_coordinates(coordinates_t* coordinates_array) { + for (unsigned i = 0; i < MAX_COORDINATES; i++) { coordinates_array[i] = set_coordinates(0, 0, 0); } @@ -33,16 +36,21 @@ coordinates_t set_coordinates(int y, int x, unsigned type) { return new_coordinates; } +/* FIXME: Does it worth doing a coordinates_t get_coordinates(int y, int x, unsigned type); helper function? */ +/* Or a int get_coordinates_x(int y, int x, unsigned type); helper function? */ + /* the function do a lot of sanity checks before adding new board elements, * hence the loop. moving the checks in the main loop is also possible */ unsigned add_coordinates(coordinates_t new_coordinates, coordinates_t* coordinates_array, unsigned round) { + /* valid coordinates are in the [1-3] range */ if (new_coordinates.y < 1 || new_coordinates.y > 3 || new_coordinates.x < 1 || new_coordinates.x > 3) { return 3; /* error value for invalid coordinates */ - } else if (round == MAX_COORDINATES + 1) { + } else if (round == MAX_COORDINATES) { + /* round is off-by-one */ + coordinates_array[MAX_COORDINATES - 1] = new_coordinates; return 1; /* error value for full array */ } - for (unsigned i = 0; i < MAX_COORDINATES; i++) { /* check if already entered */ if (new_coordinates.y == (coordinates_array + i)->y && new_coordinates.x == (coordinates_array + i)->x) { @@ -55,9 +63,85 @@ unsigned add_coordinates(coordinates_t new_coordinates, coordinates_t* coordinat return 4; /* error value for unknown error case - should never happen - */ } -bool chk_win_conditions(coordinates_t* coordinates_array) { - for (unsigned i = 0; i < MAX_COORDINATES; i++) { +static bool chk_line(coordinates_t* coordinates_array, int line_number, unsigned round) { + unsigned nb_o_align = 0; + unsigned nb_x_align = 0; + + for (unsigned i = 0; i < round; i++) { + /* check if they are all the same */ + if ((coordinates_array + i)->y == line_number && (coordinates_array + i)->type == 0) { + nb_o_align++; + } + if ((coordinates_array + i)->y == line_number && (coordinates_array + i)->type == 1) { + nb_x_align++; + } + } + if (nb_o_align == 3 || nb_x_align == 3) { + return true; + } + return false; +} + +static bool chk_column(coordinates_t* coordinates_array, int column_number, unsigned round) { + unsigned nb_o_align = 0; + unsigned nb_x_align = 0; + for (unsigned i = 0; i < round; i++) { + /* check if they are all the same */ + if ((coordinates_array + i)->x == column_number && (coordinates_array + i)->type == 0) { + nb_o_align++; + } + if ((coordinates_array + i)->x == column_number && (coordinates_array + i)->type == 1) { + nb_x_align++; + } + } + /* one column must be full of the same type */ + if (nb_o_align == 3 || nb_x_align == 3) { + return true; + } + return false; +} + +static bool chk_diagonals(coordinates_t* coordinates_array, unsigned round) { + unsigned nb_o_diag_one = 0, nb_o_diag_two = 0; + unsigned nb_x_diag_one = 0, nb_x_diag_two = 0; + + for (unsigned i = 0; i < round; i++) { + /* dumb count of each elements type in the two diagonals */ + for (int y_x_diag = 1; y_x_diag < 4; y_x_diag++) { + if ((coordinates_array + i)->y == y_x_diag && (coordinates_array + i)->x == y_x_diag && (coordinates_array + i)->type == 0) { + nb_o_diag_one++; + } + if ((coordinates_array + i)->y == y_x_diag && (coordinates_array + i)->x == (4 - y_x_diag) && (coordinates_array + i)->type == 0) { + nb_o_diag_two++; + } + if ((coordinates_array + i)->y == y_x_diag && (coordinates_array + i)->x == y_x_diag && (coordinates_array + i)->type == 1) { + nb_x_diag_one++; + } + if ((coordinates_array + i)->y == y_x_diag && (coordinates_array + i)->x == (4 - y_x_diag) && (coordinates_array + i)->type == 1) { + nb_x_diag_two++; + } + } + if (nb_o_diag_one == 3 || nb_o_diag_two == 3 || nb_x_diag_one == 3 || nb_x_diag_two == 3) { + return true; + } + } + return false; +} + +bool chk_win_conditions(coordinates_t* coordinates_array, unsigned round) { + + /* winning conditions begin at round = 4 */ + if (round > 3) { + if (chk_diagonals(coordinates_array, round)) { + return true; + } + for (unsigned i = 1; i < 4; i++) { + if (chk_line(coordinates_array, i, round) || \ + chk_column(coordinates_array, i, round)) { + return true; + } + } } return false; } diff --git a/TP_13/exo2/lib/coordinates.h b/TP_13/exo2/lib/coordinates.h index 3a17ade..b396c3d 100644 --- a/TP_13/exo2/lib/coordinates.h +++ b/TP_13/exo2/lib/coordinates.h @@ -33,6 +33,6 @@ typedef struct coordinates_s { void init_coordinates(coordinates_t* coordinates_array); coordinates_t set_coordinates(int y, int x, unsigned type); unsigned add_coordinates(coordinates_t new_coordinates, coordinates_t* coordinates_array, unsigned round); -bool chk_win_conditions(coordinates_t* coordinates_array); +bool chk_win_conditions(coordinates_t* coordinates_array, unsigned round); #endif /* COORDINATES_H */ diff --git a/TP_13/exo2/lib/display.c b/TP_13/exo2/lib/display.c index 1e5def8..21d85bb 100644 --- a/TP_13/exo2/lib/display.c +++ b/TP_13/exo2/lib/display.c @@ -24,6 +24,7 @@ * which can be a space ' ' */ void print_board(int y, int x) { + mvprintw(y, x, " | |"); mvprintw(y+1, x, " | |"); mvprintw(y+2, x, "----+----+----"); @@ -47,12 +48,14 @@ void print_board(int y, int x) { * The added (y, x) couple values can be {0, 3, 6}x{1, 6, 11} */ -void print_x(int y, int x) { +static void print_x(int y, int x) { + mvprintw(y, x, "\\/"); mvprintw(y+1, x,"/\\"); } -void print_o(int y, int x) { +static void print_o(int y, int x) { + mvprintw(y, x, "/\\"); mvprintw(y+1, x, "\\/"); } @@ -61,6 +64,7 @@ void print_o(int y, int x) { * 2 -> +3 2 -> +6 * 3 -> +6 3 -> +11 */ static int remap_y(int y) { + if (y == 1) { return 0; } else if (y == 2) { @@ -71,6 +75,7 @@ static int remap_y(int y) { } static int remap_x(int x) { + if (x == 1) { return 1; } else if (x == 2) { @@ -82,6 +87,7 @@ static int remap_x(int x) { void print_coordinates(coordinates_t coordinates_array[], int base_y, int base_x) { unsigned i = 0; + while ((coordinates_array + i)->y != 0 && (coordinates_array + i)->x != 0) { if ((coordinates_array + i)->type == 0) { print_o(base_y + remap_y((coordinates_array + i)->y), base_x + remap_x((coordinates_array + i)->x)); @@ -91,3 +97,19 @@ void print_coordinates(coordinates_t coordinates_array[], int base_y, int base_x i++; } } + +/* void printf_result(unsigned player, unsigned round) { + char* result_msg = ""; + + if (round < MAX_COORDINATES + 1) { + if (player == 0) { + result_msg = "Joueur 1"; + } else { + result_msg = "Joueur 2"; + } + printf("%s gagne !\n", result_msg); + } else { + printf("Egalite !\n"); + } +} */ + diff --git a/TP_13/exo2/lib/display.h b/TP_13/exo2/lib/display.h index c3390fe..762c908 100644 --- a/TP_13/exo2/lib/display.h +++ b/TP_13/exo2/lib/display.h @@ -21,9 +21,11 @@ #include "coordinates.h" +/* ncurses printing */ void print_board(int y, int x); -void print_x(int y, int x); -void print_o(int y, int x); void print_coordinates(coordinates_t coordinates_array[], int base_y, int base_x); +/* non ncurses printing */ +/* void printf_result(unsigned player, unsigned round); */ + #endif /* DISPLAY_H */ diff --git a/TP_13/exo2/lib/macros.h b/TP_13/exo2/lib/macros.h index 3c0ad02..f485a9a 100644 --- a/TP_13/exo2/lib/macros.h +++ b/TP_13/exo2/lib/macros.h @@ -29,4 +29,4 @@ /* FIXME: ensure we manipulate real array */ #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0])) -#endif /* MACROS_H */ +#endif /* MACROS_H */ diff --git a/TP_13/exo2/src/main.c b/TP_13/exo2/src/main.c index 390bc40..8978ef2 100644 --- a/TP_13/exo2/src/main.c +++ b/TP_13/exo2/src/main.c @@ -8,9 +8,13 @@ int main() { int row, col, errno = 0, round = 0, player = 0, key_pressed; + bool winning_condition = false; + bool loop_exit_condition = false; const int str_max_length = 255; + /* FIXME: make a strings handling library */ char* top_msg = malloc(str_max_length * sizeof(char)); char* back_msg = malloc(str_max_length * sizeof(char)); + char* exit_msg = malloc(str_max_length * sizeof(char)); initscr(); getmaxyx(stdscr, row, col); @@ -43,69 +47,95 @@ int main() { print_coordinates(coordinates_array, base_y, base_x); - /* getch() is blocking */ - key_pressed = getch(); - switch (key_pressed) { - case 'a': - new_coordinates = set_coordinates(1, 1, player); - break; - case 'z': - new_coordinates = set_coordinates(1, 2, player); - break; - case 'e': - new_coordinates = set_coordinates(1, 3, player); - break; - case 'q': - new_coordinates = set_coordinates(2, 1, player); - break; - case 's': - new_coordinates = set_coordinates(2, 2, player); - break; - case 'd': - new_coordinates = set_coordinates(2, 3, player); - break; - case 'w': - new_coordinates = set_coordinates(3, 1, player); - break; - case 'x': - new_coordinates = set_coordinates(3, 2, player); - break; - case 'c': - new_coordinates = set_coordinates(3, 3, player); - break; - default: - /* set invalid coordinates */ - new_coordinates = set_coordinates(0, 0, player); - break; + /* FIXME: group the winning case code blocks */ + if (!winning_condition) { + /* getch() is blocking */ + key_pressed = getch(); + switch (key_pressed) { + case 'a': + new_coordinates = set_coordinates(1, 1, player); + break; + case 'z': + new_coordinates = set_coordinates(1, 2, player); + break; + case 'e': + new_coordinates = set_coordinates(1, 3, player); + break; + case 'q': + new_coordinates = set_coordinates(2, 1, player); + break; + case 's': + new_coordinates = set_coordinates(2, 2, player); + break; + case 'd': + new_coordinates = set_coordinates(2, 3, player); + break; + case 'w': + new_coordinates = set_coordinates(3, 1, player); + break; + case 'x': + new_coordinates = set_coordinates(3, 2, player); + break; + case 'c': + new_coordinates = set_coordinates(3, 3, player); + break; + default: + /* set invalid coordinates */ + new_coordinates = set_coordinates(0, 0, player); + break; + } + errno = add_coordinates(new_coordinates, coordinates_array, round); + winning_condition = chk_win_conditions(coordinates_array, round); } - errno = add_coordinates(new_coordinates, coordinates_array, round); - - if (errno == 2) { - back_msg = "Choisir une case vide"; - } else if (errno == 3) { - back_msg = "Coordonnees invalides"; - } else if (errno == 1) { - back_msg = "Tableau rempli sans gagnant: egalite"; - } else if (errno == 4) { - back_msg = "Erreur inconnue"; - } else if (errno == 0) { - /* FIXME: properly zero the string */ - back_msg = ""; + if (winning_condition) { + if (player == 0) { + back_msg = "Joureur 1 gagne !"; + } else { + back_msg = "Joureur 2 gagne !"; + } + } + + if (!winning_condition) { + if (errno == 2) { + back_msg = "Choisir une case vide"; + } else if (errno == 3) { + back_msg = "Coordonnees invalides"; + } else if (errno == 1) { + back_msg = "Tableau rempli sans gagnant: egalite"; + } else if (errno == 4) { + back_msg = "Erreur inconnue"; + } else if (errno == 0) { + /* FIXME: properly zero the string */ + back_msg = ""; + } } mvprintw(base_y + 10, (base_x + 7 - strlen(back_msg)/2), back_msg); + if (winning_condition || errno == 1) { + /* print the updated coordinates before exiting */ + print_coordinates(coordinates_array, base_y, base_x); + exit_msg = "Pressez une touche pour sortir"; + mvprintw(base_y + 12, (base_x + 7 - strlen(exit_msg)/2), exit_msg); + loop_exit_condition = true; + } + refresh(); - } while (errno != 1); + } while (!loop_exit_condition); + + /* getch() is blocking */ + getch(); + + endwin(); if (!top_msg) free(top_msg); if (!back_msg) free(back_msg); - - endwin(); + if (!exit_msg) + free(exit_msg); exit(EXIT_SUCCESS); } -- 2.34.1