# I- Présentation du problème

##A) Les entrées

On nous fournit:
- deux entiers `w` et `h` pour respectivement le nombre de colonne et le nombre de ligne;
- `h` lignes de `w` colonnes;

A noter que `w` colonnes est à comprendre comme `w` cases car finalement on peut avoir plusieurs caractères dans une case sans compter les espaces.

##B) But du jeu

On dispose d'une grille de taille `w*h` qui contient les informations suivantes:
- les numéros déjà présents;
- les "indices" des cages sous forme de lettres.

Deux cases font parties de la même case si elles possèdent la même lettre et qu'elles sont collées verticalement ou horizontalement. Dans une grille de suguru 2 chiffres ne peuevent pas se retrouver à coté que ce soit dans les angles, en haut, à droite, en bas, à gauche. De plus dans une cage d'une grille de suguru il ne peut pas y avoir 2 fois le même chiffres. Ici la taille maximale des cages est 6, cela veut dire qu'une cage de 6 cases contient les chiffres de 1 à 6, une cage de 5 de 1 à 5 et ainsi de suite.

Avec ces règles ci dessus, nous devons implémenter un programme en C qui permet de résoudre une grille de suguru.

##C) La sortie

Il faut retourner la grille complétée en entière tout en respectant les règles du suguru.

# II- Ma méthode de résolution

##A) Approche synoptique

Pour résoudre ce problème, j'ai réfléchi d'une manière différente à comment je le résolverai à la main. En effet à la main on marque au crayon à papier tous les chiffres par cases puis on les élimine un à un suivant s'ils sont côté à côte, etc... Malheureusement, arrivé à un certain niveau de difficulté, il faut apprendre des techniques très sofistiqué qui demanderait un trop grand travail de codage qui serait source d'erreurs car dans toutes les cases il y aurait plusieurs possibilités. Pour ce programme j'ai donc réfléchi de la manière suivante: si une case n'a qu'une possibilité, j'inscrit ce numéro dans la case, mais s'il y a plusieurs possibilités je teste une à une les valeurs récursivement. 

##B) Approche algorithmique et explication de mon programme

Pour commencer, je commence par implémenter une structure qui me permettra par la suite de sauvegarder mes choix lorsque dans une case j'aurai plusieurs possibilité, je stocke donc l'indice de la case, les valeurs testé ainsi que le nombre de valeurs déjà testé.

Dans le suguru, l'un des éléments principal est la "séparation" des cages, en effet il faut connaitre où sont les cages, leur forme, le nombre de case qu'une cage possède, etc... J'ai donc implémenter la fonction `creer_cages_identifiants` qui permet de remplir un tableau 2 dimensions qui regroupe la grille entière avec des numéros par cage à chaque indice. Si la case à l'indice 0,0 fait partie de la case 1, je vais écrire 1 dans le tableau à cette indice pour le savoir. Cette fonction parcourt donc la grille de base qui ne contient que les lettres (cette opértion de séparation des numéros et des lettres s'effectue dans le main avant l'appel de cette fonction) pour remplir petit à petit le tableau `cage_identifiant`.

Une fois cette fonction exécuté, il m'est utile et nécessaire pour la suite d'avoir un tableau qui regroupe les cages, mais cette fois, non pas grâce à une grille mais grâce ua numéro des cages. A l'indice 0 du nouveau tableau je possèderais tout les indices qui appartiennet à la cage numéro 0. Pour ce faire, j'ai créé une fonction `classifier_indices_par_cages` qui permet de remplir un tableau pour contenir les informations que j'ai décrite plus tôt. De plus je stocke aussi dans un tableau une dimension cette fois la taille des cages, cette information me servira notamment dans la fonction suivante qui permet de faire  la liste des possibilités dans une case donnée.

Cette fonction est donc la fonction résoudre qui permet de remplir un tableau avec toutes les possibilités de chiffres dans une case, j'ai préféré cette solution plutot que stocker toutes les possibilités de toutes les cases dans un tableau car cela aurait été ingérable pour la gestion des valeurs qui changent et de la récursion.

La fonction principal est la fonction résoudre qui va permettre de remplir la grille et donc résoudre le suguru. Il y a plusieurs cas différents:
- Toutes les cases de la grille sont remplies, on sort donc de la fonction;
- Il y a une seule possibilité dans la case actuel, on inscrit donc cette valeur dans la case correspondante et on rappelle la fonction avec un indice jamais visité qui est adjacent (pour maximiser les chances d'avoir le moins de possibilité du fait qu'elle soit collé à des cases déjà rempli);
- Il y a plusieurs possibilités, dans ce cas la on choisit une valeur que l'on marque dans la grille, et on ajoute au tableau des choix, la décision que l'on vient de prendre;
- Il n'y a aucune possibilité, cela veut dire que la grille ne peut pas être complété or elle le devrait, on va donc revenir à la derniere decision et essayer une nouvelle valeur, si on avait tou essayé on remonte à la décision encore d'avant pour la changer, et ce jusqu'à trouver la grille complète valide.

Concernant le main, je récupère les entrées au début, je sépare au même moment les lettres et les numéros, puis j'effectue mes différents appels de fonctions qui permettent de résoudre la grille, je finis par afficher la grille complète.

Je n'ai pas mentionné mes fonctions d'affichages car elles ne sont la que pour vérifier si les différents tableaux sont bien rempli.

A la fin du main, je n'oublie pas de libérer la mémoire alloué tout au long de la résolution de la grille.

```c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

typedef int cpt;
void afficher_grille(int **grille, int h, int w);

//############################################ Strucuture pour les choix au fur et à mesure de la résolution ############################################
typedef struct {
    int indice[2];
    int tab[6];
    int nombre;
} choix;

//############################################ Déplacements: haut, bas, gauche, droite ############################################
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};

//############################################ Détection des cages et attribution d'identifiants uniques ############################################
void creer_cages_identifiants(char **cage, int h, int w, int **cage_identifiant) {
    int prochain_id = 0;
    cpt i,j;
    for (i = 0; i < h; i++) {
        for (j = 0; j < w; j++) {
            cage_identifiant[i][j] = -1;
        }
    }
    cpt a,b,c,d,e;
    for (a = 0; a < h; a++) {
        for (b = 0; b < w; b++) {
            if (cage_identifiant[a][b] == -1) {  
                char cage_type = cage[a][b];
                cage_identifiant[a][b] = prochain_id;

                int **queue = malloc(h * w * sizeof(int*));
                for (c = 0; c < h * w; c++) {
                    queue[c] = malloc(2 * sizeof(int));
                }

                int front = 0, rear = 0;
                queue[rear][0] = a;
                queue[rear++][1] = b;

                while (front < rear) {
                    int x = queue[front][0], y = queue[front++][1];

                    for (int d = 0; d < 4; d++) {
                        int nx = x + dx[d], ny = y + dy[d];

                        if (nx >= 0 && nx < h && ny >= 0 && ny < w &&
                            cage_identifiant[nx][ny] == -1 && cage[nx][ny] == cage_type) {
                            cage_identifiant[nx][ny] = prochain_id;
                            queue[rear][0] = nx;
                            queue[rear++][1] = ny;
                        }
                    }
                }

                for (e = 0; e < h * w; e++) {
                    free(queue[e]);
                }
                free(queue);

                prochain_id++;
            }
        }
    }
}

//############################################ Classification des indices par cages ############################################
void classifier_indices_par_cages(int **cage_identifiant, int h, int w, int ****tab, int *nombre_cages, int **cage_sizes) {
    int max_cage_id = 0;
    cpt i,j;
    for (i = 0; i < h; i++) {
        for (j = 0; j < w; j++) {
            if (cage_identifiant[i][j] > max_cage_id) {
                max_cage_id = cage_identifiant[i][j];
            }
        }
    }
    *nombre_cages = max_cage_id + 1;

    *cage_sizes = malloc((*nombre_cages) * sizeof(int));
    cpt k;
    for (k = 0; k < *nombre_cages; k++) {
        (*cage_sizes)[k] = 0;
    }
    cpt l,m;
    for (l = 0; l < h; l++) {
        for (m = 0; m < w; m++) {
            int cage_id = cage_identifiant[l][m];
            (*cage_sizes)[cage_id]++;
        }
    }

    *tab = malloc((*nombre_cages) * sizeof(int**));
    cpt n,o;
    for (n = 0; n < *nombre_cages; n++) {
        (*tab)[n] = malloc((*cage_sizes)[n] * sizeof(int*));
        for (o = 0; o < (*cage_sizes)[n]; o++) {
            (*tab)[n][o] = malloc(2 * sizeof(int)); 
        }
    }

    int *cage_counter = malloc(*nombre_cages * sizeof(int));
    cpt p;
    for (p = 0; p < *nombre_cages; p++) {
        cage_counter[p] = 0;
    }
    cpt q,r;
    for (q = 0; q < h; q++) {
        for (r = 0; r < w; r++) {
            int cage_id = cage_identifiant[q][r];
            (*tab)[cage_id][cage_counter[cage_id]][0] = q;
            (*tab)[cage_id][cage_counter[cage_id]][1] = r;
            cage_counter[cage_id]++;
        }
    }

    free(cage_counter);
}

//############################################## Possibilité de remplissage pour une case donnée ##############################################
void possible(int ligne, int colonne, int x, int y, int cage[], int** grille, int*** tab, int** cage_identifiant, int possibilite[]) {
    if (grille[x][y] == 0) {
        cpt i;
        for (i = 0; i < 6; i++) {
            possibilite[i] = i + 1;
        }
        cpt j, k;
        for (j = -1; j < 2; j++) {
            for (k = -1; k < 2; k++) {
                if(x + j < ligne && y + k < colonne && x+j>-1 && y+k>-1){
                    int val = grille[x + j][y + k];
                    if (val < 7) {
                        possibilite[val - 1] = 0;
                    }
                }
            }
        }
        int tailleCage = cage[cage_identifiant[x][y]];
        cpt l;
        for (l = tailleCage; l < 6; l++) {
            possibilite[l] = 0;
        }
        cpt m;
        for (m = 0; m < tailleCage; m++) {
            int a = tab[cage_identifiant[x][y]][m][0];
            int b = tab[cage_identifiant[x][y]][m][1];
            int val = grille[a][b];
            if (val < 7) {
                possibilite[val - 1] = 0;
            }
        }
    } 
    else {
        cpt i;
        for (i = 0; i < 6; i++) {
            possibilite[i] = 0;
        }
    }
}

//############################################ Résolution et remplissage de la grille de suguru ############################################
int resoudre(int ligne, int colonne, int x, int y, choix choisi[], int resolu[][2], int cage[], int** grille, int*** tab, int** cage_identifiant, int numero, int caseRempli, int decision) {
    if (caseRempli == ligne * colonne) {
        return 0;
    }
    int possibilite[6]={0};
    possible(ligne, colonne, x, y, cage, grille,tab,cage_identifiant,possibilite);
    int nombrePossibilite = 0;
    int valeur;
    cpt i;
    for (i = 0; i < 6; i++) {
        if (possibilite[i] != 0) {
            nombrePossibilite++;
            valeur = possibilite[i];
        }
    }
    if (nombrePossibilite == 0) {
        cpt i;
        for(i=numero-1;resolu[i][0]!=choisi[decision-1].indice[0] && resolu[i][1]!=choisi[decision-1].indice[1];i--){
            grille[resolu[i][0]][resolu[i][1]]=0;
            caseRempli--;
        }
        int nouvellePossibilite[6]={0};
        possible(ligne, colonne, choisi[decision-1].indice[0], choisi[decision-1].indice[1], cage, grille,tab,cage_identifiant,nouvellePossibilite);
        int nouveauNombrePossibilite = 0;
        cpt j;
        for (j = 0; j < 6; j++) {
            if (nouvellePossibilite[j] != 0) {
                nouveauNombrePossibilite++;
            }
        }
        if(choisi[decision-1].nombre!=nouveauNombrePossibilite){
            int nouvelleValeur=0;
            cpt j,k;
            for(j=0;j<6 && nouvelleValeur==0;j++){
                if(nouvellePossibilite[j]!=0){
                    int trouve=0;
                    for(k=0;k<choisi[decision-1].nombre && trouve==0;k++){
                        if(choisi[decision-1].tab[k]==nouvellePossibilite[j]){
                            trouve=1;
                        }
                    }
                    if(trouve==0){
                        nouvelleValeur=nouvellePossibilite[j];
                    }
                }
            }
            grille[choisi[decision-1].indice[0]][choisi[decision-1].indice[1]]=nouvelleValeur;
            choisi[decision-1].tab[choisi[decision-1].nombre]=nouvelleValeur;
            choisi[decision-1].nombre++;
            cpt l;
            int a, b;
            int x=choisi[decision-1].indice[0],y=choisi[decision-1].indice[1];
            int trouve = 0;
            for (l = 0; l < cage[cage_identifiant[x][y]] && trouve == 0; l++) {
                int tmp_x = tab[cage_identifiant[x][y]][l][0];
                int tmp_y = tab[cage_identifiant[x][y]][l][1];
                if (grille[tmp_x][tmp_y] == 0) {
                    a = tmp_x;
                    b = tmp_y;
                    trouve = 1;
                }
            }
            if (trouve == 0) {
                cpt k, l, m;
                for (k = 0; k < 20 && trouve == 0; k++) {
                    for (l = -1 - k; l < 1 + k && trouve == 0; l++) {
                        for (m = -1 - k; m < 1 + k && trouve == 0; m++) {
                            if (x + l < ligne && y + m < colonne && x+l>-1 && y+m>-1 && grille[x + l][y + m] == 0) {
                                a = x + l;
                                b = y + m;
                                trouve = 1;
                            }
                        }
                    }
                }
            }
            resoudre(ligne, colonne, a, b, choisi, resolu, cage, grille, tab, cage_identifiant, i+1, caseRempli, decision);
        }
        else{
            int nouvellePossibilite[6]={0};
            possible(ligne, colonne, choisi[decision-2].indice[0], choisi[decision-2].indice[1], cage, grille,tab,cage_identifiant,nouvellePossibilite);
            int nouveauNombrePossibilite = 0;
            cpt j;
            for (j = 0; j < 6; j++) {
                if (nouvellePossibilite[j] != 0) {
                    nouveauNombrePossibilite++;
                }
            }
            cpt k=i-1;
            j=2;
            while(nouveauNombrePossibilite==choisi[decision-j].nombre){
                for(;resolu[k][0]!=choisi[decision-j].indice[0] && resolu[k][1]!=choisi[decision-j].indice[1];k--){
                    grille[resolu[k][0]][resolu[k][1]]=0;
                    caseRempli--;
                }
                choisi[decision-j].nombre=0;
                choisi[decision-j].indice[0]=0;
                choisi[decision-j].indice[1]=0;
                cpt m;
                for(m=0;choisi[decision-j].tab[m]!=0;m++){
                    choisi[decision-j].tab[m]=0;
                }
                j++;
                int nouvellePossibilite[6]={0};
                possible(ligne, colonne, choisi[decision-j].indice[0], choisi[decision-j].indice[1], cage, grille,tab,cage_identifiant,nouvellePossibilite);
                int nouveauNombrePossibilite = 0;
                cpt l;
                for (l = 0; l < 6; l++) {
                    if (nouvellePossibilite[l] != 0) {
                        nouveauNombrePossibilite++;
                    }
                }
            }
            int nouvelleValeur=0;
            cpt m,n;
            for(m=0;m<6 && nouvelleValeur==0;m++){
                if(nouvellePossibilite[m]!=0){
                    int trouve=0;
                    for(n=0;n<choisi[decision-j+1].nombre && trouve==0;n++){
                        if(choisi[decision-j+1].tab[n]==nouvellePossibilite[m]){
                            trouve=1;
                        }
                    }
                    if(trouve==0){
                        nouvelleValeur=nouvellePossibilite[m];
                    }
                }
            }
            grille[choisi[decision-j+1].indice[0]][choisi[decision-j+1].indice[1]]=nouvelleValeur;
            choisi[decision-j+1].tab[choisi[decision-j+1].nombre]=nouvelleValeur;
            choisi[decision-j+1].nombre++;
            cpt l;
            int a, b;
            int x=choisi[decision-j+1].indice[0],y=choisi[decision-j+1].indice[1];
            int trouve = 0;
            for (l = 0; l < cage[cage_identifiant[x][y]] && trouve == 0; l++) {
                int tmp_x = tab[cage_identifiant[x][y]][l][0];
                int tmp_y = tab[cage_identifiant[x][y]][l][1];
                if (grille[tmp_x][tmp_y] == 0) {
                    a = tmp_x;
                    b = tmp_y;
                    trouve = 1;
                }
            }
            if (trouve == 0) {
                cpt k, l, m;
                for (k = 0; k < 20 && trouve == 0; k++) {
                    for (l = -1 - k; l < 1 + k && trouve == 0; l++) {
                        for (m = -1 - k; m < 1 + k && trouve == 0; m++) {
                            if (x + l < ligne && y + m < colonne && x+l>-1 && y+m>-1 && grille[x + l][y + m] == 0) {
                                a = x + l;
                                b = y + m;
                                trouve = 1;
                            }
                        }
                    }
                }
            }
            resoudre(ligne, colonne, a, b, choisi, resolu, cage, grille, tab, cage_identifiant, k+1, caseRempli, decision-j+1);
        }
    }
    else if (nombrePossibilite == 1) {
        grille[x][y] = valeur;
        resolu[numero][0]=x;
        resolu[numero][1]=y;
        cpt j;
        int a, b;
        int trouve = 0;
        for (j = 0; j < cage[cage_identifiant[x][y]] && trouve == 0; j++) {
            int tmp_x = tab[cage_identifiant[x][y]][j][0];
            int tmp_y = tab[cage_identifiant[x][y]][j][1];
            if (grille[tmp_x][tmp_y] == 0) {
                a = tmp_x;
                b = tmp_y;
                trouve = 1;
            }
        }
        if (trouve == 0) {
            cpt k, l, m;
            for (k = 0; k < 20 && trouve == 0; k++) {
                for (l = -1 - k; l < 1 + k && trouve == 0; l++) {
                    for (m = -1 - k; m < 1 + k && trouve == 0; m++) {
                        if (x + l < ligne && y + m < colonne && x+l>-1 && y+m>-1 && grille[x + l][y + m] == 0) {
                            a = x + l;
                            b = y + m;
                            trouve = 1;
                        }
                    }
                }
            }
        }
        resoudre(ligne, colonne, a, b, choisi, resolu, cage, grille, tab, cage_identifiant, numero + 1, caseRempli + 1, decision);
    } 
    else {
        grille[x][y] = valeur;
        resolu[numero][0]=x;
        resolu[numero][1]=y;
        choisi[decision].tab[0] = valeur;
        choisi[decision].tab[1] = 0;
        choisi[decision].indice[0] = x;
        choisi[decision].indice[1] = y;
        choisi[decision].nombre=1;
        cpt j;
        int a, b;
        int trouve = 0;
        for (j = 0; j < cage[cage_identifiant[x][y]] && trouve == 0; j++) {
            int tmp_x = tab[cage_identifiant[x][y]][j][0];
            int tmp_y = tab[cage_identifiant[x][y]][j][1];
            if (grille[tmp_x][tmp_y] == 0) {
                a = tmp_x;
                b = tmp_y;
                trouve = 1;
            }
        }
        if (trouve == 0) {
            cpt k, l, m;
            for (k = 0; k < 20 && trouve == 0; k++) {
                for (l = -1 - k; l < 1 + k && trouve == 0; l++) {
                    for (m = -1 - k; m < 1 + k && trouve == 0; m++) {
                        if (x + l < ligne && y + m < colonne && x+l>-1 && y+m>-1 && grille[x + l][y + m] == 0) {
                            a = x + l;
                            b = y + m;
                            trouve = 1;
                        }
                    }
                }
            }
        }
        resoudre(ligne, colonne, a, b, choisi, resolu, cage, grille, tab, cage_identifiant, numero + 1, caseRempli + 1, decision + 1);
    }
    return 0;
}

//############################################ Affichage de la grille ############################################
void afficher_grille(int **grille, int h, int w){
    cpt i,j;
    for(i=0;i<h;i++){
        for(j=0;j<w;j++){
            printf("%d",grille[i][j]);
        }
        printf("\n");
    }
}

//############################################ Affichage des indices classés par cage ############################################
void afficher_indices_par_cages(int ***tab, int nombre_cages, int *cage_sizes) {
    printf("\nIndices classés par cages :\n");
    cpt i,j;
    for (i = 0; i < nombre_cages; i++) {
        printf("Cage %d:\n", i);
        for (j = 0; j < cage_sizes[i]; j++) {
            printf("  (%d, %d)\n", tab[i][j][0], tab[i][j][1]);
        }
    }
}

//############################################ Affichage des identifiants de cage ############################################
void afficher_cages(int **cage_identifiant, int h, int w) {
    printf("\nIdentifiants des cages :\n");
    cpt i,j;
    for (i = 0; i < h; i++) {
        for (j = 0; j < w; j++) {
            printf("%d ", cage_identifiant[i][j]);
        }
        printf("\n");
    }
}

int main() {
    int w, h;                                                //Dimension de la grille
    scanf("%d%d", &w, &h); fgetc(stdin);
    
//############################################ Création des tableaux à 2 dimensions ############################################
    
    char **cage = malloc(sizeof(char*) * h);                 //Contient les lettres des cages de la grille de suguru
    int **cage_identifiant = malloc(sizeof(int*) * h);       //Contient les numéros des cages de la grille de suguru
    int ** grille=malloc(sizeof(int*)*h);                    //Contient la grille de suguru
    cpt i;
    for (i = 0; i < h; i++) {
        cage[i] = malloc(sizeof(char) * w);
        cage_identifiant[i] = malloc(sizeof(int) * w);
        grille[i]=malloc(sizeof(int)*w);
    }

//############################################ Remplissage des tableaux à 2 dimensions ############################################
    cpt j,k;
    for (j = 0; j < h; j++) {
        char line[60];
        scanf("%[^\n]", line); fgetc(stdin);
        for (k= 0; k < w * 3; k += 3) {
            cage[j][k / 3] = line[k];
            if(line[k+1]!='.'){
                grille[j][k/3]=line[k+1]-'0';
            }
            else{
                grille[j][k/3]=0;
            }
        }
    }

//############################################ Création et remplissage des tableaux concernant les cages ############################################

    creer_cages_identifiants(cage, h, w, cage_identifiant);
    int ***tab;
    int nombre_cages;
    int *cage_sizes;
    classifier_indices_par_cages(cage_identifiant, h, w, &tab, &nombre_cages, &cage_sizes);

//############################################ Affichages ############################################
    
    /*afficher_grille(grille,h,w);
    afficher_cages(cage_identifiant, h, w);
    afficher_indices_par_cages(tab, nombre_cages, cage_sizes);*/

//############################################ Test appel à la fonction possible ############################################

    /*int possibilite[6]={0};
    possible(h,w,1,0,cage_sizes,grille,tab,cage_identifiant,possibilite);

    cpt l;
    printf("Les différents chiffres possibles pour l'indice (1,0): ");
    for(l=0;l<6;l++){
        printf("%d, ",possibilite[i]);
    }
    puts("");*/

//############################################ Résolution du suguru ############################################

    choix decision[100];
    int casePrecedente[400][2];
    int caseRempli=0;
    cpt m,n;
    for(m=0;m<h;m++){
        for(n=0;n<w;n++){
            if(grille[m][n]!=0){
                caseRempli++;
            }
        }
    }
    resoudre(h,w,0,0,decision,casePrecedente,cage_sizes,grille,tab,cage_identifiant,0,caseRempli,0);
    afficher_grille(grille,h,w);

//############################################ Libération de la mémoire allouée ############################################

    cpt o,p;
    for (o = 0; o < nombre_cages; o++) {
        for (p = 0; p < cage_sizes[o]; p++) {
            free(tab[o][p]);
        }
        free(tab[o]);
    }
    free(tab);
    free(cage_sizes);
    cpt q;
    for (q = 0; q < h; q++) {
        free(cage[q]);
        free(cage_identifiant[q]);
        free(grille[q]);
    }
    free(cage);
    free(cage_identifiant);
    free(grille);

    return 0;
}
```

# III- Debriefing

J'ai pris un peu de retard petit à petit à partir de markov text generation, c'est donc pour cela que suguru est le dernier problème que j'ai traité (cryptarithm a été fait durant la même période). Je n'ai donc pas eu le temps de faire le challenge tic tac toe car je ne voulais pas tout bacler. Mon code pour suguru ne marchera donc finalement pas, néanmoins j'ai réussi à corriger toutes les erreurs de segmentation que j'ai eu pendant un bon bout de temps, mais malheureusement le programme n'aura jamais produit un retour de grille correcte. La raison pour laquelle j'ai décidé de faire Suguru plutot que tic tac toe à la fin est toute simple, étant un grand fan de sudoku, killer sudoku, suguru, etc... j'avais vraiment envie de finir ce problème car au premier semestre je n'ai pas eu le temps de faire sudoku en python car je suis le ou un des seuls élèves à ne pas avoir fait NSI en terminal, donc le temps m'a manqué pour finir correctement le programme de python du premier semestre. Voila pourquoi j'ai préféré essayer de résoudre suguru plutot que tic tac toe.

Vous trouverez [ici](../../../../telechargement.html) la page de téléchargement des fichiers concernant le rapport suguru.
