Vous êtes sur la page 1sur 7

TD de programmation structure : 1re anne

2006-2007

TD n 7
Buts Dure : 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 utiliss une seule fois. [2] Ces nombres sont disposs de telle sorte que les sommes de chaque ligne, de chaque colonne, de chaque diagonale soient toutes gales. Plusieurs algorithmes permettent de construire des carrs magiques pourvu que l'ordre donn n soit impair. Voici l'un d'entre eux : [1] On commence par crer un carr d'ordre n vide. [2] Le nombre 1 est plac ensuite dans la case juste au-dessous du centre. [3] Aussitt qu'un dplacement dcrit 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 dplacements dcrits. [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 prcdemment, 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 prsenter: - La case atteinte est vide, on y place alors le nombre suivant. - La case atteinte est dj remplie, on en choisit alors une autre en effectuant deux dplacements verticaux successifs vers le bas partir de la case prcdemment remplie (Tant que le carr n'est pas compltement rempli, cette case est ncessairement nulle).

Exemple
3 4 3 4 4 5 1 6 2 3

1 2

1 2 3

1 2

...... .

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 dinitialisation


Paramtres dentre la taille n Paramtres de sortie : aucun Paramtres dentre/sortie : : le tableau tab deux dimension Valeur de retour : aucune Rle : lecture des donnes

Dfinir 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 llment i,j tab[i][j] Finpour Finpour Mettre 1 dans llment en dessous de la case du centre tab[n/2+1][n/2] Fonction daffichage
Paramtres dentre : le tableau tab et la taille n Paramtres de sortie : aucun Paramtres dentre/sortie : aucun Valeur de retour : aucune Rle : affichage du contenu du tableau

Dfinir deux indices de boucle i et j Pour i variant de 0 n-1 faire Pour j variant de 0 n-1 faire Afficher lcran llment i,j tab[i][j] Finpour Finpour Fonction une_tape
Paramtres dentre : le tableau tab , la taille n Paramtres de sortie : Paramtres dentre/sortie : les coordonnes i et j de lancienne position et de la nouvelle position Valeur de retour : aucune Rle : trouver les nouvelles coordonnes du nombre placer.

Dfinir deux indices intermdiaires ni,nj Calculer la nouvelle position ni, 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

Sinon changer i en (i+2) modulo n Finsi Fonction principale


Dfinir la taille maximale du carr (constante DIM) Dfinir la taille du carr (variable n) Dfinir un tableau 2D t pouvant contenir le nombre maximal dlments (constante DIM) Dfinir une variable etape contenant le numero a positionner Dfinir deux variables i et j indiquant les coordonnes de la case remplir

Initialiser (t,n) i=n/2+1; j=n/2; Affichage du tableau(t,n) Pour etape variant de 1 n faire Calculer les nouvelles coordonnes (une_etape(t,n,&i,&j)) Mettre k dans le tableau (tab[i][j]=k) Afficher le tableau Finpour Programme C : Fichier entres/sortie : f1.c
#include "mesfonctions.h" /* Initialisation du tableau tab*/ void inittab(char tab[][DIM], int n){ int i,j; for (i=0; i<n; i++) for (j=0; j<n; j++) tab[i][j]=0; tab[n/2+1][n/2]=1; } /* Affichage en mode texte du tableau */ void afftab(char tab[][DIM], int n){ int i,j; for (i=0; i<n; i++) { for (j=0; j<n; j++) printf ("%d ", tab[i][j]); printf("\n"); } printf("\n"); } /* Affichage en mode graphique du tableau */ void affgraph(PFENETRE f1, int xd, int yd, int k) { char bidon[256]; /* Conversion entier chaine de caracteres */ sprintf(bidon,"%d",k); Chaine ( f1, xd,yd , bidon); puts("Taper CR pour continuer"); getchar(); }

Fichier calcul et verification : f2.c


/* Calcul une etape du carre en calculant les nouvelles coordonnes */ 3/7

void une_etape(char tab[][DIM], int n, int *ai, int *aj){ ni=(*ai+1)%n; nj=(*aj+1)%n; if (tab[ni][nj]) /* Case deja occupee */ *ai = (*ai +2)%n; else { *ai= ni; *aj=nj; } }

int ni,nj;

/* Verifie si le tableau est bien un carre magique Calcul de la somme de la premiere ligne Verification des sommes des lignes et colonnes, de la diagonale Et de lantidiagonale */ int verification(char tab[][DIM], int n) { int i,j,s0,s; /* Somme de la premiere ligne */ for (s0=i=0; i<n; i++) s0+=tab[0][i]; /* Somme des lignes */ for (i=0; i<n; i++) { for (s=j=0; j<n; j++) s +=tab[i][j]; if (s !=s0) return(0); } /* Somme des colonnes */ for (i=0; i<n; i++) { for (s=j=0; j<n; j++) s +=tab[j][i]; if (s !=s0) return(0); } /* Somme de la diagonale */ for (s=i=0; i<n; i++) s +=tab[i][i]; if (s !=s0) return(0); /* Somme de l'antidiagonale */ for (s=i=0; i<n; i++) s +=tab[i][n-1-i]; if (s !=s0) return(0); /* Ici, c'est un carre magique */ return 1; }

Fichier dentete : mesfonctions.h


#include <stdio.h> #include "ERGgraphics.h" #define DIM 15 int verification(char tab[][DIM], int n) ; void une_etape(char tab[][DIM], int n, int *ai, int *aj) ; void inittab(char tab[][DIM], int n) ; void afftab(char tab[][DIM], int n) ; void affgraph(PFENETRE f1, int xd, int yd, int k) ;

Fichier principal : p.c


#include "mesfonctions.h" main (int ac, char **av) char tab[DIM][DIM]; { int etape, n, i,j;

4/7

/* pour laffichage graphique uniquement */ int diml,dimc; PFENETRE f1; puts("Entrer la taille du carr"); scanf("%d", &n); if ( !(n%2) || n<1 || n > DIM) { printf("Erreur : n impair compris entre 3 et %d\n",DIM); exit (1); } /* Initialisation de laffichage graphique */ f1 = newfenetregraphique(diml=25*n,dimc=25*n); Grille(f1,n,n,diml/n-2,dimc/n-2,1,1); inittab(tab,n); i=n/2+1; j=n/2; affgraph(f1,(j+.2)*(dimc/n-2),(i+.9)*(diml/n-2),1 ); /* Traitement principal : place en i,j le nombre etape et affiche a chaque iteration le tableau en mode texte ET en mode graphique */ for (etape=2; etape<=n*n; etape++) { une_etape(tab,n,&i,&j); tab[i][j]=etape; affgraph(f1,(j+.2)*(dimc/n-2),(i+.9)*(diml/n-2),etape ); afftab(tab,n); } if (verification( tab,n)) printf("C'est un carre magique\n"); else printf("Ce n'est pas un carre magique\n"); }

Fichier Makefile
p : f1.o f2.o p.o TAB gcc o p p.o f1.o L/usr/X11R6/lib -lX11 -lpthread f2.o -L/users/prog1a/C/librairie -lERG -

f1.o : f1.c TAB gcc c f1.c -I/users/prog1a/C/librairie f2.o : f2.c TAB gcc c f2.c -I/users/prog1a/C/librairie p.o : p.c TAB gcc c p.c -I/users/prog1a/C/librairie Attention : TAB signifie quil faut utiliser la touche Tabulation (|), et non taper les caractres T A B

5/7

Exercice : Jeu de la vie


Prsentation du jeu: Le mathmaticien Conway a imagin un jeu, dit "Jeu de la Vie", qui met en uvre des "cellules" susceptibles de se reproduire, de disparatre ou de survivre lorsqu'elles obissent certaines rgles, appeles "gntiques". Ces cellules sont reprsentes par des lments sur un damier de taille infinie : l'univers. Ce jeu s'insre dans la thorie des automates cellulaires et de nombreux travaux de recherche existent dans ce domaine. Une des questions pose est de savoir si partir d'une configuration donne, l'volution de l'univers conduit plusieurs fois la mme configuration (boucle). Il est bien entendu que cette question ne se pose que pour un univers infini : dans un univers fini (un tableau informatique classique), le nombre de configuration est limit. Chaque cellule est donc entoure de huit cases susceptibles de contenir d'autres cellules. Les rgles sont les suivantes : a) la survie : chaque cellule ayant deux ou trois cellules adjacentes survit jusqu' la gnration suivante. b) la mort : chaque cellule ayant quatre ou plus cellules adjacentes disparat, ou meurt par surpopulation. Chaque cellule n'ayant qu'une ou aucune cellule adjacente meurt d'isolement. c) la naissance : chaque emplacement ayant exactement trois cellules adjacentes fait natre une nouvelle cellule pour la gnration suivante. Il est important de remarquer que toutes les naissances et toutes les morts ont lieu en mme temps au cours d'une gnration. Exemple: * * ** ** * * * ** ** * * ** ** ** * ** * * ** * ** ** **

Gnration 1Gnration 2 Travail raliser

***** ******* ** * ** * * ** ** ... * * * ** * ** ******* ******* ***** Gnration 3 Gnration 4 ******* * * *

Dans notre cas, l'univers sera fini et limit la taille de votre cran. Vous considrerez cependant que l'univers est rond, c'est dire que la colonne d'indice 0 a pour voisines la colonne 1 ET la colonne la plus droite, que la ligne la plus haute (indice 0) a pour voisines la ligne 1 ET la ligne la plus basse (et rciproquement). Ceci peut tre facilement ralis grce la fonction modulo (%). Vous aurez, au minimum besoin des fonctions suivantes : void initialisation(char tab[][DIM], int nb) : initialise le tableau utilisant nb lignes x nb colonnes void nouvellegeneration(char ancienne[][DIM], char nouvelle[][DIM], int nb) : calcul la nouvelle gnration partir de lancienne. Cette fonction aura besoin de la fonction suivante :
6/7

int nbvoisin(char tab[][DIM], int i, int j, int nb) : calcule le nombre de voisins vivants du point de coordonnes i et j dans le tableau tab. Noubliez pas de calculer les coordonnes des voisins laide du modulo. Attention, loperateur modulo du C peut retourner une valeur ngative si le dividende est ngatif. Utilisez donc (i-1+nb)%nb par exemple pour trouver la ligne prcdente, expression qui sera toujours comprise en 0 et nb-1. void affichegeneration(char tab[][DIM], int nb) : affiche dune gnration

Travail facultatif Vous pourrez aussi tester si votre configuration initiale conduit une population stable (i.e. une population qui n'volue plus) ou plus difficile, une population qui boucle sur elle-mme au bout de X gnrations. Raliser la lecture de la gnration initiale dans un fichier sur disque. Le nom du fichier contenant la gnration initiale sera pass en argument de la ligne de commande. Voici le code permettant un affichage graphique dune gnration
#include "ERGgraphics.h" #define DIM 100 #define TAILLE 5 /* Taille d'une case du tableau */

void Affichejeudevie(PFENETRE f1,char tab[][DIM], int nb, int taille) { int i,j; Grille(f1,nb,nb,3*taille+1,3*taille+1,0,0); for (i=0; i<nb; i++) for (j=0; j<nb; j++) CerclePleinCouleur(f1,j*(3*taille+1)+3*taille/2, i*(3*taille+1)+3*taille/2,taille, tab[i][j]?WHITE(f1):BLUE(f1)); } /* Generation aleatoire des cellules */ void inittab(char tab[][DIM], int nb) { int i,j; for (i=0; i<nb; i++) for (j=0; j<nb; j++) tab[i][j] = rand() % 2; } main (int ac, char **av) { int n; char tab1[DIM][DIM]; char tab2[DIM][DIM]; PFENETRE f1; /* On utilise que les n x n elements du tableau tab */ puts("Entrer la taille du carr"); scanf("%d", &n); inittab(tab1,n); /* Initialisation de l'affichage graphique */ f1 = newfenetregraphique(n*(3*TAILLE+1), n*(3*TAILLE+1)); do { Affichejeudevie(f1,tab1,n,TAILLE); /* Affichage dune gnration */ nouvellegeneration(tab1,tab2,n) ; /* A FAIRE */ memcpy(tab1,tab2,DIM*DIM*sizeof(char)) ; /* recopie la nouvelle gnration dans lancienne pour recommencer */ } while (getchar() !=q); /* Attente */ }

7/7