TD de programmation structurée : 1ère année

2006-2007

TD n° 7
Buts Durée : Tableau 2D, lecture dans un fichier : 1 semaine

Exemple : Carré magique
Enoncé :
4 3 8 9 5 1 2 7 6

Exemple d'un carré magique d'ordre 3

Dans leur explication du monde, les chinois racontent qu'une tortue marine aborda un jour la terre avec un carré magique d'ordre 3 sur la carapace. Un carré d'ordre n est un tableau d'entiers dont les 2 dimensions sont égales à n. Un carré (d'ordre n) est magique quand : [1] Tous les nombres entiers de 1 à n*n sont utilisés une seule fois. [2] Ces nombres sont disposés de telle sorte que les sommes de chaque ligne, de chaque colonne, de chaque diagonale soient toutes égales. Plusieurs algorithmes permettent de construire des carrés magiques pourvu que l'ordre donné n soit impair. Voici l'un d'entre eux : [1] On commence par créer un carré d'ordre n vide. [2] Le nombre 1 est placé ensuite dans la case juste au-dessous du centre. [3] Aussitôt qu'un déplacement décrit par la suite oblige à sortir du carré en traversant l'un des bords, on se place alors dans la case correspondante du bord opposé et on continue à effectuer la suite des déplacements décrits. [4] Une fois qu'une case a été remplie, on en choisit une autre en effectuant, par rapport à la case qui vient d'être remplie précédemment, deux mouvements successifs: - l'un horizontal, d'une case vers la droite. - l'autre vertical d'une case vers le bas. Deux cas peuvent se présenter: - La case atteinte est vide, on y place alors le nombre suivant. - La case atteinte est déjà remplie, on en choisit alors une autre en effectuant deux déplacements verticaux successifs vers le bas à partir de la case précédemment remplie (Tant que le carré n'est pas complètement rempli, cette case est nécessairement nulle).

Exemple 3 4 3 4 4 5 1 6 2 3 1 1 2 1 2 3 1 2 .nj Calculer la nouvelle position ni.j tab[i][j] Finpour Finpour Mettre 1 dans l’élément en dessous de la case du centre tab[n/2+1][n/2] Fonction d’affichage Paramètres d’entrée : le tableau tab et la taille n Paramètres de sortie : aucun Paramètres d’entrée/sortie : aucun Valeur de retour : aucune Rôle : affichage du contenu du tableau Définir deux indices de boucle i et j Pour i variant de 0 à n-1 faire Pour j variant de 0 à n-1 faire Afficher à l’écran l’élément i.j tab[i][j] Finpour Finpour Fonction une_étape Paramètres d’entrée : le tableau tab . Définir deux indices intermédiaires ni. .... Positionnement du 1 Exemple d'un carré magique d'ordre 5 Positionnement du 2 Positionnement du 3 Positionnement du 4 Positionnement du 6 Algorithme Fonction d’initialisation Paramètres d’entrée la taille n Paramètres de sortie : aucun Paramètres d’entrée/sortie : : le tableau tab à deux dimension Valeur de retour : aucune Rôle : lecture des données Définir deux indices de boucle i et j Pour i variant de 0 à n-1 faire Pour j variant de 0 à n-1 faire Mettre 0 dans l’élément i... nj à partir de i et j (ni=(i+1) modulo n et nj=(j+1)modulo n) Si cette case (tab[ni][nj] est vide) Alors changer les valeurs de i et j en ni et nj 2/7 . la taille n Paramètres de sortie : Paramètres d’entrée/sortie : les coordonnées i et j de l’ancienne position et de la nouvelle position Valeur de retour : aucune Rôle : trouver les nouvelles coordonnées du nombre à placer.

} /* Affichage en mode graphique du tableau */ void affgraph(PFENETRE f1. int k) { char bidon[256]. i++) { for (j=0. int yd. j++) tab[i][j]=0. } /* Affichage en mode texte du tableau */ void afftab(char tab[][DIM]. j++) printf ("%d ".c /* Calcul une etape du carre en calculant les nouvelles coordonnées */ 3/7 . Affichage du tableau(t. int n){ int i. int xd. i<n. } Fichier calcul et verification : f2. } printf("\n").&j)) Mettre k dans le tableau (tab[i][j]=k) Afficher le tableau Finpour Programme C : Fichier entrées/sortie : f1. Chaine ( f1.n."%d".&i. j<n. int n){ int i.Sinon changer i en (i+2) modulo n Finsi Fonction principale Définir la taille maximale du carré (constante DIM) Définir la taille du carré (variable n) Définir un tableau 2D t pouvant contenir le nombre maximal d’éléments (constante DIM) Définir une variable etape contenant le numero a positionner Définir deux variables i et j indiquant les coordonnées de la case à remplir Initialiser (t. getchar(). j<n. tab[i][j]). xd. j=n/2.c #include "mesfonctions. printf("\n"). for (i=0. /* Conversion entier  chaine de caracteres */ sprintf(bidon.yd . for (i=0. bidon). puts("Taper CR pour continuer"). i++) for (j=0.k). i<n. tab[n/2+1][n/2]=1.n) Pour etape variant de 1 à n faire Calculer les nouvelles coordonnées (une_etape(t.j.h" /* Initialisation du tableau tab*/ void inittab(char tab[][DIM].n) i=n/2+1.j.

if (s !=s0) return(0). int n) . if (s !=s0) return(0).nj. int *aj){ ni=(*ai+1)%n. i<n. void une_etape(char tab[][DIM].j. int *ai. if (s !=s0) return(0).h" main (int ac. int k) .h" #define DIM 15 int verification(char tab[][DIM].s. /* Somme des lignes */ for (i=0. int n) { int i. i++) s0+=tab[0][i]. void afftab(char tab[][DIM]. i++) { for (s=j=0.void une_etape(char tab[][DIM]. i++) { for (s=j=0. i<n.h #include <stdio. { int etape. j<n.s0. i<n.c #include "mesfonctions. int yd. Fichier principal : p. i. int n) . int xd. int n. char **av) char tab[DIM][DIM]. /* Ici. 4/7 . int n) . } Fichier d’entete : mesfonctions. else { *ai= ni.h> #include "ERGgraphics. if (s !=s0) return(0). int n. void inittab(char tab[][DIM]. int *aj) . *aj=nj. } /* Somme des colonnes */ for (i=0. j++) s +=tab[j][i]. de la diagonale Et de l’antidiagonale */ int verification(char tab[][DIM]. i++) s +=tab[i][n-1-i]. if (tab[ni][nj]) /* Case deja occupee */ *ai = (*ai +2)%n. i<n. c'est un carre magique */ return 1. /* Verifie si le tableau est bien un carre magique Calcul de la somme de la premiere ligne Verification des sommes des lignes et colonnes. int *ai. /* Somme de la premiere ligne */ for (s0=i=0. } } int ni. n. void affgraph(PFENETRE f1. j++) s +=tab[i][j]. nj=(*aj+1)%n. i++) s +=tab[i][i]. /* Somme de l'antidiagonale */ for (s=i=0.j. i<n. } /* Somme de la diagonale */ for (s=i=0. j<n.

2)*(dimc/n-2).c TAB gcc –c f2.c TAB gcc –c p. scanf("%d".etape ). /* Traitement principal : place en i.2)*(dimc/n-2).c -I/users/prog1a/C/librairie f2.o : f2.(i+.dimc. affgraph(f1.n).o L/usr/X11R6/lib -lX11 -lpthread f2.o p. else printf("Ce n'est pas un carre magique\n").DIM).n).c -I/users/prog1a/C/librairie Attention : TAB signifie qu’il faut utiliser la touche Tabulation (|).1).o f1.o : f1.(j+.9)*(diml/n-2). j=n/2. etape<=n*n. inittab(tab.(i+. Grille(f1.9)*(diml/n-2). exit (1). i=n/2+1. } if (verification( tab. } Fichier Makefile p : f1.j le nombre etape et affiche a chaque iteration le tableau en mode texte ET en mode graphique */ for (etape=2. afftab(tab. etape++) { une_etape(tab. } /* Initialisation de l’affichage graphique */ f1 = newfenetregraphique(diml=25*n.&i.dimc/n-2. if ( !(n%2) || n<1 || n > DIM) { printf("Erreur : n impair compris entre 3 et %d\n".1./* pour l’affichage graphique uniquement */ int diml.(j+.o TAB gcc –o p p.dimc=25*n). &n).n)) printf("C'est un carre magique\n"). PFENETRE f1. tab[i][j]=etape.1 ).n.n.c -I/users/prog1a/C/librairie p.o : p. affgraph(f1.n.o f2.o -L/users/prog1a/C/librairie -lERG - f1.c TAB gcc –c f1. et non taper les caractères ’T’ ’A’ ’B’ 5/7 .&j). puts("Entrer la taille du carré").diml/n-2.

Ce jeu s'insère dans la théorie des automates cellulaires et de nombreux travaux de recherche existent dans ce domaine. c) la naissance : chaque emplacement ayant exactement trois cellules adjacentes fait naître une nouvelle cellule pour la génération suivante. c'est à dire que la colonne d'indice 0 a pour voisines la colonne 1 ET la colonne la plus à droite. qui met en œuvre des "cellules" susceptibles de se reproduire. ou meurt par surpopulation. int nb) : initialise le tableau utilisant nb lignes x nb colonnes • void nouvellegeneration(char ancienne[][DIM]. Une des questions posée est de savoir si à partir d'une configuration donnée. l'évolution de l'univers conduit plusieurs fois à la même configuration (boucle). * * * ** * ** ******* ******* ***** Génération 3 Génération 4 ******* * * * Dans notre cas. b) la mort : chaque cellule ayant quatre ou plus cellules adjacentes disparaît. Vous considérerez cependant que l'univers est rond. le nombre de configuration est limité. Les règles sont les suivantes : a) la survie : chaque cellule ayant deux ou trois cellules adjacentes survit jusqu'à la génération suivante. Exemple: * * ** ** * * * ** ** * * ** ** ** * ** * * ** * ** ** ** Génération 1Génération 2 Travail à réaliser ***** ******* ** * ** * * ** ** . appelées "génétiques". Il est important de remarquer que toutes les naissances et toutes les morts ont lieu en même temps au cours d'une génération. Ceci peut être facilement réalisé grâce à la fonction modulo (%). Cette fonction aura besoin de la fonction suivante : 6/7 .Exercice : Jeu de la vie Présentation du jeu: Le mathématicien Conway a imaginé un jeu. l'univers sera fini et limité à la taille de votre écran. Vous aurez. au minimum besoin des fonctions suivantes : • void initialisation(char tab[][DIM]. Chaque cellule est donc entourée de huit cases susceptibles de contenir d'autres cellules. Ces cellules sont représentées par des éléments sur un damier de taille infinie : l'univers. Il est bien entendu que cette question ne se pose que pour un univers infini : dans un univers fini (un tableau informatique classique).. int nb) : calcul la nouvelle génération à partir de l’ancienne. Chaque cellule n'ayant qu'une ou aucune cellule adjacente meurt d'isolement. de disparaître ou de survivre lorsqu'elles obéissent à certaines règles. que la ligne la plus haute (indice 0) a pour voisines la ligne 1 ET la ligne la plus basse (et réciproquement). char nouvelle[][DIM].. dit "Jeu de la Vie".

tab2. i++) for (j=0. n*(3*TAILLE+1)). expression qui sera toujours comprise en 0 et nb-1.char tab[][DIM]. int nb. do { Affichejeudevie(f1.n) . à une population qui boucle sur elle-même au bout de X générations.e. } main (int ac.nb. /* recopie la nouvelle génération dans l’ancienne pour recommencer */ } while (getchar() !=’q’).taille.3*taille+1.tab2. j<nb. char tab2[DIM][DIM]. • Réaliser la lecture de la génération initiale dans un fichier sur disque. for (i=0.h" #define DIM 100 #define TAILLE 5 /* Taille d'une case du tableau */ void Affichejeudevie(PFENETRE f1.0. Utilisez donc (i-1+nb)%nb par exemple pour trouver la ligne précédente. int taille) { int i. l’operateur modulo du C peut retourner une valeur négative si le dividende est négatif.n). /* Initialisation de l'affichage graphique */ f1 = newfenetregraphique(n*(3*TAILLE+1). Grille(f1. int nb) { int i.n. j<nb. void affichegeneration(char tab[][DIM]. i*(3*taille+1)+3*taille/2. j++) CerclePleinCouleur(f1. char **av) { int n.DIM*DIM*sizeof(char)) . int nb) : calcule le nombre de voisins vivants du point de coordonnées i et j dans le tableau tab. Voici le code permettant un affichage graphique d’une génération #include "ERGgraphics.j*(3*taille+1)+3*taille/2.• • int nbvoisin(char tab[][DIM]. i++) for (j=0. i<nb. inittab(tab1. /* On utilise que les n x n elements du tableau tab */ puts("Entrer la taille du carré").0). scanf("%d". PFENETRE f1. &n). Le nom du fichier contenant la génération initiale sera passé en argument de la ligne de commande.tab1.TAILLE). j++) tab[i][j] = rand() % 2. i<nb.j.3*taille+1. } /* Generation aleatoire des cellules */ void inittab(char tab[][DIM]. int nb) : affiche d’une génération Travail facultatif • Vous pourrez aussi tester si votre configuration initiale conduit à une population stable (i. /* A FAIRE */ memcpy(tab1. int j. une population qui n'évolue plus) ou plus difficile. /* Affichage d’une génération */ nouvellegeneration(tab1. char tab1[DIM][DIM]. tab[i][j]?WHITE(f1):BLUE(f1)). N’oubliez pas de calculer les coordonnées des voisins à l’aide du modulo. Attention. int i. /* Attente */ } 7/7 .j. for (i=0.nb.