Skip to content

Tableaux, Tableaux à deux dimensions, et affichage d'une grille

Rappels tableaux (Exercice 3, TP3 de 3R-IN1A)

Écrivez les fonctions suivantes, n'oubliez pas d'écrire une fonction main() permettant de les tester. Les arguments devant être fournis aux fonctions par le main() seront soit demandé à l'utilisateur avec un scanf, soit rentré sur la ligne de commande.

  • Écrire une fonction qui prend un tableau d'entiers et renvoie le plus grand élément.
  • Écrire une fonction qui prend deux tableaux d'entiers et recopie le premier dans le second. Réfléchissez aux paramètres pour gérer correctement les cas où les deux tableaux ne font pas la même taille.
  • Écrire une fonction qui prend trois tableaux d'entiers et recopie le 2e puis le 3e dans le 1er. Vous pouvez (devez ?) vous servir de la fonction précédente.
  • Écrire une fonction qui renvoie le nombre de caractères d'une chaine de caractères (sans utiliser la fonction strlen de string.h)
  • Écrire une fonction qui prend deux chaines de caractères et renvoie la taille du plus long préfixe commun (exemple pour "avion" et "aviation", la fonction revoie 3 car le plus long préfixe commun est "avi").

Découverte d'un nouvelle dimension

Vous pouvez utilement (re)lire ce document jusqu'au slide 9.

Il est donc possible en C de déclarer facilement des matrices à deux dimensions, allouées sur la pile.

Par exemple :

int matrice[5][10];
Alloue une matrice de 5 lignes et de 10 colonnes, chaque élément étant un entier. Pour utiliser la case 3 de la ligne 4 (les cases et les lignes sont numérotées à partir de 0), il faudra alors écrire :
matrice[4][3] = ...;

Attention, en mémoire, il n'y a aucune différence entre

int matrice[5][10];
et
int matrice[50];
Aussi, si vous voulez passer le tableau à une fonction, il faudra préciser au moins la dernière dimension (le nombre de colonne) pour que le compilateur puisse comprendre que matrice[i][j] équivaut à matrice[i*NB_COLONNE+j].

Pour notre exemple de matrice à 5 lignes, 10 colonnes, cela donne :

void fonction(int matrice[][10], int nb_ligne, int nb_col){...};
ou
void fonction(int matrice[5][10], int nb_ligne, int nb_col){...};
mais pas
void fonction(int matrice[][], int nb_ligne, int nb_col){...};
L'appel de fonction se fait comme pour un tableau normal :
fonction(matrice,5,10);

Pour chacune des fonctions suivantes, vous devez aussi faire le main() qui va avec !

  • Testez ces deux programmes avec la commande time qui permet de mesurer le temps d'exécution d'un programme, et expliquez le résultat.

Programme 1

#include <stdio.h>

#define NBL 1000
#define NBC 2000

int main()
{
    int matrice[NBL][NBC];
    int i,j,sum;
    for(i=0;i<NBL;i++)
        for(j=0;j<NBC;j++)
            sum+=matrice[i][j];
    return sum;     
}

Programme 2

#include <stdio.h>

#define NBL 1000
#define NBC 2000

int main()
{
    int matrice[NBL][NBC];
    int i,j,sum;
    for(j=0;j<NBC;j++)
        for(i=0;i<NBL;i++)
            sum+=matrice[i][j];
    return sum;     
}
  • Écrivez une fonction qui prend un tableau d'entiers à deux dimensions et demande à l'utilisateur de saisir les valeurs avec scanf
  • Écrivez une fonction qui affiche un tableau d'entiers à deux dimensions en affichant un retour à la ligne a la fin de chaque ligne.
  • Écrivez une fonction qui renvoie le produit de la somme de chaque ligne d'un tableau d'entier à deux dimensions

Application au mini projet

On veut représenter en mémoire une grille constituée de cases soit blanches (vide), soit noires (mur), soit rouge (fruit). Cette grille sera représentée par une matrice de char, la valeur 'w' sera utilisée pour les cases blanches, et la valeur 'b' pour les cases noires et la valeur 'r' pour les cases rouges.

  • On sait que les lignes seront des chaines de caractères, si on veut 5 char par lignes, combien vaut alors la deuxième dimension ?
  • Écrivez la fonction debug() qui prend en paramètres une matrice de char et l'affiche sur la sortie standard
  • L'exécution du main() suivant devra fonctionner :
int main()
{
    char grid[8][7] = {
        "bwrwbw",
        "wrwbwr",
        "bwrwbw",
        "wrwbwr",
        "bwrwbw",
        "wrwbwr",
        "bwrwbw",
        "wrwbwr"
    };
    debug(grid,8,7);
    return 0;
}
  • Dans un nouveau dossier snake2020 créer trois fichiers grid.h, grid.c et game.c : grid. h et grid.c contiendront les fonctions et constantes permettant de représenter une grille de jeu et game.c contiendra une fonction main() qui sert à démarrer le programme.
  • Faites un makefile adapté à ce nouveau projet
  • Dans grid.h, définissez deux constantes NBL et NBC qui représentent le nombre de lignes et de colonnes de la grille
  • ajoutez la fonction debug() à grid.h et grid.c (adaptez le prototype : nous n'avons plus besoin des paramètres entiers puisqu'ils sont donnés par les constantes NBL et NBC+1)
  • Écrivez une fonction compute_size() qui prend en paramètre deux entiers w, h qui représentent respectivement la largeur et la hauteur d'une fenêtre graphique. La fonction calcule alors la plus grande valeur a possible pour représenter toute la grille avec des carrés de coté a de sorte qu'elle tienne entièrement dans la fenêtre. Indice : les deux contraintes à respecter sont w >= nbc x a et h >= nbl x a. Le coté a est donc le minimum entre deux valeurs que vous pouvez calculer indépendamment.
  • À l'aide de la bibliothèque MLV, écrivez la fonction draw_grid() qui prend en paramètre la grille et qui l'affiche dans une fenêtre graphique. Les cases seront des carrés de coté calculé avec la fonction compute_size() (vous avec dans la bibliothèque à votre disposition les fonctions MLV_get_window_height() et MLV_get_window_width() pour récupérer les dimensions de la fenêtre). Attention, les coordonnées (0,0) sont en haut à gauche de la fenêtre (ça tombe bien, comme dans notre représentation textuelle)...
  • Dans le fichier game.c, adaptez le main précédant pour créé une fenêtre et qu'il appelle draw_grid() à la place de debug().
  • Faites en sorte que si l'utilisateur appuie sur ESC, le programme se termine, mais que sinon le programme attende.
  • lire les slides 17 à 20 de ce document. Ajoutez une enum Element dans grid.h avec les valeurs WALL, EMPTY et FRUIT pour décrire les valeurs possibles des cases. Remplacez dans draw_grid() vos tests par les noms donnés aux caractères dans l'enum.
  • Note: nous pourrions modifier la déclaration de la grille pour qu'elle soit non plus un tableau de char mais un tableau d'Element, mais ce n'est pas une bonne idée. En effet les enum en C sont des valeurs de type int, donc prennent plus de place que les char en mémoire.
  • Remplacez dans l'enum le caractère 'w' par le caractère ' ' (espace), 'b' par 'w', et 'r' par 'f'. Testez le main suivant, qui devrait fonctionner avec dans le fichier grid.h les constantes NBL et NBC valant respectivement 22 et 36.
int main()
{

    char grid[NBL][NBC+1] = {
        "w                                  w",
        "                                    ",
        "               f                    ",
        "                                    ",
        "     f               f              ",
        "                                    ",
        "                                    ",
        "               f                    ",
        "                                    ",
        "                                    ",
        "         wwwwwwwwww                 ",
        "                                    ",
        "                                    ",
        "                                    ",
        "                                    ",
        "                                    ",
        "                  f                 ",
        "                                    ",
        "         f                f         ",
        "                                    ",
        "                 f                  ",
        "w                                  w"
    };

    MLV_Keyboard_button touche = MLV_KEYBOARD_NONE;
    int width = 640, height = 480;


    /* Ouverture de la fenêtre graphique */
    MLV_create_window( "SNAKE", "3R-IN1B", width, height );
    MLV_change_frame_rate( 24 );

    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 );



        draw_grid(grid);


        MLV_actualise_window();


        touche = MLV_KEYBOARD_NONE;
        MLV_delay_according_to_frame_rate();
    }


    MLV_free_window();
    return 0;


}

Rendu

N'oubliez pas de pousser votre travail sur gitlab !

Dans un répertoire nommé login_tp2 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_tp2.tgz login_tp2. Déposez sur blackboard le fichier obtenu.