Rapport : Killer sudoku solver
Table des matières
1. Introduction
1.1. Principe de focntionnment
Url du challenge : https://www.codingame.com/ide/puzzle/killer-sudoku-solver
L’objectif de ce challenge est de résoudre une grille de killer sudoku.
Rappel des règles du sudoku :
- Chaque ligne doit contenir les chiffres de 1 à 9
- Chaque colonne doit contenir les chiffres de 1 à 9
- Chaque carré de 3x3 doit contenir les chiffres de 1 à 9
A cela s’ajoute les contraintes du killer sudoku :
- La somme des cases d’une cage doit être égale à la valeur indiquée dans la case en haut à gauche de la cage
1.2. Données fournies
On reçoit en entrée une grille de killer sudoku sous la forme suivante : 9 lignes contenat la grille, chaque ligne contient 9 caractères, un espace , 9 caractères. Les 9 premiers caractères sont les chiffres de la première ligne, les 9 suivants des lettres indiquant les cages.
Enfin un 10e ligne contient les valeurs des cages.
56..1..2. aabbccdde ..72..68. afghhiide ..2.87.15 jfggklmme ......3.9 jjgnklopp .7....2.. qqgnooorr 9.634.8.. stuuvwwxx 2.9..8... stuuvvwyz ..41.2... sAuuByyyz .8.4...3. CADDBEEFF a=12 b=17 c=4 d=14 e=15 f=13 g=19 h=7 i=10 j=16 k=10 l=13 m=10 n=15 o=15 p=13 q=11 r=11 s=18 t=3 u=28 v=15 w=20 x=8 y=22 z=12 A=11 B=13 C=6 D=9 E=10 F=5
1.3. Données à fournir
On doit fournir en sortie une grille de killer sudoku complétée.
123874569 567932184 849651237 916247853 358196472 472385916 291768345 685423791 734519628
2. Méthode de résolution
On commence par définir les structures de données qui vont nous permettre de stocker les données du problème.
typedef struct { int taille; // Nombre de d'element dans la cage int somme; // Somme de la cage int *coords[MAX_COORDS_CAGE]; // tableau de pointeur vers les coordonnees des cases de la cage } cage_t; typedef struct { cage_t cages[MAX_CAGES]; } liste_cages_t;
On définit ensuite les fonctions qui vont nous permettre de manipuler ces structures. initialiser la liste des cages ajouter une coordonnée à une cage ajouter la somme d’une cage verifier ajouté une valeur à une cage est possible (pas de doublon dans la ligne, colonne ou cage)
int lettre_to_index(char lettre) { return lettre >= 'a' ? lettre - 'a' : lettre - 'A' + 26; } void init_liste_cages(liste_cages_t *ls_cages) { for (int i = 0; i < MAX_CAGES; i++) { ls_cages->cages[i].taille = 0; } } void ajouter_coord_cage(liste_cages_t *ls_cages, int num_cage, int *coord) { ls_cages->cages[num_cage].coords[ls_cages->cages[num_cage].taille] = coord; ls_cages->cages[num_cage].taille++; } void ajouter_somme_cage(liste_cages_t *ls_cages, int num_cage, int somme) { ls_cages->cages[num_cage].somme = somme; } bool somme_cage_possible(liste_cages_t *ls_cages, int num_cage, int num) { int somme = 0; for (int i = 0; i < ls_cages->cages[num_cage].taille; i++) { somme += *ls_cages->cages[num_cage].coords[i]; } return somme + num <= ls_cages->cages[num_cage].somme; }
On lit ensuite la grille: on stock dans un tableau 2D les valeurs de la grille on stock dans un tableau 2D les index des cages on stock dans une liste les cages avec leurs coordonnées et leur somme
void lire_grille(int grille[TAILLE][TAILLE], int cage_index[TAILLE][TAILLE], liste_cages_t *ls_cages) { int i, j; int index; for (i = 0; i < TAILLE; i++) { char grid_line[10]; char grid_cages[10]; scanf("%s%s", grid_line, grid_cages); fgetc(stdin); for (j = 0; j < TAILLE; j++) { if (grid_line[j] == '.') { grille[i][j] = 0; } else { grille[i][j] = grid_line[j] - '0'; } } for (j = 0; j < TAILLE; j++) { index = lettre_to_index(grid_cages[j]); cage_index[i][j] = index; ajouter_coord_cage(ls_cages, index, &grille[i][j]); } } char cages[251]; scanf("%[^\n]", cages); char somme[3]; char lettre; i = 0; while (cages[i] != '\0') { lettre = cages[i]; i += 2; j = 0; somme[0] = '\0'; somme[1] = '\0'; somme[2] = '\0'; while (cages[i] != ' ' && cages[i] != '\0') { somme[j] = cages[i]; j++; i++; } ajouter_somme_cage(ls_cages, lettre_to_index(lettre), atoi(somme)); i++; } }
On définit ensuite la fonction qui va nous permettre de vérifier si une valeur est valide pour une case donnée. On vérifie que la valeur n’est pas présente dans la ligne, la colonne et la sous-grille. On vérifie que la valeur n’est pas présente dans la cage. On vérifie que la somme de la cage n’est pas dépassée.
bool est_valide(int grille[TAILLE][TAILLE], int cage_index[TAILLE][TAILLE], liste_cages_t *ls_cages, int x, int y, int num) { int i; // Vérifier la ligne et la colonne for (i = 0; i < TAILLE; i++) { if (grille[i][y] == num || grille[x][i] == num) { return false; } } // Vérifier la sous-grille int x0 = (x / 3) * 3; int y0 = (y / 3) * 3; for (i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (grille[x0 + i][y0 + j] == num) { return false; } } } // Vérifier les cages return somme_cage_possible(ls_cages, cage_index[x][y], num); }
On définit ensuite la fonction qui va nous permettre de résoudre la grille. On parcourt la grille de gauche à droite et de haut en bas. Si on tombe sur une case vide, on essaie de mettre une valeur de 1 à 9. Si la valeur est valide, on essaie de résoudre la grille avec cette valeur. Si la grille est résolue, on retourne true. Sinon, on essaie avec une autre valeur. Si on a essayé toutes les valeurs, on retourne false.
bool resolution(int grille[TAILLE][TAILLE], int cage_index[TAILLE][TAILLE], liste_cages_t *ls_cages) { int i, j, num; for (i = 0; i < TAILLE; i++) { for (j = 0; j < TAILLE; j++) { if (grille[i][j] == 0) { for (num = 1; num <= 9; num++) { if (est_valide(grille, cage_index, ls_cages, i, j, num)) { grille[i][j] = num; if (resolution(grille, cage_index, ls_cages)) { return true; } else { grille[i][j] = 0; } } } return false; } } } return true; }
On définit enfin la fonction qui va nous permettre d’afficher la grille.
void afficher_grille(int grille[TAILLE][TAILLE]) { int i, j; for (i = 0; i < TAILLE; i++) { for (j = 0; j < TAILLE; j++) { printf("%d", grille[i][j]); } printf("\n"); } }
On définit enfin la fonction principale qui va nous permettre de résoudre la grille.
int main() { int grille[TAILLE][TAILLE]; int cage_index[TAILLE][TAILLE]; liste_cages_t ls_cages; init_liste_cages(&ls_cages); lire_grille(grille, cage_index, &ls_cages); resolution(grille, cage_index, &ls_cages); afficher_grille(grille); return 0; }
2.1. Implémentation complète
#include <stdio.h> #include <stdbool.h> #include <string.h> #include <stdlib.h> #define TAILLE 9 #define MAX_CAGES 52 #define MAX_COORDS_CAGE 9 typedef struct { int taille; // Nombre de d'element dans la cage int somme; // Somme de la cage int *coords[MAX_COORDS_CAGE]; // tableau de pointeur vers les coordonnees des cases de la cage } cage_t; typedef struct { cage_t cages[MAX_CAGES]; } liste_cages_t; int lettre_to_index(char lettre) { return lettre >= 'a' ? lettre - 'a' : lettre - 'A' + 26; } void init_liste_cages(liste_cages_t *ls_cages) { for (int i = 0; i < MAX_CAGES; i++) { ls_cages->cages[i].taille = 0; } } void ajouter_coord_cage(liste_cages_t *ls_cages, int num_cage, int *coord) { ls_cages->cages[num_cage].coords[ls_cages->cages[num_cage].taille] = coord; ls_cages->cages[num_cage].taille++; } void ajouter_somme_cage(liste_cages_t *ls_cages, int num_cage, int somme) { ls_cages->cages[num_cage].somme = somme; } bool somme_cage_possible(liste_cages_t *ls_cages, int num_cage, int num) { int somme = 0; for (int i = 0; i < ls_cages->cages[num_cage].taille; i++) { somme += *ls_cages->cages[num_cage].coords[i]; } return somme + num <= ls_cages->cages[num_cage].somme; } void lire_grille(int grille[TAILLE][TAILLE], int cage_index[TAILLE][TAILLE], liste_cages_t *ls_cages) { int i, j; int index; for (i = 0; i < TAILLE; i++) { char grid_line[10]; char grid_cages[10]; scanf("%s%s", grid_line, grid_cages); fgetc(stdin); for (j = 0; j < TAILLE; j++) { if (grid_line[j] == '.') { grille[i][j] = 0; } else { grille[i][j] = grid_line[j] - '0'; } } for (j = 0; j < TAILLE; j++) { index = lettre_to_index(grid_cages[j]); cage_index[i][j] = index; ajouter_coord_cage(ls_cages, index, &grille[i][j]); } } char cages[251]; scanf("%[^\n]", cages); char somme[3]; char lettre; i = 0; while (cages[i] != '\0') { lettre = cages[i]; i += 2; j = 0; somme[0] = '\0'; somme[1] = '\0'; somme[2] = '\0'; while (cages[i] != ' ' && cages[i] != '\0') { somme[j] = cages[i]; j++; i++; } ajouter_somme_cage(ls_cages, lettre_to_index(lettre), atoi(somme)); i++; } } bool est_valide(int grille[TAILLE][TAILLE], int cage_index[TAILLE][TAILLE], liste_cages_t *ls_cages, int x, int y, int num) { int i; // Vérifier la ligne et la colonne for (i = 0; i < TAILLE; i++) { if (grille[i][y] == num || grille[x][i] == num) { return false; } } // Vérifier la sous-grille int x0 = (x / 3) * 3; int y0 = (y / 3) * 3; for (i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (grille[x0 + i][y0 + j] == num) { return false; } } } // Vérifier les cages return somme_cage_possible(ls_cages, cage_index[x][y], num); } bool resolution(int grille[TAILLE][TAILLE], int cage_index[TAILLE][TAILLE], liste_cages_t *ls_cages) { int i, j, num; for (i = 0; i < TAILLE; i++) { for (j = 0; j < TAILLE; j++) { if (grille[i][j] == 0) { for (num = 1; num <= 9; num++) { if (est_valide(grille, cage_index, ls_cages, i, j, num)) { grille[i][j] = num; if (resolution(grille, cage_index, ls_cages)) { return true; } else { grille[i][j] = 0; } } } return false; } } } return true; } void afficher_grille(int grille[TAILLE][TAILLE]) { int i, j; for (i = 0; i < TAILLE; i++) { for (j = 0; j < TAILLE; j++) { printf("%d", grille[i][j]); } printf("\n"); } } int main() { int grille[TAILLE][TAILLE]; int cage_index[TAILLE][TAILLE]; liste_cages_t ls_cages; init_liste_cages(&ls_cages); lire_grille(grille, cage_index, &ls_cages); resolution(grille, cage_index, &ls_cages); afficher_grille(grille); return 0; }
3. Tests
3.1. Compilation
Compilation avec gcc :
✅ Compilation sans erreur/warning OK
3.2. Test de Codingame
3.2.1. Test 1 : Easy
Input:
56..1..2. aabbccdde ..72..68. afghhiide ..2.87.15 jfggklmme ......3.9 jjgnklopp .7....2.. qqgnooorr 9.634.8.. stuuvwwxx 2.9..8... stuuvvwyz ..41.2... sAuuByyyz .8.4...3. CADDBEEFF a=12 b=17 c=4 d=14 e=15 f=13 g=19 h=7 i=10 j=16 k=10 l=13 m=10 n=15 o=15 p=13 q=11 r=11 s=18 t=3 u=28 v=15 w=20 x=8 y=22 z=12 A=11 B=13 C=6 D=9 E=10 F=5
Expected output:
568913427 197254683 342687915 851726349 473891256 926345871 219538764 734162598 685479132
Actual output:
568913427 197254683 342687915 851726349 473891256 926345871 219538764 734162598 685479132
Run test:
Test 1: Easy ✅ Test passed
3.2.2. Test 2 : Medium
Input:
.1..65..7 abcccdeee 6.7....15 fbbgddhie .54..1..3 fjggdkhil ......... fjmgkknil 16..3.5.. ooppqqnrr ..8..2... sotuvqnnw 3......7. xxtuyyzzA ...3..... BCDDEFzAA .2.1..349 BBDGGGzzz a=9 b=16 c=17 d=21 e=18 f=12 g=18 h=15 i=12 j=12 k=15 l=5 m=9 n=19 o=10 p=6 q=12 r=17 s=5 t=13 u=15 v=1 w=4 x=7 y=11 z=33 A=12 B=17 C=9 D=10 E=7 F=4 G=14
Expected output:
913865427 687243915 254791683 479586132 162437598 538912764 345629871 891374256 726158349
Actual output:
913865427 687243915 254791683 479586132 162437598 538912764 345629871 891374256 726158349
Run test:
Test 2: Medium ✅ Test passed
3.2.3. Test 3 : Hard
Input:
........1 abbcdddef ....5.... agccchfff ...3..... ijjjhhkkl .......9. ijmmnnokp ..6...... qrrsttuvv ....9.7.. qwxsyuuzA .....3... qwxxyBBCA ......... qDxEEBBCC ..8..5... DDFFFBGHH a=14 b=9 c=17 d=22 e=3 f=20 g=3 h=9 i=6 j=21 k=19 l=7 m=9 n=11 o=4 p=6 q=16 r=15 s=11 t=11 u=10 v=9 w=9 x=20 y=16 z=5 A=5 B=22 C=19 D=17 E=11 F=15 G=3 H=11
Expected output:
672489531 831752649 549316827 157238496 396547218 284691753 415873962 763924185 928165374
Actual output:
672489531 831752649 549316827 157238496 396547218 284691753 415873962 763924185 928165374
Run test:
Test 3: Hard ✅ Test passed
3.2.4. Test 4 : Expert
Input:
......... abbcccddd ......... aabefghii ......... jkkefghil ......... jmnnnooil ......... mmmppqqrl ......... sttpuuqrr ......... svwwwxxxy ......... zvvABBCCy ......... zDDAAEEFF a=7 b=23 c=13 d=13 e=5 f=10 g=17 h=11 i=28 j=12 k=8 l=16 m=20 n=9 o=11 p=22 q=14 r=8 s=15 t=13 u=4 v=17 w=11 x=16 y=8 z=4 A=19 B=10 C=7 D=13 E=15 F=6
Expected output:
496175238 218369547 753248691 531627489 827594316 649813752 962451873 374982165 185736924
Actual output:
496175238 218369547 753248691 531627489 827594316 649813752 962451873 374982165 185736924
Run test:
Test 4: Expert ❌ Test failed (timeout)
3.2.5. Résumé global
✅ Test 1 : Easy passed ✅ Test 2 : Medium passed ✅ Test 3 : Hard passed ❌ Test failed (timeout) Passed: 3 (75.0%) Failed: 1 (25.0%)
Le dernier test echoue par manque de temps.
4. Conclusion
Lors de cet exercice, j’ai pu mettre en place un algorithme récursif pour résoudre un killer sudoku. Pour résoudre le dernier test, qui échoue, j’ai testé différentes solution qui n’ont pas fonctionné.
- Limiter le nombre de solutions possibles à tester (1-9) pour chaque case
- Trouver des solutions évidentes (ex: 1 seul chiffre possible pour une case) ou utiliser la règle du 45.
Retour vers ma page Web personnelle
Crédits :
- Template : github