|
Sudoku : Exemple de backtracking. Code source en langage C Objectif Exécution Principe de résolution Commentaires Compilation Généralités /************ SUDOKU BACKTRACKING Fevrier 2008 *************/ #include <stdio.h> #include <string.h> /* dimension du cote des petits carres */ #define MIN 3 /* dimension du cote du grand carre */ #define MAX 9 /* code de sortie des fonctions */ #define FAUX 0 #define VRAI 1 char grille[MAX][MAX]; /* la grille */ long int g_comp_tests; /* compteur de tests */ int g_flag_affiche; /* Drapeau d'option d'affichage de l'option -a */ /********/ int main(int argc, char *argv[]); int parametre_affichage(char t[]); /**** SAISIES ET VERIFICATIONS ****/ int saisie_grille(void); int saisie_une_ligne(int ligne); int verif_saisie_grille(void); int verif_saisie_lignes(void); int verif_saisie_une_ligne(int ligne); int verif_saisie_colonnes(void); int verif_saisie_une_colonne(int col); int verif_saisie_carres(void); int verif_saisie_un_carre(int carre); void trans_carre(char tamp[], int carre); /**** RESOLUTION ****/ void resolve(void); int case_dispo_nbre(int nbre, int ligne, int col); void trans_carre_de_case(char tamp[], int ligne, int col); /**** DIVERS ****/ void affiche_grille_attente(void); void affiche_grille(void); void affiche_chaine(int nbre); int grille_finie(void); void attente(void); /******/ int main(int argc, char *argv[]) /******/ { . /* option d'affichage dans la fontion resolve */ . g_flag_affiche = ((argc> = 2) && (parametre_affichage(argv[1]))); . /**********/ . printf("\n******* SUDOKU *******\n\n"); . printf("l'option sudoku -a affiche la resolution case par case.\n\n"); . printf("Saisissez %d lignes de %d chiffres,\n", MAX, MAX); . printf("les chiffres peuvent etre separes par n'importe quels caracteres\n"); . printf("Rentrez un 0 pour chaque case vide.\n"); . printf("Pour quitter le programme, valider la lettre Q.\n\n"); . while (saisie_grille()) { . . g_comp_tests = 0; . . resolve(); . . affiche_grille(); . . if (! grille_finie()) printf("Enonce errone...\n"); . . printf("Tests : %ld\n\n", g_comp_tests); . }/* end while */ . printf("\n"); . return 0; } /**********************/ int parametre_affichage(char t[]) /**********************/ { . /* Renvoi VRAI si l'utilisateur a taper une demande d'option d'affichage . sous la forme : a, A, ou /a , /A , -a , -A ect... sinon renvoi FAUX */ . char param; . if (strlen(t) == 1) param = t[0]; . else if (strlen(t) == 2) param = t[1]; . else param = 0; . return ((param == 'A') || (param == 'a')); }/* end parametre_affichage */ /**********************************/ /**** SAISIES ET VERIFICATIONS ****/ /**********************************/ /***************/ int saisie_grille(void) /***************/ {/* l'utilisateur saisie 9 lignes de 9 chiffres */ . int ligne; . printf("Saisissez votre grille : \n"); . while (VRAI){ . . for (ligne = 0; ligne < MAX; ligne++){ . . . if (! saisie_une_ligne(ligne)) return (FAUX); . . }/* end for */ . . printf("\nEnonce : \n"); . . affiche_grille(); . . if (verif_saisie_grille()) return(VRAI); . . else printf("\n"); . }/* end while */ }/* end saisie */ /******************/ int saisie_une_ligne(int ligne) /******************/ { /* l'utilisateur doit saisir une ligne de 0 a 9 chiffres separes ou non par un ou plusieurs carateres. Seuls les 9 premiers chiffres seront retenus. Le + pratique : pave numerique et mettre un point tous les 3 chiffres Eviter d'utiliser gets() a cause des debordements. Penser a vider entierement le buffer */ . int c, i; . int col = 0; . int result = VRAI; . /* RAZ des cases de la ligne */ . for (i = 0; i < MAX; i++) { . . grille[ligne][i] = 0; . }/* for */ . /*******/ . printf("Ligne %d : ", ligne+1); . while(VRAI){ . . c = fgetc(stdin); . . if (c == feof(stdin) || c == 0x0A) return result; . . else if ((c == 'Q') || (c == 'q')) result = FAUX; . . /* transfert de la saisie dans les cases de la ligne */ . . else if ( (result) && (col < MAX) && (c >= '0') && (c <= '9')) { . . . grille[ligne][col++] = c-48; . . }/*end if */ . }/* end while */ }/*end saisie_une_ligne */ /*********************/ int verif_saisie_grille(void) /*********************/ {/* verifie si pas de doublon dans l'ensemble de la grille */ . int result = VRAI; . if (! verif_saisie_lignes()) result = FAUX; . if (! verif_saisie_colonnes()) result = FAUX; . if (! verif_saisie_carres()) result = FAUX; . return result; }/* end verif_saisie_grille */ /*********************/ int verif_saisie_lignes(void) /*********************/ {/* verifie si pas de doublon dans les lignes */ . int ligne; . int result = VRAI; . for (ligne = 0; ligne < MAX; ligne++) { . . if (! verif_saisie_une_ligne(ligne)) result = FAUX; . }/* end for */ . return result; }/* end verif_saisie_lignes */ /************************/ int verif_saisie_une_ligne(int ligne) /************************/ {/* verifie si pas de doublon dans la ligne */ . int i,j; . for (i = 0; i < MAX-1; i++) { . . if ( grille[ligne][i] == 0 ) continue; . . for (j = i+1; j < MAX; j++) { . . . if (grille[ligne][i] == grille[ligne][j]) { . . . . printf("Erreur ligne %d : chiffres egaux\n", ligne+1); . . . . return FAUX; . . . }/* end if */ . . }/* end for j */ . }/* end for i */ . return VRAI; }/* end verif_saisie_une_ligne */ /***********************/ int verif_saisie_colonnes(void) /***********************/ {/* verifie si pas de doublon dans les colonnes */ . int col; . int result = VRAI; . for (col = 0; col < MAX; col++) { . . if (! verif_saisie_une_colonne(col)) result = FAUX; . }/* end for */ . return result; }/* end verif_saisie_colonnes */ /**************************/ int verif_saisie_une_colonne(int col) /**************************/ {/* verifie si pas de doublon dans la colonne */ . int i, j; . for (i = 0; i < MAX-1; i++) { . . if ( grille[i][col] == 0 ) continue; . . for (j = i+1; j < MAX; j++) { . . . if (grille[i][col] == grille[j][col]) { . . . . printf("Erreur colonne %d : chiffres egaux\n", col+1); . . . . return FAUX; . . . }/* end if */ . . }/* end for j */ . }/* end for i */ . return VRAI; }/* end verif_saisie_une_colonne */ /*********************/ int verif_saisie_carres(void) /*********************/ {/* verifie si pas de doublon dans les carres de 3 X 3 */ . int carre; . int result = VRAI; . for (carre = 0; carre < MAX; carre++) { . . if (! verif_saisie_un_carre(carre)) result = FAUX; . }/* end for */ . return result; }/* end verif_saisie_carres */ /***********************/ int verif_saisie_un_carre(int carre) /***********************/ {/* verifie si pas de doublon dans le carre de 3 X 3 */ . char tamp[MAX+10]; . int i, j; . trans_carre(tamp, carre); . for (i = 0; i < MAX-1; i++) { . . if (tamp[i] == 0 ) continue; . . for (j = i+1; j < MAX; j++) { . . . if (tamp[i] == tamp[j]) { . . . . printf("Erreur carre %d : chiffres egaux\n", carre+1); . . . . return FAUX; . . . }/* end if */ . . }/* end for j */ . }/* end for i */ . return VRAI; }/* end verif_saisie_un_carre */ /**************/ void trans_carre(char tamp[], int carre) /**************/ {/* transfert le carre de 3 X 3 dans un tableau a une dimension */ . int i,j, x,y,z; . x = (((carre) % MIN)*MIN); . y = (((carre+MIN) / MIN)*MIN)-MIN; . z = 0; . for (j = y; j < y+MIN; j++) { . . for (i = x; i < x+MIN; i++) { . . . tamp[z++] = grille[j][i]; . . }/* end for i */ . }/* end for j */ }/* end trans_carre */ /********************/ /**** RESOLUTION ****/ /********************/ /**********/ void resolve(void) /**********/ {/* Attention, fonction recursive... */ . int ligne, col, nbre, nbre_tamp; . for (ligne = 0; ligne < MAX; ligne++) { . . for (col = 0; col < MAX; col++) { . . . if (grille[ligne][col]) continue; . . . for (nbre = 1; nbre <= MAX; nbre++) { . . . . if (! case_dispo_nbre(nbre, ligne, col)) continue; . . . . nbre_tamp = grille[ligne][col]; . . . . grille[ligne][col] = nbre; . . . . g_comp_tests++; . . . . if (g_flag_affiche) affiche_grille_attente(); . . . . resolve(); . . . . if (grille_finie()) return; . . . . /* pour voir toutes les solutions des grilles . . . . a solutions multiples . . . . mettre en rem la ligne du dessus et . . . . retirer le rem de la ligne du dessous */ . . . . /* if (grille_finie()) affiche_grille_attente(); */ . . . . grille[ligne][col] = nbre_tamp; . . . }/* end for nbre */ . . . return; . . }/* end for col */ . }/* end for ligne */ . return; }/* end resolve */ /******************/ int case_dispo_nbre(int nbre, int ligne, int col) /******************/ { /* teste si le nbre est deja place dans la ligne, ou la colonne, ou le carre de 3 X 3 afferent a la case pointee par ligne, col */ . char tamp[MAX+10]; . int i; . /*si nbre est utilise dans la ligne, renvoi FAUX */ . for (i = 0; i < MAX; i++) if (grille[ligne][i] == nbre) return(FAUX); . /*si nbre est utilise dans la colonne, renvoi FAUX */ . for (i = 0; i < MAX; i++) if (grille[i][col] == nbre) return(FAUX); . /*si nbre est utilise dans le mini carre, renvoi FAUX */ . trans_carre_de_case(tamp, ligne, col); . for (i = 0; i < MAX; i++) if (tamp[i] == nbre) return (FAUX); . /*donc nbre est diponible pour la case */ . return(VRAI); }/* end case_dispo_nbre */ /**********************/ void trans_carre_de_case(char tamp[], int ligne, int col) /**********************/ /* tranfert le carre de 3 X 3 d'une case dans un tableau a une dimension */ { . int i, j, k; . while ((ligne % MIN) != 0) ligne--; . while ((col % MIN) != 0) col--; . k = 0; . for (j = ligne; j < ligne+MIN; j++) { . . for (i = col; i < col+MIN; i++) { . . . tamp[++k] = grille[j][i]; . . }/* end for i */ . }/* end for j */ }/* end trans_carre_de_case */ /****************/ /**** DIVERS ****/ /****************/ /*************************/ void affiche_grille_attente(void) /*************************/ {/* affiche la grille avec mise en attente de la touche entree */ . affiche_grille(); . printf("%ld, Touche entree pour la suite...", g_comp_tests); . attente(); }/* end option_affiche */ /*****************/ void affiche_grille(void) /*****************/ {/* affiche l'ensemble de la grille */ . int ligne, col; . for (ligne = 0; ligne < MAX; ligne++) { . . for (col = 0; col < MAX; col++) { . . . printf(" %1d ", grille[ligne][col]); . . . if ( ((col % MIN) == MIN-1) && (col < MAX-1) ) { . . . . printf(" | "); . . . } . . }/* end for col */ . . printf("\n"); . . if ( ((ligne % MIN) == MIN-1) && (ligne < MAX-1) ) { . . . affiche_chaine(33); . . } . }/* end for ligne */ . printf("\n"); }/* end affiche_grille */ /*****************/ void affiche_chaine(int nbre) /*****************/ {/* Affiche une suite de caractere etoile */ . int i; . for (i = 1; i <= nbre; i++) { . . printf("*"); . } . printf("\n"); }/* end affiche_chaine */ /**************/ int grille_finie(void) /**************/ /* teste si la grille est finie. Retourne VRAI si grille finie, sinon retourne FAUX. Commence par la fin de la grille celle ci ce completant par son debut */ { . int ligne, col; . for (ligne = MAX-1; ligne >= 0; ligne--) { . . for (col = MAX-1; col >= 0; col--) { . . . if (grille[ligne][col] == 0 ) { . . . . return FAUX; . . . } . . }/* end for col */ . }/* end for ligne */ . return VRAI; }/* end grille_finie */ /**********/ void attente(void) /**********/ {/* attente de la touche entree au clavier */ . char c; . while(VRAI){ . . c = fgetc(stdin); . . if (c == feof(stdin) || c == 0x0A) return; . }/* end while */ }/* end attente */ Objectif L'objectif de ce programme est de résoudre un énoncé soduku en backtracking. Exécution
Le programme s'exécute en mode commande. Principe de résolution
Méthode récursive : Commentaires
Le programme résout : Compilation Compiler en mode C. Il est inutile de compiler en c++.
Ce programme a été compilé et testé avec gcc sous Linux et sous Windows. Pour un maximum de standardisation seules les fonctions systèmes : printf(), fgetc() et strlen() ont été utilisées. Généralités Le Sudoku repose sur les principes de la « programmation par contraintes », ce qui fait que sa résolution intéresse beaucoup les informaticiens. |