Skip to content

Passer des paramètres à un programme et les gérer, ajout du serpent et déplacements automatiques

Les arguments du main

Soit la ligne de commande :

$> ./a.out toto tata 8 9 10

Comment le programme peut il récupérer les informations supplémentaires passées sur la ligne de commande ? Avec un tableau de chaines de caractères ! Donc un entier donnant la taille du tableau, puis un tableau à deux dimensions de caractères. Le "vrai" prototype de la fonction main() est donc :

int main(int argc, char* argv[]);

Dans notre exemple de ligne de commande, lorsque le programme démarre, les variable sont initialisées ainsi :

argc : 6
argv[0] : "./a.out"
argv[1] : "toto"
argv[2] : "tata"
argv[3] : "8"
argv[4] : "9"
argv[5] : "10"
  • écrivez un programme qui affiche son nom. L'exécution doit ressembler à ça :
$> ./a.out
./a.out
$> cp a.out toto.exe
$> ./toto.exe
./toto.exe
  • écrivez un programme qui affiche tous ses paramètres en les transformant en majuscule (faites une fonction pour afficher une chaine de caractères en majuscules).

  • écrivez un programme qui affiche la somme des arguments numériques qu'il reçoit en paramètre

    $> ./a.out 8 2 3
    13
    
    Pour cela il faudra être capable de transformer une chaine de caractère représentant un nombre décimal en entier. Le plus simple pour cela est d'utiliser sscanf, qui fonctionne comme scanf mais en prenant en premier argument une chaine de caractères qui va "remplacer" l'entrée standard. Par exemple :
    char * s = "123";
    int x;
    sscanf(s,"%d",&x);
    printf("%d\n",x); /* affiche 123 */
    
    Il existe d'autres fonctions pour faire cela qui permettent de gérer proprement les cas de dépassement de capacité, mais ce n'est pas l'objet du TP !

Gestion propre des différents cas possibles : getoptlong()

Ecrire une programme qui accepte plusieurs type d'option et dans n'importe quel ordre peut vite devenir un cauchemard si vous essayez de tout faire vous même. Heureusement il existe des bibliothèques permettant d'écrire du code plus propre.

Lisez cette page jusqu'au point 2.1.3 inclus.

Testez les exemples.

Retour au jeu du serpent

  • Si vous n'aviez pas terminé le tp2, c'est le moment ou jamais !
  • Modifiez le main de game.c pour qu'il traite les options avec getoptlong(). Pour l'instant il n'y aura qu'une option --help / -h qui permet d'afficher un petit texte d'aide. Au fur et a mesure du projet, nous rajouterons des options, il faudra modifier le texte d'aide. Pour rendre les choses plus lisible, vous ferez une fonction séparée print_help() pour réaliser l'affichage de l'aide.

Nous voulons maintenant ajouter à la grille notre serpent

  • ajouter à votre projet un fichier snake.c et un fichier snake.h
  • pour l'instant, un serpent sera modélisé en mémoire comme un tableau de coordonnées, chaque case correspondant aux coordonnées d'une partie du corps du serpents. Le tableaux est ordonné, c'est à dire que la première case correspond aux coordonnées de la tête, et la dernière aux coordonnées de la queue.
    • ajoutez un nouveau type Coord représentant une structure contenant deux entiers x et y
    • ajoutez une constante SNAKE_SIZE égale à 4
    • ajoutez une structure struct _snake contenant un tableau pos de SNAKE_SIZE Coord
    • à l'aide de typedef définissez un type Snake représentant un struct _snake
  • ajoutez dans la bibliothèque grid une fonction place_snake prenant en paramètre une grille et un Snake et qui met à la valeur SNAKE ('s' en principe) les cases de la grille correspondantes aux coordonnées stockées dans le tableau pos du serpent passé en paramètre.
  • modifiez votre game.c pour, avant de rentrer dans la boucle de jeu, créer un serpent dont le tableau pos contient les coordonnées : (1,3)(1,2)(1,1)(1,0) et appeler la fonction place_snake()

Bravo, il y a maintenant un serpent à l'écran. Mais il ne fait rien. Il va falloir qu'il puisse se déplacer.

  • ajoutez une enum direction dans snake.h avec les valeurs TOP, BOTTOM, LEFT et RIGHT qui représentent les 4 directions possibles
  • ajoutez un champs appelé dir de type enum direction (vous pouvez aussi si vous le souhaitez définir avec typedef un type Direction) dans la structure struct _snake
  • ajoutez une fonction crawl() aux fichiers snake.h et snake.c qui va modifier le tableau de positions du serpent :
    • la fonction prend en paramètre un serpent
    • calcule la nouvelle position de la tête, en fonction de la direction. Si la tête était à la position (x,y), la nouvelle position est :
      • ((x-1+NBC)%NBC,y) si la direction est LEFT. En effet on pourrait penser dans un premier temps à (x-1,y), mais il ne faut pas que x devienne négatif ! si le serpent essaye de sortir à gauche de l'écran, nous allons le faire réapparaitre à droite. Donc la nouvelle coordonnée en x est (x-1+NBC)%NBC.
      • ((x+1)%NBC,y) si la direction est RIGHT. Idem si il essaye de s'échapper par l droite, on le ramène à gauche
      • (x,(y-1+NBL)%NBL) si la direction est TOP
      • (x,(y+1)%NBL) si la direction est BOTTOM
    • déplace toutes les cases du tableau de position du serpent vers la droite :
      • la case SNAKE_SIZE-1 reçoit une copie de la case SNAKE_SIZE-2, (la queue a maintenant pour coordonnées les anciennes coordonnées de l'avant derniere partie du serpent)
      • la case SNAKE_SIZE-2 reçoit une copie de la case SNAKE_SIZE-3,
      • ...
      • la case 1 reçoit une copie de la case 0 (la partie du serpent qui suit immédiatement la tete a avancé jusqu'aux anciennes coordonnées de la tête)
    • enfin la case 0 reçoit comme nouvelle valeur la nouvelle position de la tête que nous avons calculé plus haut (la tête a avancé)
  • ajoutez une fonction move_snake() aux fichiers grid.c et grid.h
    • la fonction prend en paramètres un serpent et une grille
    • efface la queue du serpent de la grille (met à BLANK dans la grille la position stockée en fin du tableau représentant les positions du serpent)
    • appelle crawl();
    • dessine la nouvelle tête (met à la valeur SNAKE dans la grille la position stockée en tête du tableau représentant les positions du serpent)
  • modifiez la boucle du jeu pour qu'à la fin de chaque passage dans la boucle, la fonction move_snake() soit appelée
  • n'oubliez pas avant de tester d'initialiser le champs dir de votre serpent à la valeur de votre choix et observez le résultat avec les 4 valeurs possibles.
  • vous remarquez que les collisions ne sont pas détectées ! On garde ça pour la prochaine séance !

Rendu

N'oubliez pas de pousser votre travail sur gitlab !

Dans un répertoire nommé login_tp3 copiez vos .c, makefile et .h. Vous pouvez y ajouter un fichier texte README si vous avez des choses à expliquer à joindre à votre rendu. Depuis le répertoire parent, tapez la commande tar czf login_tp3.tgz login_tp3. Déposez sur blackboard le fichier obtenu.