Dernière modification : 19/05/2020 à 15:16
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.
fscanf(...," %c",...)
et printf("%c",...)
dans une boucle. Comment sait on que l'on est arrivé à la fin du fichier ? Indice : lisez la section "RETURN VALUE" de la page de manuel de la fonction scanf()\n
. Pour cela vous pouvez utiliser entre autre fgets(), qui s'utilise ainsi : 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.
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
)
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.
move_snake
pour qu'elle renvoie un enum Element
(ou un Element
si vous avez défini un tel type cf tp2). La fonction
sauvegardera l'élement à la position de la tête du serpent après sont appel à
crawl()
avant de le remplacer par SNAKE
pour pouvoir le renvoyermove_snake()
nb_fruit
qui devra être initialisée à la bonne valeur (pour cela vous devrez faire un parcourt de la grille pour compter les fruits. Vous pouvez aussi modifier la fonction qui lit la grille depuis un fichier pour qu'elle compte les fruits en même temps.move_snake()
a renvoyé un fruit, décrémentez cette variable. Si la variable passe à zero, la boucle de jeu devra s'arrêter. Vous afficherez dans la fenêtre un texte de votre choix indiquant que le joueur a gagné.move_snake()
a renvoyé un mur ou le serpent, la boucle de jeu devra s'arreter. Vous afficherez dans la fenêtre un texte de votre choix indiquant que le joueur a perdu, et pourquoi (mur ou serpent).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.
switch
) à la fin de la boucle sur la valeur de la variable touche
.
Les valeurs correspondant aux flèches directionnelles du clavier sont : MLV_KEYBOARD_DOWN, MLV_KEYBOARD_RIGHT, MLV_KEYBOARD_LEFT et MLV_KEYBOARD_UP.
Si la variable touche
correspond à l'une de ces valeurs, vous changerez le champs direction de votre serpent pour lui donner la valeur appropriée.x
passages. Plus x
sera petit, plus la difficulté sera grande. Commencez avec une valeur de x
égale à 6.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 */
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.