Cliquer ici pour imprimer

Dernière modification : 19/05/2020 à 17:16

Première version fonctionnelle

Lecture / ecriture dans / depuis un fichier

La bibliothèque standard d'entrées/sorties du C (stdio.h pour standard input output) fournit des fonctions pour que le programme puisse lire, c'est à dire récupérer des données, et écrire, c'est à dire envoyer et/ou stocker des données, dans un fichier.

Vous connaissez déjà ces fonctions : printf pour écrire, et scanf pour lire.

En réalité, printf et scanf sont des versions proxy de fprintf et fscanf.

Extrait du résultat de la commande man 3 printf :

PRINTF(3) Linux Programmer's Manual PRINTF(3)

NAME printf, fprintf, dprintf, sprintf, snprintf, vprintf, vfprintf, vdprintf, vsprintf, vsnprintf - formatted output conversion

SYNOPSIS

   #include <stdio.h>

   int printf(const char *format, ...);
   int fprintf(FILE *stream, const char *format, ...);
   int dprintf(int fd, const char *format, ...);
   int sprintf(char *str, const char *format, ...);
   int snprintf(char *str, size_t size, const char *format, ...);

Vous voyez que la fonction fprintf a une signature très similaire à printf, elle prend un paramètre supplémentaire de type FILE* appellé stream qui représente un fichier ouvert en lecture et/ou écriture.

Un programme a systématiquement au moins trois fichiers ouverts, et peut donc utiliser trois variables gloables de type FILE* :

Par défaut, stdin est le clavier, stdout et stderr sont tous les deux l'écran (le terminal en réalité). Ceci peut être modifié lors du lancement du programme, par exemple pour rediriger la sortie d'erreur standard d'un programme vers un fichier de log, on pourra le lancer ainsi :

$> ./mon_programme 2> error_log

Pour ouvrir d'autres fichiers, et donc obtenir plus de variables de type FILE*, il faudra utiliser la fonction fopen(). Lorsque l'on a fini d'utiliser le fichier, il faut penser à appeler la fonction fclose() pour libérer les ressources proprement.

    char line[MAX_LINE_SIZE];
    int taille_line;
    FILE * fichier = fopen(...);
    ...
    if(fgets(line,MAX_LINE_SIZE,fichier) == NULL){
        /* il y a une erreur de lecture */
        fprintf(stderr,"erreur de lecture du fichier"); exit(1);
    }
    ...
    fclose(fichier);

fgets met le caractère \n dans la ligne ainsi qu'un \0, permettant ainsi facilement d'afficher la ligne lue, par exemple avec printf("%s",line);

fgets est ainsi très pratique lorsqu'on connaît la taille max d'une ligne. Lorsqu'on ne connait pas il y a d'autre fonction comme getline(), qui ne nous intéresse pas pour le moment.

Application au mini projet

Ajoutez une option -i à votre programme prenant en paramètre un nom de fichier. Le programme lira alors la grille initiale dans ce fichier, ligne à ligne grâce a la fonction fgets(), au lieu d'utiliser celle qui est en dur dans le code. Pour l'instant le programme n'acceptera que des grilles qui font toutes la même taille (donnée par les constantes NBL et NBC)

Détection des collisions

Nous allons modifier les fonctions move_snake() et crawl() pour que le jeu prenne fin lorsque le serpent rencontre un mur ou se mange lui même, et également pour permettre au serpent de manger les fruits, et que le jeu s'arrete lorsque tous les fruits sont mangés.

Interaction du joueur

Le squelette de boucle de jeu donné à la fin du tp2 nous permet de récupérer a chaque passage une touche clavier éventuellement pressée par le joueur. Pour l'instant elle ne nous sert qu'à quitter si le joueur a appuyé sur ESC.

Votre boucle devrait maintenant avoir une structure ressemblant à cela :


    /* ... début de de la fonction */

    MLV_create_window( "SNAKE", "3R-IN1B", width, height );
    MLV_change_frame_rate( 24 );

    int loop_count=0; /* TODO : a mettre au début de la fonction pour respecter la norme ANSI */

    while(
        MLV_get_event (
            &touche, NULL, NULL,
            NULL, NULL,
            NULL, NULL, NULL,
            NULL
        ) == MLV_NONE ||
        touche != MLV_KEYBOARD_ESCAPE
    ){


        MLV_clear_window( MLV_COLOR_BROWN );


        loop_count = (loop_count+1)%DIFFICULTY;

        if(loop_count ==0){

            /* TODO : appel à move_snake() et fin du jeu en fonction de la valeur renvoyée */

        }

        draw_grid(grid);        
        MLV_actualise_window();


        switch(touche)
        {
            case MLV_KEYBOARD_DOWN :
                /* TODO change la direction du serpent*/  
                break;
            case : /*TODO a completer */
        }


        touche = MLV_KEYBOARD_NONE;
        MLV_delay_according_to_frame_rate();
    }


    MLV_free_window();


    /* fin de la fonction */

Rendu

Vous avez normalement maintenant une version fonctionnelle du jeu. Vous avez jusqu'à dimanche soir pour déposer sur Blackboard votre projet dans son état actuel (une seule tentative, attention).

La deuxième partie du projet va consister à modifier l'implémentation du serpent et de la grille afin de pouvoir charger dynamiquement des nouveaux niveaux, de lever la limitation sur la taille de la grille, et de permettre au serpent de grandir lorsqu'il mange des fruits.