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

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. 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.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. 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 ..j tab[i][j] Finpour Finpour Fonction une_étape Paramètres d’entrée : le tableau tab ...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. Définir deux indices intermédiaires ni. ..

} printf("\n"). j<n.h" /* Initialisation du tableau tab*/ void inittab(char tab[][DIM]. /* Conversion entier  chaine de caracteres */ sprintf(bidon.n) Pour etape variant de 1 à n faire Calculer les nouvelles coordonnées (une_etape(t.k). tab[i][j]). i<n. printf("\n"). int k) { char bidon[256].&j)) Mettre k dans le tableau (tab[i][j]=k) Afficher le tableau Finpour Programme C : Fichier entrées/sortie : f1. j=n/2. j<n. int yd. int n){ int i. int xd. i<n.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. Affichage du tableau(t. } /* Affichage en mode texte du tableau */ void afftab(char tab[][DIM]."%d".c #include "mesfonctions. } /* Affichage en mode graphique du tableau */ void affgraph(PFENETRE f1. j++) tab[i][j]=0.n.yd . Chaine ( f1. } Fichier calcul et verification : f2.c /* Calcul une etape du carre en calculant les nouvelles coordonnées */ 3/7 . puts("Taper CR pour continuer"). for (i=0. i++) { for (j=0. bidon).j.&i.n) i=n/2+1. for (i=0. tab[n/2+1][n/2]=1. xd. getchar(). int n){ int i.j. j++) printf ("%d ". i++) for (j=0.

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

/* pour l’affichage graphique uniquement */ int diml.c TAB gcc –c f1. Grille(f1. tab[i][j]=etape.&i.2)*(dimc/n-2).1. if ( !(n%2) || n<1 || n > DIM) { printf("Erreur : n impair compris entre 3 et %d\n".(j+. et non taper les caractères ’T’ ’A’ ’B’ 5/7 .etape ).2)*(dimc/n-2). else printf("Ce n'est pas un carre magique\n").o f1.o f2.n.(j+. inittab(tab. affgraph(f1.j le nombre etape et affiche a chaque iteration le tableau en mode texte ET en mode graphique */ for (etape=2.&j).c -I/users/prog1a/C/librairie p.diml/n-2.o L/usr/X11R6/lib -lX11 -lpthread f2.9)*(diml/n-2).1).n. } Fichier Makefile p : f1. /* Traitement principal : place en i.o : p. etape++) { une_etape(tab.c -I/users/prog1a/C/librairie Attention : TAB signifie qu’il faut utiliser la touche Tabulation (|).(i+.n. afftab(tab.n).1 ).n)) printf("C'est un carre magique\n"). etape<=n*n.dimc. i=n/2+1.c -I/users/prog1a/C/librairie f2.(i+. scanf("%d". PFENETRE f1.o : f1. j=n/2.c TAB gcc –c p.dimc/n-2.o TAB gcc –o p p. exit (1). &n).c TAB gcc –c f2. } /* Initialisation de l’affichage graphique */ f1 = newfenetregraphique(diml=25*n.o : f2. puts("Entrer la taille du carré").o p.dimc=25*n).DIM). affgraph(f1.o -L/users/prog1a/C/librairie -lERG - f1.n). } if (verification( tab.9)*(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. * * * ** * ** ******* ******* ***** Génération 3 Génération 4 ******* * * * Dans notre cas. Cette fonction aura besoin de la fonction suivante : 6/7 . Chaque cellule est donc entourée de huit cases susceptibles de contenir d'autres cellules.. Chaque cellule n'ayant qu'une ou aucune cellule adjacente meurt d'isolement. Ceci peut être facilement réalisé grâce à la fonction modulo (%). de disparaître ou de survivre lorsqu'elles obéissent à certaines règles. le nombre de configuration est limité. b) la mort : chaque cellule ayant quatre ou plus cellules adjacentes disparaît. Ces cellules sont représentées par des éléments sur un damier de taille infinie : l'univers. Une des questions posée est de savoir si à partir d'une configuration donnée. int nb) : initialise le tableau utilisant nb lignes x nb colonnes • void nouvellegeneration(char ancienne[][DIM]. Les règles sont les suivantes : a) la survie : chaque cellule ayant deux ou trois cellules adjacentes survit jusqu'à la génération suivante. Il est bien entendu que cette question ne se pose que pour un univers infini : dans un univers fini (un tableau informatique classique). ou meurt par surpopulation. Vous considérerez cependant que l'univers est rond. 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. que la ligne la plus haute (indice 0) a pour voisines la ligne 1 ET la ligne la plus basse (et réciproquement). qui met en œuvre des "cellules" susceptibles de se reproduire. Exemple: * * ** ** * * * ** ** * * ** ** ** * ** * * ** * ** ** ** Génération 1Génération 2 Travail à réaliser ***** ******* ** * ** * * ** ** . l'évolution de l'univers conduit plusieurs fois à la même configuration (boucle). dit "Jeu de la Vie". au minimum besoin des fonctions suivantes : • void initialisation(char tab[][DIM]. l'univers sera fini et limité à la taille de votre écran. char nouvelle[][DIM].. int nb) : calcul la nouvelle génération à partir de l’ancienne.Exercice : Jeu de la vie Présentation du jeu: Le mathématicien Conway a imaginé un jeu. c'est à dire que la colonne d'indice 0 a pour voisines la colonne 1 ET la colonne la plus à droite. Vous aurez. c) la naissance : chaque emplacement ayant exactement trois cellules adjacentes fait naître une nouvelle cellule pour la génération suivante.

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

Sign up to vote on this title
UsefulNot useful