Rapport : Suguru solver
Table des matières
1. Introduction
1.1. Principe de focntionnment
Url du challenge : https://www.codingame.com/ide/puzzle/suguru-solver
L’objectif de ce challenge est de résoudre une grille de Suguru
Rappel des règles du Suguru :
- La grille est composée de différentes zones, appelées cages.
- Chaque cage est de 1 à 6 cellules et contient les chiffres de 1 à la taille de la cage. C’est-à-dire qu’une cage à 2 cellules contient les chiffres 1 et 2, et une cage à 5 cellules contient les chiffres 1 à 5.
- Les cellules adjacentes, même en diagonale, peuvent ne jamais contenir le même chiffre.
1.2. Données fournies
On reçoit en entrée une grille de Suguru incomplète. la premiere ligne contient deux entiers, la largeur et la hauteur de la grille. Les lignes suivantes contient la grille, avec un point pour une cellule vide, et un chiffre pour une cellule déjà remplie suivit d’une lettre indiquant la cage à laquelle elle appartient.
4 5 G. G4 G1 G. R. G. B. B. G. R. R4 B. G. R. R2 B. G3 G. R. B.
1.3. Données à fournir
On doit fournir en sortie une grille de Suguru complète.
2413 1525 2341 1523 3414
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 *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 creer des tableaux 2D et de les detruire. En effet, selon les tests la taille de la grille peut varier de 4 à 20.
int **creer_grille(int largeur, int hauteur) { int **grille = malloc(hauteur * sizeof *grille); for (int i = 0; i < hauteur; i++) { grille[i] = malloc(largeur * sizeof *grille[i]); } return grille; } void detruire_grille(int **grille, int hauteur) { for (int i = 0; i < hauteur; i++) { free(grille[i]); } free(grille); }
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
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++; }
On définit ensuite la fonction qui va nous permettre de lire les donnnées d’entrée et de les stocker dans les grilles.
void lire_grille(int **grille, int **cage_index, int l, int h) { int i, j, k; for (i = 0; i < h; i++) { char line[60]; scanf("%[^\n]", line); fgetc(stdin); for (j = 0; j < l; j++) { k = j * 3; cage_index[i][j] = line[k] - 'A' + 26; if (line[k + 1] == '.') { grille[i][j] = 0; } else { grille[i][j] = line[k + 1] - '0'; } } } }
On definit ensuite une fonction completioncage qui chnage les numero des cages pour qu’il n’y ait pas de doublons. On en profite pour remplir la liste des cages. La propagation est recursive et se propage dans les cases adjacentes si l’index de la case est similaire à celui de la case actuelle (les cases adjacentes sont dans la meme cage)
void propagation(int **grille, int **cage_index, int **new_index, liste_cages_t *ls_cages, int l, int h, int x, int y) { // propagation au dessus, en dessous, à gauche et à droite de la case si l'index de la case est similaire à celui de la case actuelle // mais pas en diagonale int i, j; for (i = x - 1; i <= x + 1; i++) { for (j = y - 1; j <= y + 1; j++) { if (i >= 0 && i < h && j >= 0 && j < l) { if (i == x || j == y) { if (new_index[i][j] == 0 && cage_index[i][j] == cage_index[x][y]) { new_index[i][j] = new_index[x][y]; ajouter_coord_cage(ls_cages, new_index[x][y], &grille[i][j]); propagation(grille, cage_index, new_index, ls_cages, l, h, i, j); } } } } } } void completion_cage(int **grille, int **cage_index, int l, int h, liste_cages_t *ls_cages) { int i, j; int **new_index = creer_grille(l, h); int prochain_index = 1; // Initialisation de la grille de l'index des cages à 0 for (i = 0; i < h; i++) { for (j = 0; j < l; j++) { new_index[i][j] = 0; } } for (i = 0; i < h; i++) { for (j = 0; j < l; j++) { if (new_index[i][j] == 0) { new_index[i][j] = prochain_index; propagation(grille, cage_index, new_index, ls_cages, l, h, i, j); ajouter_coord_cage(ls_cages, prochain_index, &grille[i][j]); prochain_index++; } } } for (i = 0; i < h; i++) { for (j = 0; j < l; j++) { cage_index[i][j] = new_index[i][j]; } } detruire_grille(new_index, h); }
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 d’abord que la valeur n’est pas présente dans les cases voisines (3x3 centré sur la case) On vérifie ensuite que la valeur n’est pas présente dans la cage Enfin que la valeur n’est pas superieure à la taille de la cage
bool est_valide(int **grille, int **cage_index, liste_cages_t *ls_cages, int x, int y, int num, int l, int h) { // Verifier voisin 3x3 centré sur la case int i, j; for (i = x - 1; i <= x + 1; i++) { for (j = y - 1; j <= y + 1; j++) { if (i >= 0 && i < h && j >= 0 && j < l && grille[i][j] == num) { return false; } } } // Vérifier la cage int index = cage_index[x][y]; for (i = 0; i < ls_cages->cages[index].taille; i++) { if (*(ls_cages->cages[index].coords[i]) == num) { return false; } } // la valeur ne dépasse pas la taille de la cage if (num > ls_cages->cages[index].taille) { return false; } return true; }
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 à 6. 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, int **cage_index, liste_cages_t *ls_cages, int l, int h) { int i, j, num; for (i = 0; i < h; i++) { for (j = 0; j < l; j++) { if (grille[i][j] == 0) { for (num = 1; num <= 6; num++) { if (est_valide(grille, cage_index, ls_cages, i, j, num, l, h)) { grille[i][j] = num; if (resolution(grille, cage_index, ls_cages, l, h)) { 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, int l, int h) { int i, j; for (i = 0; i < h; i++) { for (j = 0; j < l; j++) { printf("%-1d", grille[i][j]); } printf("\n"); } }
On définit enfin la fonction principale qui va nous permettre de résoudre la grille.
int main() { int l, h; scanf("%d%d", &l, &h); fgetc(stdin); int **grille = creer_grille(l, h); int **cage_index = creer_grille(l, h); liste_cages_t ls_cages; init_liste_cages(&ls_cages); lire_grille(grille, cage_index, l, h); completion_cage(grille, cage_index, l, h, &ls_cages); resolution(grille, cage_index, &ls_cages, l, h); afficher_grille(grille, l, h); detruire_grille(grille, h); detruire_grille(cage_index, h); return 0; }
2.1. Implémentation complète
#include <stdio.h> #include <stdbool.h> #include <string.h> #include <stdlib.h> #define MAX_CAGES 100 #define MAX_COORDS_CAGE 6 typedef struct { int taille; // Nombre de d'element dans 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 **creer_grille(int largeur, int hauteur) { int **grille = malloc(hauteur * sizeof *grille); for (int i = 0; i < hauteur; i++) { grille[i] = malloc(largeur * sizeof *grille[i]); } return grille; } void detruire_grille(int **grille, int hauteur) { for (int i = 0; i < hauteur; i++) { free(grille[i]); } free(grille); } 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 lire_grille(int **grille, int **cage_index, int l, int h) { int i, j, k; for (i = 0; i < h; i++) { char line[60]; scanf("%[^\n]", line); fgetc(stdin); for (j = 0; j < l; j++) { k = j * 3; cage_index[i][j] = line[k] - 'A' + 26; if (line[k + 1] == '.') { grille[i][j] = 0; } else { grille[i][j] = line[k + 1] - '0'; } } } } void propagation(int **grille, int **cage_index, int **new_index, liste_cages_t *ls_cages, int l, int h, int x, int y) { // propagation au dessus, en dessous, à gauche et à droite de la case si l'index de la case est similaire à celui de la case actuelle // mais pas en diagonale int i, j; for (i = x - 1; i <= x + 1; i++) { for (j = y - 1; j <= y + 1; j++) { if (i >= 0 && i < h && j >= 0 && j < l) { if (i == x || j == y) { if (new_index[i][j] == 0 && cage_index[i][j] == cage_index[x][y]) { new_index[i][j] = new_index[x][y]; ajouter_coord_cage(ls_cages, new_index[x][y], &grille[i][j]); propagation(grille, cage_index, new_index, ls_cages, l, h, i, j); } } } } } } void completion_cage(int **grille, int **cage_index, int l, int h, liste_cages_t *ls_cages) { int i, j; int **new_index = creer_grille(l, h); int prochain_index = 1; // Initialisation de la grille de l'index des cages à 0 for (i = 0; i < h; i++) { for (j = 0; j < l; j++) { new_index[i][j] = 0; } } for (i = 0; i < h; i++) { for (j = 0; j < l; j++) { if (new_index[i][j] == 0) { new_index[i][j] = prochain_index; propagation(grille, cage_index, new_index, ls_cages, l, h, i, j); ajouter_coord_cage(ls_cages, prochain_index, &grille[i][j]); prochain_index++; } } } for (i = 0; i < h; i++) { for (j = 0; j < l; j++) { cage_index[i][j] = new_index[i][j]; } } detruire_grille(new_index, h); } bool est_valide(int **grille, int **cage_index, liste_cages_t *ls_cages, int x, int y, int num, int l, int h) { // Verifier voisin 3x3 centré sur la case int i, j; for (i = x - 1; i <= x + 1; i++) { for (j = y - 1; j <= y + 1; j++) { if (i >= 0 && i < h && j >= 0 && j < l && grille[i][j] == num) { return false; } } } // Vérifier la cage int index = cage_index[x][y]; for (i = 0; i < ls_cages->cages[index].taille; i++) { if (*(ls_cages->cages[index].coords[i]) == num) { return false; } } // la valeur ne dépasse pas la taille de la cage if (num > ls_cages->cages[index].taille) { return false; } return true; } bool resolution(int **grille, int **cage_index, liste_cages_t *ls_cages, int l, int h) { int i, j, num; for (i = 0; i < h; i++) { for (j = 0; j < l; j++) { if (grille[i][j] == 0) { for (num = 1; num <= 6; num++) { if (est_valide(grille, cage_index, ls_cages, i, j, num, l, h)) { grille[i][j] = num; if (resolution(grille, cage_index, ls_cages, l, h)) { return true; } else { grille[i][j] = 0; } } } return false; } } } return true; } void afficher_grille(int **grille, int l, int h) { int i, j; for (i = 0; i < h; i++) { for (j = 0; j < l; j++) { printf("%-1d", grille[i][j]); } printf("\n"); } } int main() { int l, h; scanf("%d%d", &l, &h); fgetc(stdin); int **grille = creer_grille(l, h); int **cage_index = creer_grille(l, h); liste_cages_t ls_cages; init_liste_cages(&ls_cages); lire_grille(grille, cage_index, l, h); completion_cage(grille, cage_index, l, h, &ls_cages); resolution(grille, cage_index, &ls_cages, l, h); afficher_grille(grille, l, h); detruire_grille(grille, h); detruire_grille(cage_index, h); 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 : 4x5
Input:
4 5 G. G4 G1 G. R. G. B. B. G. R. R4 B. G. R. R2 B. G3 G. R. B.
Expected output:
2413 1525 2341 1523 3414
Actual output:
2413 1525 2341 1523 3414
Run test:
Test 1: 4x5 ✅ Test passed
3.2.2. Test 2 : 8x8
Input:
8 8 G. G. G. M. M5 R. R. G. R. R. R. R. M. M. G. G. G. M. R. C. C4 M. G. R. G. M. M. C. C. B. B. R. G. M. M. C. B. B. R. R. R. B. G. G. B. G. G4 G. R. B. B. G. R. R. G. G. R. B. B. G. G. R. R. B.
Expected output:
21345121 34212343 21534121 34212354 15354123 32412341 15354152 24123231
Actual output:
21345121 34212343 21534121 34212354 15354123 32412341 15354152 24123231
Run test:
Test 2: 8x8 ✅ Test passed
3.2.3. Test 3 : 15x10
Input:
15 10 R. B5 B. B4 B. R. R4 R. B. B5 B. R1 R4 R. B. R4 R. B1 B. R. R6 G. R. B. G4 R. R. G. R6 B1 R. R6 R. G5 G. G. G2 B. B. G3 G. G. G. B. B4 B. B1 B. G. B. B. B. R1 R. R6 C1 C. C. B5 B. B5 B. R. R. R. B. R. R. G. R. C. C6 R. R1 R. B. R. R. C. R. B6 B. G. G. G. C. R. R. G. R. G2 G. C. C1 C. C. R. R3 G. B. B. G. G. G. G4 C. G4 G. C. B3 B. B4 R. R6 R. B. R. R. R. G. C2 C. C. B. B. R. B. G. R. G. R. R. B. R. B. C. C6 R3 R5 R. R. R. G5 G. G. G. B6 B2 B4 B.
Expected output:
156423423561452 431316151423261 562543242356134 213621515613456 542143232426214 636356451353536 215142134212614 343635426534325 251212634215163 463564151646245
Actual output:
156423423561452 431316151423261 562543242356134 213621515613456 542143232426214 636356451353536 215142134212614 343635426534325 251212634215163 463564151646245
Run test:
Test 3: 15x10 ✅ Test passed
3.2.4. Test 4 : 20x20
Input:
20 20 R. R6 R. B6 B. R. B. B6 B. R. R. B. B5 R6 R. R1 G. G. G. B. R3 B. B. B2 R. R. R. B. B. B. G6 B. B. R2 R. R. B. G. G. B2 R5 G. B. G. R. G. G3 R. R. G. G. B3 B. G. G. G. B. B5 R. B5 R. G. G5 G. R. G. G. R. B. G. B. R. R. R. G. G. B. B. R. B. B. B. G. R. B. B. G. B4 B. G. B4 B. R. R. Y. R4 R. B6 R. B. B5 R. R3 R1 R. Y. G. B. B. G. B. B6 Y. Y. Y. Y. R. R. Y. B4 B. B. G. R. Y. Y. Y. G1 B. R. B. R. B. B5 B. R. R. G. Y. Y. R. B. G4 G. Y2 Y. R. G3 G. R. R. R5 G. B. B. G3 G. G. G. Y. R. R. G. G. R. R. R. G. B6 B. R. G4 G. G1 B6 R. G. B. B. G. R. B3 G. B. B5 R. G. G5 B. B. B2 G. R. G. R. R. R2 R. G. G. B. B. R. B. B1 Y. Y. R. R. G. B. R6 R. R. B. Y. Y. R. G3 G. B. B5 R. B. G. R. Y. R. G. G4 R. R. B. B. B3 B. Y4 Y. Y. Y. G. B. R6 B. G5 R. Y. Y3 G. Y. Y. Y. B. R. G. G. G. G. R1 R4 G. G. R. R3 G. G. R. Y. G. G. Y. G. R. R4 R. Y. G5 G. R3 R. G. G. B. R. G. G. R4 B. B. B6 G. G5 R. R2 Y. Y. Y. B1 R. R. R. B5 B. B. B. B. R. R6 B. B. G2 G. G. B. Y. Y1 B. B. B5 G. R. G. G. G. G. R. R. G. G. B3 R1 B. B6 B. B. R. B. B6 Y. G. R. B. G6 G. R. G2 G. G. G6 R. R. B. G. R. R3 R4 R. Y. Y. G. B1 B. B. R. R4 R. R3 B. B. R4 R. G. G. G. R. G. G. Y5 Y. R. B. B. G. G. G. G. R. B. B3 R. G5 G1 B. B. B. G. G4 G. Y. R.
Expected output:
26465136121456412531 31323242456212353142 56416131314365142535 12535452525243231426 36462164134315142613 52314523252624353524 41653141631415216131 32412653426523432454 41534312631461616121 23265265452325352454 16431413131641413632 25126252645352364256 14645163131216123614 25232352524634545435 43454141461512623162 15132626252343514251 34251313131561263642 25636245652425341213 13214531243634253562 46432124365121314231
Actual output:
26465136121456412531 31323242456212353142 56416131314365142535 12535452525243231426 36462164134315142613 52314523252624353524 41653141631415216131 32412653426523432454 41534312631461616121 23265265452325352454 16431413131641413632 25126252645352364256 14645163131216123614 25232352524634545435 43454141461512623162 15132626252343514251 34251313131561263642 25636245652425341213 13214531243634253562 46432124365121314231
Run test:
Test 4: 20x20 ❌ Test failed (timeout)
3.2.5. Résumé global
✅ Test 1 : 4x5 passed ✅ Test 2 : 8x8 passed ✅ Test 3 : 15x10 passed ❌ Test failed (timeout) Passed: 3 (75.0%) Failed: 1 (25.0%)
Le dernier test echoue par manque de temps.
3.3. Test Valgrind
✅ Valgrind check ==13237== Memcheck, a memory error detector ==13237== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==13237== Using Valgrind-3.20.0 and LibVEX; rerun with -h for copyright info ==13237== Command: ./temp/code ==13237== 156423423561452 431316151423261 562543242356134 213621515613456 542143232426214 636356451353536 215142134212614 343635426534325 251212634215163 463564151646245 ==13237== ==13237== HEAP SUMMARY: ==13237== in use at exit: 0 bytes in 0 blocks ==13237== total heap usage: 35 allocs, 35 frees, 10,232 bytes allocated ==13237== ==13237== All heap blocks were freed -- no leaks are possible ==13237== ==13237== For lists of detected and suppressed errors, rerun with: -s ==13237== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
4. Solution de la communauté
Malgré le fait que le dernier test échoue, j’ai pu soumettre ma solution à Codingame et elle a été validée. Mais il n’y aucun code en C dans la communauté.
5. Conclusion
Lors de cet exercice, j’ai pu mettre en place un algorithme récursif pour résoudre un suguru. 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-6) pour chaque case a moins de 6 solutions possibles si la taille de la cage est inferieur.
- Trouver des solutions évidentes (ex: 1 seul chiffre possible pour une case).
Retour vers ma page Web personnelle
Crédits :
- Template : github