TP_13 exo2: Finish the implementation of a basic tic-tac-toe game.
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Wed, 22 Mar 2017 22:24:34 +0000 (23:24 +0100)
committerJérôme Benoit <jerome.benoit@piment-noir.org>
Wed, 22 Mar 2017 22:24:34 +0000 (23:24 +0100)
Next will be the AI and the play again feature.

Signed-off-by: Jérôme Benoit <jerome.benoit@piment-noir.org>
.gitignore
README
TP_13/exo1/lib/macros.h
TP_13/exo2/Makefile
TP_13/exo2/README
TP_13/exo2/lib/coordinates.c
TP_13/exo2/lib/coordinates.h
TP_13/exo2/lib/display.c
TP_13/exo2/lib/display.h
TP_13/exo2/lib/macros.h
TP_13/exo2/src/main.c

index fe389eb8aac9e42272b20c6e612d2dd74bd5ba3d..2882444788bbaa4f0d1fbba82882534d9c5bb954 100644 (file)
@@ -1,3 +1,4 @@
+exo?
 *.static
 *.dynamic
 # for cygwin  
diff --git a/README b/README
index d78fddcfd8e28141dffb51f7fc38c308bc070b49..80f8fbc5e34d6ee47591676614bae578c1d08adf 100644 (file)
--- 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
index 3c0ad027e55f85b5acb0e83d8d64676e9e39655e..f485a9afabfb96f282b9cccd17ffe55ed49af65a 100644 (file)
@@ -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 */
index 88bc97f74f9aeab370e6a1a634a800c94d822eb0..0a1ce9293f402faa6697ac09bfa2530f7d064bff 100644 (file)
@@ -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 \
index 10090fefdaff90ca7182724c3a44be0c9a5eb1b5..5d2d3c3f4aebc81fd3280bff99fa75c49bcb068f 100644 (file)
@@ -1,7 +1,7 @@
 Tic-tac-toe
 -----------
 
-Touches correspondantes au case de la grille:
+Touches correspondantes aux cases de la grille:
 
 a|z|e
 -+-+-
index a9d8159a3584af3fc321d4857d4f73d558d80267..0a2d5e028c406579b8da3c3509727463fee1710e 100644 (file)
 
 #include "coordinates.h"
 
+#include <ncurses.h>
+
 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;
 }
index 3a17ade5a608a152f621d6a7fefa15b3915f9e61..b396c3d890c92896336d04c5b8cd41613090dfbc 100644 (file)
@@ -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 */
index 1e5def8f66dc154056b67e1e4defcc6661ee732a..21d85bb84cbece58dff339b217de62212d6cfb3a 100644 (file)
@@ -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");
+    }
+} */
+
index c3390fe2ca95a77622bebb9318f6d14d97926e2a..762c908e5ca5cc9110ad49912a267ba3f313d50d 100644 (file)
 
 #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 */
index 3c0ad027e55f85b5acb0e83d8d64676e9e39655e..f485a9afabfb96f282b9cccd17ffe55ed49af65a 100644 (file)
@@ -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 */
index 390bc40005122a294846808868852871354b1235..8978ef2eee6ed33ce70c14b70a4f956e428769c7 100644 (file)
@@ -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);
 }