Killer Sudoku Solver

Page web ISIMA
Fichier .org source
Défi de programmation - Killer Sudoku Solver

header.jpg

1. Introduction

Le but de ce problème est de résoudre une variante du sudoku.
Cette variante hérite des règles de base du sudoku, à savoir qu’un chiffre se trouve une unique fois dans chaque ligne colonne, et sous-grille.
Mais cette variante fournie également des « cages », dans laquelle la somme de ses chiffres doit être égale à un nombre donné (chaque case ne doit pas forcément atteindre toute la même somme).

Exemple de Killer Sudoku :

killerSudoku.png

2. Approche utilisée

2.1. Description synoptique

Le principe de mon algorithme est de tester les cases une par une, si il y a déjà un chiffre on peut passer à la suivante, mais si la case est vide (si elle contient un 0), on test chaque chiffres de 1 à 9 si il est possible sur cette case (si il n’est ni présent de la ligne, colonne, ou sous-grille et que la somme dans la cage est correcte). Ensuite on passe à la case suivante.

2.2. Algorithme

killerSudokuAlgo.png

3. Resolution

3.1. Programme

#include <stdio.h>
#include <stdlib.h>

void display(int grille[9][9]) {
    for (int i=0; i<9; ++i) {
        for (int j=0; j<9; ++j) {
            printf("%d", grille[j][i]);
        }
        printf("\n");
    }
}

int missingOnLine(int k, int grille[9][9], int i) {
    for (int j=0; j < 9; j++)
        if (grille[i][j] == k)
            return 0;
    return 1;
}

int missingOnCol(int k, int grille[9][9], int j) {
    for (int i=0; i < 9; i++)
        if (grille[i][j] == k)
            return 0;
    return 1;
}

int missingOnBlock(int k, int grille[9][9], int i, int j) {
    int i2 = i-(i%3), j2 = j-(j%3);
    for (i=i2; i < i2+3; i++)
        for (j=j2; j < j2+3; j++)
            if (grille[i][j] == k)
                return 0;
    return 1;
}

int valueOf(char cage[], char cible) {
    int i=0;
    char temp[3];
    while(cage[i]!=cible && i<251){
        ++i;
    }
    ++i;
    temp[0] = cage[i+1];
    temp[1] = cage[i+2];
    int rep = atoi(temp);
    return rep;
}

int possibleCage(int tab[][9],int ligne, int colonne, int entier, char tab_cages[][9], char cages[]){
    char loc = tab_cages[ligne][colonne];
    int valeur_max = valueOf(cages,loc);
    int somme = 0;
    int rec = 0;
    int i,j;
    for(i=0;i<9;++i){
        for(j=0;j<9;++j){
            if(tab_cages[i][j]==loc){
                somme += tab[i][j];
                if (somme+entier > valeur_max) return 0;
                if (tab[i][j] == entier) {
                    return 0;
                }
                if(tab[i][j] == 0){
                    ++rec;
                }
            }
        }
    }
    int test = somme+entier == valeur_max;
    if (rec){
        test = somme+entier <= valeur_max;
    }
    return test;
}

int possible(int grille[9][9], char cages[9][9], char keys[251], int pos, int k) {
    int i = pos/9, j = pos%9;
    int col = missingOnCol(k,grille,j);
    int line = missingOnLine(k,grille,i);
    int block = missingOnBlock(k,grille,i,j);
    int sum = possibleCage(grille, i, j, k, cages, keys);
    return col && line && block && sum;
}

int isValid(int grille[9][9], int position, char cages[9][9], char keys[251]) {
    if (position >= 9*9)
        return 1;

    int i = position/9, j = position%9;

    if (grille[i][j] != 0)
        return isValid(grille, position+1, cages, keys);

    for (int k=1; k < 10; ++k) {
        if (possible(grille, cages, keys, position, k)) {
            grille[i][j] = k;

            if (isValid(grille, position+1, cages, keys))
                return 1;

            grille[i][j] = 0;
        }
    }
    grille[i][j] = 0;
    return 0;
}

int main()
{
    int grille[9][9];
    char cages[9][9];

    for (int i = 0; i < 9; i++) {
        char grid_line[10];
        char grid_cages[10];
        scanf("%s%s", grid_line, grid_cages); fgetc(stdin);

        for (int j=0; j<9; ++j) {
            grille[j][i] = (grid_line[j] == '.') ? 0 : grid_line[j] - '0';
            cages[j][i] = grid_cages[j];
        }
    }
    char keys[251];
    scanf("%[^\n]", keys);

    isValid(grille, 0, cages, keys);
    display(grille);

    return 0;
}

3.2. Test

A partir du test suivant (killerSudoku.txt):

.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

Le programme complète la grille en fonction des cages et l’affiche :

913865427
687243915
254791683
479586132
162437598
538912764
345629871
891374256
726158349

4. Bilan

Le problème a été plus dur à résoudre que le sudoku classique, surtout d’un point de vue rapidité de l’algorithme.
Mon algorithme fonctionne mais dépasse le temps maximum sur certains tests de Codingame.

Auteur: Rémi SCHIRRA

Created: 2024-05-03 ven. 13:45