Réalisation de 3 fonctions pour le sudoku

45K Messages postés 13 Date d'inscription dimanche 20 décembre 2020 Statut Membre Dernière intervention 8 juin 2023 - Modifié le 7 juin 2023 à 11:53
 PierrotLeFou - 8 juin 2023 à 19:35

Bonjour,

Je dois réaliser trois fonctions pour le sudoku. Je rencontre des problèmes d'implémentation pour les deux dernières fonctions :

La première s'appelle :

int colonnevalide(int sudo[][9], int col);

Elle vérifie si les cases de la colonne correspondante ne contiennent que des chiffres allant 1 à 9 (et pas deux fois le même) ou des 0.

La seconde s'appelle ;

int carrevalide(int sudo[][9], int centre_x, centre_y);

Elle reçoit les coordonnées du centre d'un carré de taille 3x3 et doit vérifier si les cases du carré correspondantes ne contiennent que des chiffres allant de 1 à 9 (et pas deux fois les mêmes) ou des 0.

La troisième et dernière fonction s'appelle :

int grillevalide(int sudo[][9]);

Elle vérifie si le sudoku est valide, c'est-à-dire que la ligne, la colonne et le carré de taille 3x3 ne contiennent que des chiffres allant de 1 à 9 (et pas deux fois les mêmes) ) ou des 0.

Voici ce que j'ai fait :


Je ne sais pas comment avoir les coordonnées du centre d'un carré (dans la fonction grillevalide) et (dans la fonction carrevalide) je n'arrive pas à vérifier les lignes et les colonnes sans utiliser de boucle imbriquée. 

int colonnevalide(int sudo[][9] , int col) {
    for (int case = 0 ; case > 9 ; case++) {
        for (int j = case + 1 ; j < 9 ; j++) {
            if (sudo[case][col] == sudo[j][col]) {
                if(sudo[case][col] != 0){
                    return 0; 
                }
            }
        }
    }
    return 1;
}

int carrevalide(int sudo[][9] , int centre_x, int centre_y) {
    int tmp_l = tmp_c = 2;
    int col = ligne = 1;
    int cmpt = 0;
    centre_x = centre_x - 1 ;
    centre_y = centre_y + 1;
    while(cmpt != 3) {
        if(tmp_l <= 3) {
            if (
                sudo[centre_x ][centre_y - ligne - 1] ==
                sudo[centre_x ][centre_y - tmp_l - 1]
            )
                return -1;
            if (
                sudo[centre_x + col - 1  ][centre_y ] ==
                sudo[centre_x + tmp_c - 1 ][centre_y]
            )
                return -1;
        }
        if (ligne == 3) {
            ++cmpt;
            ++centre_x;
            ++centre_;
        } else{
            ++ligne;
            ++col;
            tmp_l = tmp_c = 2;
        }        
    }
}

int grillevalide(int sudo[][9]) {
    for (int i = 0 ; i < 9 ; i++) {
        if (colonnevalide(sudo , i) == 0)
            return -1;
        i = i % 3 == 0 ? i + 3 / 2 : (i % 3) / 2;  
        if (carrevalide(sudo , i , i ) == -1)
            return -1;
    }
    return 0;
}

Pouvez-vous m'aider s'il vous plaît :) .

Je vous remercie d'avance.
Windows / Chrome 113.0.0.0

A voir également:

10 réponses

Tu écris des fonctions mais tu n'as rien pour les tester. Est-ce que tu joues aux devinettes?
Je te donne le code pour tester les carrés.
Je te laisse écrire le code pour les deux autres fonctions.
Ta fonction grillevalide n'est pas correcte. Si tu pouvais tester et afficher certaines valeurs, tu saurais pourquoi.
(il y a trop de choses dans ta boucle)
(désolé si mon code n'est pas coloré. Je suis aveugle et ma synthèse vocale ne me donne pas accès à la barre des fonctions)
 

int carrevalide(int sudo[][9], int cx, int cy) {
    // cx et cy ont été calculés dans la fonction grillevalide().
    int freq[10] = { 0 };   // Tableau des fréquences de chaque chiffre de 0 à 9.
    for(int l = cx - 1; l < cx + 2; l++) {
        for(int c = cy - 1; c < cy + 2; c++) {
            int v = sudo[l][c];   // Chiffre se trouvant dans la position (l, c)
            // Si le chiffre n'est pas 0 et s'il a déjà été rencontré
            if(v != 0 && freq[v] != 0) return -1;   // Le carré n'est pas valide.
            freq[v] = 1;   // Ce chiffre a été rencontré une fois.
        }
    }
    return 0;   // Le carré est valide.
}

On n'a pas besoin d'incrémenter les indices avec les boucles for. Ça se fait dans l'énoncé lui-même.

2

Je ne sais pas à quel moment du jeu ces fonctions seront appelées. Je ne peux pas encore dire que ce n'est pas la bonne approche ...
Tu n'as pas de fonction pour tester les lignes, ce qui t'aiderait pour tester la grille au complet.
Les centres des carrés sont en positions 1, 4, 7, autant pour les lignes que les colonnes.
Deux boucles imbriquées partant à 1, augmentant de 3, et inférieures à 9.

Si on part d'une position arbitraire (x, y):

cx = x/3*3+1 cy = y/3*3+1  (x et y sont des entiers (int), donc ce sont des divisions entières)

Je choisirais plutôt de donner les coordonnées du coin supérieur gauche à la fonction pour éviter quelques problèmes d'initialisation.
Ensuite, pour tester les lignes, les colonnes, et même les carrés sans boucles imbriquées inutiles.
(le truc est bon pour les trois fonctions)
Je te donne une variante, on peut en avoir d'autres:
On dcrée un tableau de fréquences de 10 (pour 0 à 9( valeurs initialisés à 0.
On ajoute 1 dans le tableau de fréquences en se servant de la valeur dans la grille comme indice,
par exemple, si j'ai un 6 en position sudo[3][7], j'ajoute 1 dans le tableau des fréquences à la position 6 sans rien tester.
À la fin, je vérifie (sauf pour 0) que les valeur sont toutes à 0 ou 1.
Une autre variante serait de tester au fur et à mesure qu'on parcourt que la valeur du tableau est bien 0,
si non, il y a erreur (sauf encore pour 0)
si oui, on place 1 dans le tableau.
Par exemple, si sudo[3][7] vaut 6, je vérifie si la position 6 dans le tableau defréquence vaut 0 ou pas ...

1
[Dal] Messages postés 6175 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 30 avril 2024 1 083
4 juin 2023 à 16:56

Salut Pierrot,

Je choisirais plutôt de donner les coordonnées du coin supérieur gauche à la fonction pour éviter quelques problèmes d'initialisation.

Je pense que 45K ne doit pas avoir le choix des prototypes, vu que cela ressemble à un exercice.

On dcrée un tableau de fréquences de 10 (pour 0 à 9( valeurs initialisés à 0.
On ajoute 1 dans le tableau de fréquences en se servant de la valeur dans la grille comme indice,
par exemple, si j'ai un 6 en position sudo[3][7], j'ajoute 1 dans le tableau des fréquences à la position 6 sans rien tester.
À la fin, je vérifie (sauf pour 0) que les valeur sont toutes à 0 ou 1.
Une autre variante serait de tester au fur et à mesure qu'on parcourt que la valeur du tableau est bien 0,
si non, il y a erreur (sauf encore pour 0)

Oui, c'est une bonne méthode. Il faut aussi, bien sûr, penser à initialiser le tableau des fréquences à 0 avant de commencer à l'utiliser.

1
PierrotLeFou
4 juin 2023 à 04:20

Ces fonctions ne devraient être appelées que pour deux raisons:
1. On te fournit des positions de départ (contraintes) et tu veux vérifier si elles sont correctes.
2. Tu veux vérifier si ton code est correct et la solution que tu as trouvée est correcte.
Pour la fonction carrevalide(), tu peux simplifier grandement avec deux boucles imbriquées du style:
    for(int i = centre_Z - 1; i < centre_Z + 2; i++)
où Z est soit x soit y et tu auras besoin de deux indices du genre l (ligne) et c (colonnes).

1
PierrotLeFou
4 juin 2023 à 17:44

Ma phrase:
> On crée un tableau de fréquences de 10 (pour 0 à 9( valeurs initialisés à 0.
est sans doute mal formulée. Bien sûr qu'il faut initialiser le tableau des fréquences à 0.
Je préfère des fonctions du genre:
vérifier si le chiffre X n'est pas dans la ligne, la colonne, le carré associés à cette coordonnée.
Elles peuvent servir comme outil de validation des données fournies au départ placés sur une grille initialement "vide".
Et elles servent dans l'algorithme pour essayer de placer un chiffre dans une position vide.

1
[Dal] Messages postés 6175 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 30 avril 2024 1 083
5 juin 2023 à 10:49

Au temps pour moi, tu l'avais effectivement mentionné, je t'avais lu trop rapidement :-)

1

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question

Je comprends que ça n'est pas évident pour toi, du moins pour l'instant, d'écrire le sudoku au complet et de tout tester.
Tu pourrais tout de même faire un test minimal. Tu pourrais définir une grille 9x9 dans le main.
Pour le premier test, tu mets tout à 0 dans la grille et tu testes une ligne, une colonne et un carré.
Les 3 tests devraient donner que c'est bon.
Ensuite, tu mets par exemple des 1 partout. Les 3 tests devraient donner mauvais.
Tu dois être capable de placer les chiffres 1 à 9 dans une ligne et une colonne (essaies aussi pour un carré).
Tu fais le test pour chacun des 3 cas, ils devraient être tous bons.
Puis tu remplaces arbitrairement une position par une valeur présente ailleurs. Par exemple un 5 dans la position 3.
Vérifies que tu as une erreur partout.
Si tu réussis à faire déjà cela, ce sera un bon début ....

1
45K Messages postés 13 Date d'inscription dimanche 20 décembre 2020 Statut Membre Dernière intervention 8 juin 2023
Modifié le 7 juin 2023 à 11:55

Oui, c'est un exercice, j'ai oublié de le préciser, je ne devais que les trois fonctions du coup.

J'ai rectifié et j'aimerais savoir si c'est correct.

int GrilleValide(int sudo[][9]) {
    for(int x = 1 ; x < 9 ; x++) {
        if (colonnevalide(sudo , x) == -1)
            return -1;
        if (lignevalide(sudo , x) == -1)
            return -1;
        for(int y = 1 ; y < 9 ; y++){
            int c_x = (x / 3) * 3 + 1;
            int c_y = (y / 3) * 3 + 1;
            if (carrevalide(sudo, c_x, c_y) == -1)
               return -1;
        }
    }
    return colonnevalide(sudo, 0) || lignevalide(sudo, 0);
}

int carrevalide(int sudo[][9], int c_x , int c_y){
    int centre_z = c_x;
    int ligne = c_y - 1;
    int col = c_x - 1;
    if (ligne % 3 == 0) {
        for (int i = centre_z - 1 ; i < centre_z + 2 ; i++){
            for (int j = centre_z ; j < centre_z + 1 ; j++){
                if (sudo[col][j] == sudo[col][i])
                    return -1;
                if (sudo[i][ligne] == sudo[j][ligne])
                    return -1;
            }
            ++col;
            ++ligne;
        }
        return 0;
    }
    return -1;
}
0
[Dal] Messages postés 6175 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 30 avril 2024 1 083
Modifié le 6 juin 2023 à 18:25

Vois le message de Pierrot https://forums.commentcamarche.net/forum/affich-37858870-realisation-de-3-fonctions-pour-le-sudoku#p37859943 pour un exemple de code de carrevalide() qui tient la route.

Juste deux questions :

  • Est-il obligatoire pour toi de retourner -1 pour une résultat invalide et 0 pour un résultat valide ? Ce n'est pas très naturel en C. On retournerait plutôt 0 pour indiquer "faux" et 1 (ou toute autre valeur que 0) pour retourner vrai, ou on utiliserait un type bool qui existe depuis C99 (en faisant un #include <stdbool.h>) qui peut prendre des valeurs false ou true (mais ton prototype imposé indique un type int, alors tu devrais t'y tenir). Que tu utilises C99 ou pas, cela permettrait d'écrire if (carrevalide(sudo, 1, 1)) { printf("Le carré de centre 1,1 est valide\n"); } ce qui se lit mieux
  • Ton enseignant vous a-t-il fourni des cas de test automatisés pour tester tes fonctions de façon systématique et complète ?
0
45K Messages postés 13 Date d'inscription dimanche 20 décembre 2020 Statut Membre Dernière intervention 8 juin 2023
Modifié le 6 juin 2023 à 23:55

Ah, merci, je n'avais pas vu du coup en fait, c'est un exercice à l'écrit, on n'a pas de valeur à tester, c'est pour ça que j'essaie de deviner XD

ok je vais rectifier ce que j'ai marqué :)

0
45K Messages postés 13 Date d'inscription dimanche 20 décembre 2020 Statut Membre Dernière intervention 8 juin 2023
8 juin 2023 à 11:34

AHHHH OK , En fait, je pensais que pour tester le carrée 3 * 3 il fallait simplement tester les lignes et les colonnes du carrée. Maintenant, je pense que c'est bon :)

#include <stdio.h>
#define MAX 9

int colonnevalide(int sudo[][9] , int col) {
    int cases , j;
    for (cases = 0 ; cases < 9 ; cases++) {
        for (j = cases + 1 ; j < 9 ; j++) {
            if (sudo[cases][col] == sudo[j][col]) {
                if(sudo[cases][col] != 0){
                    return 1; 
                }
            }
        }
    }
    return 0;
}


int lignevalide(int sudo[][9] , int col) {
    int cases;
    for (cases = 0 ; cases  < 9 ; cases++) {
        for (int j = cases + 1 ; j < 9 ; j++) {
            if (sudo[col][cases] == sudo[col][j]) {
                if(sudo[col][cases] != 0){
                    return 1; 
                }
            }
        }
    }
    return 0;
}
int carreValide(int sudo[][9], int cx, int cy) {
    // cx et cy ont été calculés dans la fonction grillevalide().
    int freq[10] = { 0 };   // Tableau des fréquences de chaque chiffre de 0 à 9.
    for(int l = cx - 1; l < cx + 2; l++) {
        for(int c = cy - 1; c < cy + 2; c++) {
            int v = sudo[l][c];   // Chiffre se trouvant dans la position (l, c)
            // Si le chiffre n'est pas 0 et s'il a déjà été rencontré
            if(v != 0 && freq[v] != 0) return -1;   // Le carré n'est pas valide.
            freq[v] = 1;   // Ce chiffre a été rencontré une fois.
        }
    }
    return 0;   // Le carré est valide.
}


int GrilleValide(int sudo[][9]) {
    for(int x = 1 ; x < 9 ; x++) {
        if (colonnevalide(sudo , x)){
            printf("carre");
            return 1;
        }
            
        if (lignevalide(sudo , x)){
            printf("2");

            return 1;
        }
           
            
        for(int y = 1 ; y < 9 ; y++){
            int c_x = (x / 3) * 3 + 1;
            int c_y = (y / 3) * 3 + 1;
            
            if (carrevalide(sudo, c_x, c_y)){
               

               return 1;
            }
        }
    }
    
    return colonnevalide(sudo, 0) || lignevalide(sudo, 0);
}



void creer_tableau(int tab[][MAX] , int entier){
    int i , j ;
    for( i = 0 ; i < 9 ; i++){
        for( j = 0 ; j < 9 ; j++){
            tab[i][j] = entier;

        }
    }
    tab[2][5] = 5;

}

void print_tab(int tab[][9] ){
    for(int i = 0 ; i < 9 ; i++){
        for(int j = 0 ; j < 9 ; j++){
            printf("%d",tab[i][j]); 

        }
        printf("\n");
    }
}

int main(){
    int x , y , val;
    int tab[9][9];
    creer_tableau(tab , 0);
    print_tab(tab);
    for(int  i = 0 ; i <  9 ; i++){
        printf("x , y , val\n");
        scanf("%d %d %d", &x , &y , &val);
        tab[x][y] = val;
        print_tab(tab);

        printf("%d\n" , GrilleValide(tab));

    }

}
0
[Dal] Messages postés 6175 Date d'inscription mercredi 15 septembre 2004 Statut Contributeur Dernière intervention 30 avril 2024 1 083
Modifié le 8 juin 2023 à 12:57

Salut,

Si ton enseignant ne t'a pas donné un jeu de test pour tester tes fonctions automatiquement, tu t'en fabriques un, au lieu de taper des trucs au clavier.

En vidant le contenu de ton main, et en compilant tes fonctions, on cet avertissement et erreur de liaison :

$ gcc -Wall -Wextra 37858870.c
37858870.c: In function ‘GrilleValide’:
37858870.c:67:19: warning: implicit declaration of function ‘carrevalide’; did you mean ‘carreValide’? [-Wimplicit-function-declaration]
               if (carrevalide(sudo, c_x, c_y)){
                   ^~~~~~~~~~~
                   carreValide
/usr/bin/ld: /tmp/ccBGLuij.o: in function `GrilleValide':
37858870.c:(.text+0x320): undefined reference to `carrevalide'
collect2: error: ld returned 1 exit status

Le C est sensible à la casse.

En corrigeant cette erreur le code compile et produit un exécutable.

Comme indiqué, en C 0 signifie faux et 1 (ou toute autre valeur que 0) signifie vrai. Tu fais l'inverse. Je n'ai pas regardé plus que cela ton code, mais il me parait très bizarre.

Voilà comment on utilise un jeu de test, pour tester :

#include <assert.h>

(... insère tes fonctions à tester ici ...)

int main(void) {
        /*
         * La grille ci-dessous est invalide
         * les éléments suivants sont invalides :
         * - ligne 6 incorrecte.
         * - ligne 8 incorrecte.
         * - colonne 1 incorrecte.
         * - colonne 2 incorrecte.
         * - bloc 6 incorrect.
         * (les autres sont valides)
         * Source : https://dpt-info.di.unistra.fr/~grosjean/l2me/sudoku.pdf
         */
        int tab[9][9] = {
                { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
                { 4, 5, 6, 7, 8, 9, 1, 2, 3 },
                { 7, 8, 9, 1, 2, 3, 4, 5, 6 },
                { 2, 3, 1, 5, 6, 4, 8, 9, 7 },
                { 5, 6, 4, 8, 9, 7, 2, 3, 1 },
                { 8, 9, 7, 2, 3, 1, 5, 6, 4 },
                { 3, 5, 2, 6, 4, 5, 9, 7, 8 },
                { 6, 4, 5, 9, 7, 8, 3, 1, 2 },
                { 9, 7, 6, 3, 1, 2, 6, 4, 5 }
        };
        printf("Grille testée :\n");
        print_tab(tab);
        /* Les lignes 0 à 5 sont correctes */
        assert(lignevalide(tab, 0) && "ligne 0 valide");
        assert(lignevalide(tab, 1) && "ligne 1 valide");
        assert(lignevalide(tab, 2) && "ligne 2 valide");
        assert(lignevalide(tab, 3) && "ligne 3 valide");
        assert(lignevalide(tab, 4) && "ligne 4 valide");
        assert(lignevalide(tab, 5) && "ligne 5 valide");

        return 0;
}

Le premier test échoue et interrompt le programme :

$ gcc -Wall -Wextra 37858870.c
$ ./a.out 
Grille testée :
123456789
456789123
789123456
231564897
564897231
897231564
352645978
645978312
976312645
a.out: 37858870.c:114: main: Assertion `lignevalide(tab, 0) && "ligne 0 valide"' failed.
Abandon

Corrige ton code pour que le premier test passe, ainsi que les 5 autres.

Ensuite, ajoute un test pour la 6ème ligne en vérifiant que la fonction renvoie bien qu'elle est non valide.

Répète le cycle, en ajoutant des nouveaux tests et en vérifiant s'ils passent en exécutant systématiquement tous les tests qui on,t déjà passé (pour vérifier l'absence de régression)s jusqu'à ce que tu aies testé tous les cas de figure sur ce jeu de test.


0
PierrotLeFou
8 juin 2023 à 19:35

Je vois que tu as utilisé mon truc pour la fonction carrevalide, mais pas pour les fonctions lignevalide et colonnevalide.
Tu pourrais afficher les indices dans ces trois fonctions juste à l'intérieur de la boucle interne.
Tu verrais que, dans carrevalide, je ne fais que 9 tests. Et tu en fais combien dans les deux autres? Près de 40?
Est-ce que tu comprends ce que je fais dans carrevalide? Un copier-coller ne suffit pas.
Dans grillevalide, pourquoi tester les indices de 1 à 8 et tester 0 à la fin?
Tu pourrais faire une boucle de 0 à 9 exclus et tester chaque ligne et chaque colonne dans cette même boucle en utilisant le même indice comme paramètre.
Pour les carrés, tu en as bien 9 également. Pourquoi commencer encore à 1?
As-tu oublié que les indices des tableaux commencent à 0 et non à 1?
En fait, tu pourrais placer les trois tests dans la même boucle en calculant correctement les cx et cy.

0