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 :

Date: 2023-03-03

Auteur: Jules Girard

Created: 2023-04-26 mer. 19:29