IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

Scrabble : revue de code


Sujet :

C

  1. #1
    Futur Membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Avril 2015
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Avril 2015
    Messages : 2
    Points : 5
    Points
    5
    Par défaut Scrabble : revue de code
    Bonjour à tous,
    Je suis ce que j'appelle un débutant++, dans le sens ou je pratique le C depuis quelques mois, mais sans grande expérience.
    Pour m'entrainer, je crée donc des petits programmes sans intérêts.
    Mon principal problème, c'est que je ne sais jamais si je suis dans le vrai ou non, n'ayant pas de correcteur.
    Je souhaite donc vous soumettre un programme fonctionnel, avec je l'espere des retours sur ma façon de coder.
    Comme le titre l'indique, il s'agit juste d'un programme qui permet à l'utilsateur de saisir des lettres, afin que le programme lui donne
    tous les mots qu'il est possible de générer.
    Merci d'avance.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
     
     
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <unistd.h>
    #include <ctype.h>
     
    /*
     *Fake Scrabble renvoi des mots que l'on peut faire à partir de lettre fourni par l'utilisateur. (en Anglais)
     *Ces mots apportent des points selon leur compléxité.
    */
     
    /* Constante */
    #define MAX_WORD_LENGTH 45
     
    /* Variable Globale */
    char word_result[MAX_WORD_LENGTH + 1];
     
    /* Declaration des fonctions */
    static void purger(void);
    static void clean(char *chaine);
    static int find_word_with_pattern(const char *word, const char *pattern);
    static void usage(void);
    static void valid_alpha(char *pattern);
    static int score(char *word);
     
    /* main */
    int main(int ac, char **av)
    {
        FILE *fp;
        char *pattern;
     
        if (ac == 2)
        {
            fp = fopen("./words.txt", "r");
            if (fp == NULL)
                printf("fopen failed : %s\n",strerror(errno));
            else
            {
                while (!feof(fp))
                {
                    pattern = av[1];
     
                    valid_alpha(pattern);
                    fgets(word_result, sizeof(word_result), fp);
                    clean(word_result);
                    if (find_word_with_pattern(word_result, pattern) == 1)
                    {
                        if(strlen(word_result) == 1) //Une seul lettre
                            printf("\nLetter : %c\n\n", toupper(word_result[0]));
                        else if(strlen(word_result) > 1)
                            printf("Valid result : [%s] ----> %d point(s)\n",word_result, score(word_result));
                    }
                    memset(word_result, 0, MAX_WORD_LENGTH + 1);
                }
            }
        }
        else
        {
            usage();
            exit(EXIT_FAILURE);
        }
        fclose(fp);
        return 0;
    }
     
     
    /* Implémentations des fonctions */
     
     
    /* vider le buffer de getchar() */
    static void purger(void)
    {
        int c;
     
        while ((c = getchar()) != '\n' && c != EOF);
    }
     
    static void clean(char *chaine)
    {
        char *p = strchr(chaine, '\n');
     
        if (p)
            *p = 0;
        else
            purger();
    }
     
     
    /* On compare les lettres fournies à chaque mot dans le dico */
    static int find_word_with_pattern(const char *word, const char *pattern)
    {
        char *word_dup;
        char *pattern_dup;
        int i;
     
        word_dup = strdup(word);
        pattern_dup = strdup(pattern);
     
        if (word == NULL || pattern == NULL)
            return -1;
        for (i = 0; i < strlen(pattern_dup); i++)
        {
            for (int j = 0; j < strlen(word_dup); j++)
            {
                if (word_dup[j] == '*')
                    continue;
                if (word_dup[j] == pattern_dup[i])
                {
                    word_dup[j] = '*';
                    break;
                }
            }
        }
        i = 0;
        while (word_dup[i])
        {
            if (word_dup[i] != '*')
                return -1;
            i++;
        }
        free(word_dup);
        free(pattern_dup);
        return 1;
    }
     
    static void usage()
    {
        printf("Only one paramater is needed (pattern)");
    }
     
     
    /* On verifie si la chaine ne contient que des caracteres alphabétiques */
    static void valid_alpha(char *pattern)
    {
     
        while (*pattern)
        {
            if(!isalpha(*pattern))
            {
                printf("pattern must contain only alphabetical characters\n");
                exit(EXIT_FAILURE);
            }
            pattern++;
        }
     
    }
     
    /* Rappel : Chaque lettre au scrabble vaut n points, score() calcule la somme pour chaque mot trouvé */
    static int score(char *word)
    {
        int value_of_letter[26] = {1,3,3,2,1,
            4,2,4,1,8,
            5,1,3,1,1,
            3,10,1,1,1,
            1,4,4,8,4,10}; // a vaut 1 pts (value_of_letter[0]), b vaut 3 pts (value_of_letter[1]), etc...
        int total_score = 0 , i = 0;
     
        while (word[i])
        {
            total_score = value_of_letter[tolower(word[i]) - 'a'] + total_score;
            i++;
        }
        return total_score;
    }

  2. #2
    Membre expert Avatar de jopopmk
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mars 2011
    Messages
    1 856
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 1 856
    Points : 3 570
    Points
    3 570
    Par défaut
    Salut,

    je ne parlerai pas d'algo (c'est ta tambouille, si c'est fonctionnel c'est bien)
    ni de mise en forme (j'utilise pas du tout les mêmes règles que toi).

    Quelques remarques plus ou moins intéressantes (plus moins que plus) :
    - dans find_word_with_pattern tu devrais faire tes strlen une fois pour toute et pas les mettre dans tes boucles for, sinon la fonction sera appelé à chaque itération,
    - dans cette même fonction tu as des cas où la mémoire n'est pas désallouée,
    - je sais pas comment fonctionnent tes words/patterns, mais le cas qui fait un continue me semble dispensable/intégrable,
    - la fonction purger, plutôt minimaliste, n'est appelée que dans clean : autant n'en faire qu'une seule fonction (en C on évite de surdécouper et d'empiler les appels de fonction),
    - a priori le memset dans ta fonction main n'a pas de raison d'être, fgets ajoute le trailing zero qui va bien.

    Quelques remarques pas intéressantes :
    - l'avantage des ++v++ c'est de pouvoir s'incruster dans n'importe quelle instruction, profites en (cf. tes while),
    - tu peux utiliser l'opérateur += pour ton calcul de score total,
    - ta fonction valid_alpha est bien violente : si ça passe pas le test tu quittes carrément l'appli.

    Enfin, y'a peut-être un peu de blindage à faire par-ci par-là.

    Bon continuation
    Plus je connais de langages, plus j'aime le C.

  3. #3
    Expert éminent
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 565
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 565
    Points : 7 648
    Points
    7 648
    Par défaut
    Bonjour,

    Sur la présentation :
    * beaucoup de bonne pratiques, c'est clair.
    * la fonction clean(), le nom n'est pas assez explicite, difficile de voir qu'elle agit aussi sur le flux stdin (mais ne voulais-tu pas vider le flux du fichier?!)
    * quand j'ai débuté l'informatique on conseillait 2 lignes de commentaires pour une ligne de code. Aujourd'hui c'est moins nécessaire, mais les commentaires d'explication sont un plus. Ton code est quand même supérieur à la moyenne.

    Sur le code :
    * la fonction feof() ne dis pas que l'on est à la fin du fichier, mais que la dernière opération s'est mal passée. Ici un fgets() est effectué après avoir extrait la dernière ligne, d'où une chaîne non remplie (c'est peut-être la raison du memset()?)
    * pourquoi faire un pattern_dup alors que tu fais que lire le pattern?
    * la fonction a un return avant les free(), aïe un memory leak.
    * quand un paramètre pointeur n'est pas utilisé pour écrire, il est plus qu'utile de l'indiquer const. C'est fait pour certaines fonctions pas pour d'autres.
    * la table value_of_letter est clairement constante et consomme une copie inutile, il faut la déclarer : static int const value_of_letter[]...
    * le valid_alpha() qui arrête l'application sur erreur de saisie, c'est violent. Ici c'est parce qu'il s'agit une donnée en ligne de commande, mais il est plus sain de remonter l'erreur au main(). Son nom peut explicite pourrait te tenter de l'utiliser indûment ailleurs.
    * la fonction score() utilise sans contrôle les caractères reçus, on ne peux pas se fier à isalpha() car en locale C détecte bien 26 lettres, mais en locale fr_FR il pourra accepter d'autres caractères.

    Dalfab

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Pour feof(), je rectifie: la fonction permet de savoir si la cause du dernier échec sur un fichier est bien une fin de fichier. Ca pourrait ne pas être le cas.

    En général, FILE est une structure contenant une série de champ d'état (souvent des bitfields), et feof() vérifie l'un d'entre eux.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

Discussions similaires

  1. Réponses: 1
    Dernier message: 29/11/2011, 21h35
  2. Réponses: 18
    Dernier message: 19/04/2007, 17h42
  3. envoyer un mail avec mon programme
    Par shrek dans le forum C++Builder
    Réponses: 8
    Dernier message: 06/12/2006, 12h27
  4. [] Utiliser AVI d'une DLL dans mon programme
    Par seb.49 dans le forum VB 6 et antérieur
    Réponses: 5
    Dernier message: 02/05/2003, 14h52
  5. Réponses: 11
    Dernier message: 17/03/2003, 10h56

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo