Generateur de discours creux en langue coton


INTRODUCTION

Les traitements en relation avec textes et langues ont fait l’objet de recherches importantes qui ont marqué les débuts de l’intelligence artificielle et probablement le mouvement de la linguistique structurale avec des personnalités comme Bar-Hillel, Bloomfield, Harris et Chomsky [GANASCIA, 1992, p. 71 à 79]. Plus récemment le thème de la génération automatique de texte a été approfondi avec les travaux de Jean-Pierre Balpe [BALPE, 2000].

L’objectif de l’expérimentation proposée ici est la génération de texte à partir d’une librairie de mots. Le socle des librairies de mots est donné par le livre de François-Bernard Huyghe, La langue de coton. C’est la langue du discours médiatique par excellence éventuellement pratiquée en politique. François-Bernard Huyghe se livre en fait à une critique féroce de ce qu’il considère, dans les médias en général, comme une technique grinçante de l’art de la séduction à des fins de manipulation, de dissimulation ou de pouvoir [HUYGHE, 1991].

Lors du travail préparatoire deux grands aspects ont pu être observés : la génération automatique de type paradigmatique, par catégories de mots et d’expressions, avec des résultats intéressants, et la génération automatique de syntagmes éventuellement étendus à la production de paragraphes entiers, beaucoup plus difficile. Vous trouverez plus de détails dans ma thèse [DROUILLON, 2003, p.114, 127]

TELECHARGER

aaaTexte des explications, aaaExecutable (Windows), aaaSource (utilise la librairie Allegro)

 

EXPLICATIONS

1. Principe

S’appuyer sur des protophrases. Ce sont des modèles de phrases littéralement utilisés comme des moules à phrases. Chaque protophrase est un syntagme constitué de primitives qui sont des catégories de mots. La génération automatique consiste à prendre pour chaque catégorie un mot au hasard. Voici un exemple de protophrase :

aaaaa Sélectionner dans une listeaaaaa : verbe à l’infinitif
aaaaa
Ponctuationaaaaaaaaaaaaaaaaa: « , » (une virgule)
aaaaa
Sélectionner dans une listeaaaaa : verbe à l’infinitif
aaaaa
Ponctuationaaaaaaaaaaaaaaaaa: « , » (une virgule)
aaaaa
Sélectionner dans une listeaaaaa : verbe à l’infinitif
aaaaa
Expression fixeaaaaaaaaaaaaaa : « et »
aaaaa
Sélectionner dans une listeaaaaa : verbe à l’infinitif
aaaaa
Expression fixeaaaaaaaaaaaaaa : « tous les »
aaaaa
Sélectionner dans une listeaaaaa : nom masculin pluriel
aaaaa
Expression fixeaaaaaaaaaaaaaa : « de la »
aaaaa
Sélectionner dans une listeaaaaa : nom féminin commençant avec une consonne
aaaaa
Sélectionner dans une listeaaaaa : adjectif féminin singulier
aaaaa
Ponctuationaaaaaaaaaaaaaaaaa: « . » (un point)

2. Mise en forme et initialisation

Le premier point est la construction d’une librairie. Pour chaque catégorie un tableau regroupe les mots disponibles et chaque mot est implémenté par une chaîne de caractères, à savoir un « char* ». Ainsi, la structure de données pour la librairie est un ensemble de tableaux de chaînes de caractères. Nous avons déjà rencontrés ce type sous la forme « char* line[ ] » au chapitre Onze à propos de l’implémentation d’une image bitmap. Il a servi pour définir l’espace mémoire de l’image. Nous allons l’utiliser maintenant du point de vue de l’écriture des mots et de leur stockage.

Rappels sur les caractères et chaînes de caractères (au cas où...)

Dans le langage C une lettre, un caractère, sont codés sur un octet et correspondent au type « char ». Une constante de type caractère apparaît entourée par deux apostrophes. ' a ' est la constante caractère a, c’est la lettre a dans un programme. Pour obtenir des mots il faut assembler des caractères, d’où la construction d’une « chaîne de caractères ».

La chaîne de caractères est une suite de caractères terminées par le caractère nul : ' \0 '. Ce caractère vaut zéro. La suite est délimitée par des guillemets, par exemple "bonjour" est une chaîne de caractère. En fait cette suite de caractères correspond à un tableau de huit caractères, sept lettres plus le caractère nul « \0 » qui signale la fin de la chaîne. C’est un tableau de char. L’espace, qui est une ponctuation, est aussi un caractère. De ce fait une chaîne de caractère peut contenir une phrase entière ou plusieurs. "Il fait beau. Je prends ma canne et mon chapeau." est une chaîne de caractères.

Il y a trois formes sous lesquelles se présentent les chaînes de caractères dans un programme :

aaaaa 1. Une constante chaîne de caractère comme : "bonjour". En fait elle est mémorisée comme un tableau de caractère. Il contient aaaaaaa les caractères de la chaîne et se termine par un ' \0 ' qui en marque la fin [KERNIGHAN, 1995, p.29].

aaaaa 2. Un tableau de caractère du genre :
aaaaaaa
char chaine1[10] = {' b ' , ' o ' , ' n ' , ' j ' , ' o ' , ' u ' , ' r ' , ' \0 ' } ;
aaaaaaa
ou encore
aaaaaaa
char chaine1[ ] = "bonjour" ;

aaaaaaa Remarquons que dans le premier cas « chaine1[10] » il y a deux cases disponibles en plus (initialisées à zéro). Dans le aaaaa aaaaaaa second, la taille du tableau « chaine1 » est automatiquement ajustée sur le nombre de caractères de la chaîne qui lui est aaaaa aaaaaaa affectée à l’initialisation. Le ' \0 ' final est implicite. La taille du tableau « chaine1 » est alors de huit caractères.

aaaaa 3. Le pointeur de caractères : char* chaine2 = "bonjour" ;
aaaaaaa
Cette dernière formulation de la chaîne de caractère est à rapprocher de l’équivalence établie entre tableaux et pointeurs. Dans aaaaaaa ce cas toujours, la chaîne est implicitement terminée par le caractère nul ' \0 '.

Le tableau de pointeurs de caractères va permettre de stocker les mots par catégorie. Chaque catégorie a son tableau de char* initialisé directement dans le programme. Une sentinelle est placée à la fin du tableau. C’est une astuce pour fournir un test d’arrêt à la fin du tableau alors que le nombre de ses éléments n’est pas connu. La sentinelle est simplement la valeur 0 castée en char*, c’est-à-dire la valeur NULL mais dans le type char*.

aaaaa
char*adj_f_p[ ]={
aaaaa
"transcendantales",
aaaaa
"probables",
aaaaa
"banales",
aaaaa
"barbares",
aaaaa
"baroques",
aaaaa
"cognitives",
aaaaa
…………. ,
aaaaa ( ( char* ) 0 ) } ;

Compte-tenu du nombre important de catégories de mots ainsi parfois que du nombre éventuellement élevé de mots par catégorie, il est plus clair dans le programme d’utiliser un fichier par catégorie. De ce fait les données du programme se trouvent partagées sur plusieurs fichiers.

Rappel mot-clé « extern »

Pour qu’une donnée définie sur un fichier soit connue sur un autre fichier il faut indiquer à cet autre fichier que la donnée en question est externe, faute de quoi elle n’est pas reconnue au moment de la compilation. C’est le rôle du mot-clé « extern » qui permet à une fonction dans un fichier donné de se référer à des données externes, déclarées sur d’autres fichiers en dehors de toute fonction [BRAQUELAIRE 1998, p26, 71] [KERNIGAHN 1995, p31].

Voici l’ensemble des catégories de mots du programme. Chacune correspond à un tableau de char* qui est défini dans un fichier dédié :

extern char*adj_f_p[ ];aaaaaaaaa // adjectif féminin pluriel
extern char*adj_m_p[ ];aaaaaaaa// adjectif masculin pluriel
extern char*adj_f_s[ ];aaaaaaaaa// adjectif féminin singulier
extern char*adj_m_s[ ];aaaaaaaa// adjectif masculin singulier
extern char*passif_f_s[ ];aaaaaaa// passif féminin singulier
extern char*passif_m_s[ ];aaaaa // passif masculin singulier
extern char*passif_m_p[ ];aaaaa // passif masculin pluriel
extern char*nom_f_cons[ ];aaaa // nom féminin singulier qui commence par une consonne
extern char*nom_m_cons[ ];aaa // nom masculin singulier qui commence par une consonne
extern char*nom_f_voy[ ];aaaaaa// nom féminin singulier qui commence par une voyelle
extern char*nom_m_voy[ ];aaaaa// nom masculin singulier qui commence par une voyelle
extern char*nom_f_p[ ];aaaaaaaa// nom féminin pluriel
extern char*nom_m_p[ ];aaaaaaa// nom masculin pluriel
extern char*nom_prop[ ];aaaaaaa// nom propre
extern char*nombres[ ];aaaaaaaa// nombre
extern char*adverbe[ ];aaaaaaaaa// adverbe
extern char*amorce[ ];aaaaaaaaa// diverses formules
extern char*infinitif[ ];aaaaaaaaaa// verbe à l’infinitif
extern char*verb_pr_3s[ ];aaaaaa// verbe présent 3e pers. singulier
extern char*verb_pr_3p[ ];aaaaaa// verbe présent 3e pers. pluriel
extern char*se_verb_pr_3s[ ];aaa// verbe présent 3e pers. singulier commençant par « se »
extern char*se_verb_pr_3p[ ];aaa// verbe présent 3e pers. pluriel commençant par « se »
extern char*verb_cond_3s[ ];aaaa// verbe conditionnel, 3e pers. singulier
extern char*verb_pr_3s_obj[ ];aaa// verbe présent 3e pers. singulier suivi adverbe

Toujours au niveau des structures de données, il reste à stocker le résultat qui est une ou plusieurs phrases générées par le programme. Il s’agit d’un char* déclaré en global. Les fonctions qui l’utilisent sont regroupées dans un seul fichier. Le nombre total de protophrases disponibles dans le programme est accessible via une macro ce qui donne les deux déclarations suivantes :

#define NB_PROTO 8
char *LISTE;

Faire une protophrase

La première fonction est celle de la construction d’une protophrase. Sur le même modèle nous avons plusieurs fonction qui déterminent des protophrases différentes.

(1)aaaa void fabrique_proto_1( void)
aaaaaa {
(2) aaaa char *mot[14];
aaaaaaa int i;
(3) aaaaaaaaa mot[0] = "La";
aaaaaaaaaaaa mot[1] = select(nom_f_cons);
aaaaaaaaaaaa mot[2] = select(adj_f_s);
aaaaaaaaaaaa mot[3] = "qui";
aaaaaaaaaaaa mot[4] = select(verb_pr_3s_obj);
aaaaaaaaaaaa mot[5] = "dans les";
aaaaaaaaaaaa mot[6] = select(nom_m_p);
aaaaaaaaaaaa mot[7] = select(adj_m_p);
aaaaaaaaaaaa mot[8] = "peut";
aaaaaaaaaaaa mot[9] = select(infinitif);
aaaaaaaaaaaa mot[10] = "l'";
aaaaaaaaaaaa mot[11] = select(nom_f_voy);
aaaaaaaaaaaa mot[12] = select(adj_f_s);
aaaaaaaaaaaa mot[13] = ".";

(4)aaaaaaaaaafor( i=0; i<14; i++)
aaaaaaaaaaaaaaaaa ajoute_liste(mot[i]);
aaaaaa }

(1) La fonction « fabrique_proto_1 » n’a pas de paramètre et ne renvoie rien.

(2) Son principe est de stocker un ensemble de mots selon la définition d’une protophrase. Le stockage des mots est effectué à l’aide d’un tableau de char* de la taille du nombre des éléments qui composent le syntagme. Il est nommé « mot ».

(3) Chaque position du syntagme correspond à la position équivalente du tableau par numéro d’ordre. Certaines positions sont affectées d’un mot fixe, d’une ponctuation ou d’un article suivi d’une apostrophe. D’autres sont piochées dans la liste du genre voulu avec un appel à la fonction « select » qui prend en argument la liste dans laquelle sélectionner un mot avec le nom du tableau correspondant.

(4) A l’issue du remplissage du tableau « mot » il s’agit d’ajouter chaque mot de la phrase obtenue au texte en cours d’élaboration. C’est le rôle de la fonction « ajoute_liste » qui prend en argument un char* et est appelée pour chaque mot du syntagme via une boucle for.

Détail de la fonction « select »

La sélection d’un mot dans une liste s’effectue de la façon suivante :

(1)aaaa char* select(char** type_mot)
aaaaaa {
aaaaaa int i;
(2) aaaaaaaaa for ( i = 0 ; type_mot[ i ] != NULL; i++)
aaaaaaaaaaaaaaaaaaa ;
(3) aaaaaaaaa i = rand% i ;
(4) aaaaaaaaa return type_mot[ i ];
aaaaaa }

(1) La fonction « select » prend un pointeur de pointeur sur char en paramètre, à savoir une double indirection avec un char**. Ceci afin de pouvoir passer en argument un tableau de char* qui est lui-même une double indirection, un char**. La fonction renvoie une chaîne de caractères

(2) Il n’y a pas dans le programme de référence à la taille des listes et le nombre des éléments n’est pas connu. Ainsi le premier point est de compter le nombre des éléments de la liste. La variable « i » est incrémentée de 0 à la fin de la liste.

(3) Ensuite un nombre pseudo-aléatoire modulo i est donné par la fonction « rand » et affecté à i.

(4) Ce nombre détermine le mot de la liste que retourne la fonction avec l’expression « type_mot[ i] »

Détail de la fonction « ajoute_liste »

Une fois l’ensemble des mots du syntagme de la protophrase réuni dans le tableau « mot » de la fonction « frabrique_proto_1 », il reste à copier dans l’ordre chacun de ces mots à la suite du texte en cours d’élaboration et stocké dans la variable globale de type char*
« LISTE »

(1)aaaa void ajoute_liste(char* mot)
aaaaaa {
(2) aaaaaint t1=0, t2;

(3) aaaaaaaaa t2=strlen(mot);

(4) aaaaaaaaa if( ! LISTE)
aaaaaaaaaaaaaaa LISTE = calloc(sizeof(char),t2);
aaaaaaaaaaaa else{
(5) aaaaaaaaaaaa t1=strlen(LISTE)+1;
aaaaaaaaaaaaaaa LISTE = (char*)realloc(LISTE,sizeof(char)*(t1+t2));
(6) aaaaaaaaaaaa if( mot[0] != '.' && mot[0] != ',' && LISTE[t1-2] != 39 ){
aaaaaaaaaaaaaaaaaa LISTE[t1-1]= 32;
aaaaaaaaaaaaaaaaaa LISTE[t1]='\0';
aaaaaaaaaaaaaaa }
aaaaaaaaaaaaaaa else
(7) aaaaaaaaaaaaaaa LISTE[t1-1]='\0';
aaaaaaaaaaaa }
(8) aaaaaaaaa strcat(LISTE,mot);
aaaaaa }

(1) La fonction « ajoute_liste » ne renvoie rien et a un char* en paramètre. Il s’agit du mot à ajouter à la liste des mots du texte en cours.

(2) Deux variables sont nécessaires, une pour avoir la taille du texte déjà réalisé et l’autre pour avoir la taille du mot à ajouter de façon à pouvoir augmenter la taille du bloc de mémoire en conséquence.

(3) Initialisation de la variable « t2 » avec un appel à la fonction « strlen » de la librairie standard du C <string.h>. Cette fonction retourne la longueur en nombre de caractères de la chaîne passée en argument (sans compter le caractère nul de la fin).

(4) S’il s’agit du premier mot de la liste LISTE prend la taille « t2 » du premier mot et une adresse mémoire lui est affectée.

(5) Si LISTE est déjà une adresse mémoire, des mots lui ont été affectés et il est nécessaire de connaître sa taille afin de pouvoir l’augmenter en proportion du nouveau mot à ajouter. C’est ce qui motive l’appel à la fonction « strlen » qui prend LISTE en argument et affecte la valeur de retour à la variable « t1 ». La longueur total du texte, t1, est augmentée de 1 afin de mettre soit un espace pour séparer deux mots soit le caractère nul nécessaire à la fin de la chaîne de caractères. La taille du bloc de mémoire à l’adresse de LISTE est ensuite réallouée selon le nouveau nombre total des caractères ( t1+t2 ) requis par le texte.

ATTENTION : l
a fonction suppose que chaque protophrase se termine par un point, une virgule ou une apostrophe. En effet à l'ajoute de chaque mot un caractère est ajouté à la liste et cette place supplémentaire est utilisée tantôt pour mettre un espace (lorsque la première lettre du mot n'est ni un point, ni une virgule, ni une apostrophe) et dans ce cas la liste n'est pas terminée par un '\0', tantôt pour mettre le '\0' final au moment de la concaténation puisque chaque mot est une chaine terminée par '\0'. De ce fait si le proto ne se termine pas par un point et que tous les caractères ajoutés ont servi à des espaces la chaine peut ne pas être terminée avec le '\0' et provoquer une erreur.

(6) Il s’agit de gérer les espaces qui séparent les mots. Il y en a toujours un sauf dans le cas de ponctuation et d’une apostrophe. Précisons que chaque lettre fait l’objet d’un codage et peut être traduite par un nombre entier . Ainsi la valeur ASCII de l’apostrophe est 39 et celle de l’espace est 32. Si le mot n’est pas un point, s’il n’est pas une virgule et s’il n’est pas une apostrophe, un espace est ajouté à la dernière position de la chaîne liste, juste après le dernier caractère et juste derrière lui est indiquée la fin de la chaîne avec le caractère nul.

(7) Si en revanche le mot est un point, une virgule ou une apostrophe, il n’y a pas d’espace à ajouter et la fin de la chaîne est juste derrière le dernier caractère, indiquée par le caractère nul.

(8) Pour finir, une fois l’espace mémoire préparé et la jonction entre la liste existante et le nouveau mot éclaircie, il reste à copier le mot à la fin de la liste. Pour ce faire c’est un appel à la fonction « strcat » de la librairie standard <string.h>. Cette fonction prend deux chaînes de caractères en argument et concatène la seconde à la suite de la première à partir du premier caractère nul qu’elle rencontre., et sous réserve que l’espace mémoire soit suffisant.
Le principe de la fabrication des protophrases est mis en place ainsi que l’élaboration progressive du texte final à travers une liste de mots. Il reste à implémenter la mise en œuvre par le programme.

3. Processus générateur

Le principe génératif est mis en scène de la façon suivante :

(1)aaaa while(!key[KEY_ESC]){

(2)aaaaaaaaa if(key[KEY_ENTER]){
aaaaaaaaaaaaaaa degrade_pas_variable(pal);
aaaaaaaaaaaaaaa clear(screen);
(3) aaaaaaaaaaaa construit_texte(irand(5)+1);
(4)aaaaaaaaaaaaaaffiche_texte();
aaaaaaaaaaa }
aaaaaaa }

(1) Au sein d’une boucle while infinie sauf pression sur la touche Echap :

(2) si pression sur la touche Entrée (enter), une nouvelle palette de couleur est créée, l’écran est effacé,

(3) La fonction « construit_texte » est appelée. Cette fonction prend un nombre aléatoire de protophrases en argument dans la fourchette de 1 à 5.

(4) Lorsque le texte est créé il est affiché à l’écran avec un appel à la fonction « affiche_texte ».

Détail de la fonction « construit_texte »

Cette fonction permet de sélectionner plusieurs protophrases et de les enchaîner en une seule liste, le texte résultant :

(1)aaaa void construit_texte(int nombre_proto)
aaaaaa {
aaaaaaa int choix;

(2)aaaaaaaaaa if( LISTE )
aaaaaaaaaaaaaaaa free(LISTE);
aaaaaaaaaaaaaLISTE = NULL;

(3)aaaaaaaaaa do{
(4)aaaaaaaaaaaaaaa choix = irand(NB_PROTO);
(5)aaaaaaaaaaaaaaa switch(choix){
aaaaaaaaaaaaaaaaaaaaaacase 0 : fabrique_proto_1(); break;
aaaaaaaaaaaaaaaaaaaaaacase 1 : fabrique_proto_2(); break;
aaaaaaaaaaaaaaaaaaaaaacase 2 : fabrique_proto_3(); break;
aaaaaaaaaaaaaaaaaaaaaacase 3 : fabrique_proto_4(); break;
aaaaaaaaaaaaaaaaaaaaaacase 4 : fabrique_proto_5(); break;
aaaaaaaaaaaaaaaaaaaaaacase 5 : fabrique_proto_6(); break;
aaaaaaaaaaaaaaaaaaaaaacase 6 : fabrique_proto_7(); break;
aaaaaaaaaaaaaaaaaaaaaacase 7 : fabrique_proto_8(); break;
aaaaaaaaaaaaaaaaaaaaaadefault : fabrique_proto_2(); break;
aaaaaaaaaaaaaa aaa}
(6)aaaaaaaaaaaaaaa nombre_proto--;
(7)aaaaaaaaaa }while(nombre_proto > 0);

(8)aaaaaaaaaa correction_majuscules();
aaaaaa }

(1) La fonction « construit_texte » prend comme paramètre le nombre total de protophrases à additionner pour faire un texte.

(2) Le premier point consiste à libérer la mémoire occupée par le texte précédent, si ce texte existe à savoir que la variable LISTE n’est pas nulle.

(3) Ensuite, les protophrases sont sélectionnées au hasard selon le nombre souhaité passé en argument et compte-tenu du nombre total des modèles dans le programme. Pour ce faire une boucle do-while est utilisée.

(4) Dans la boucle un nombre aléatoire est tout d’abord obtenu dans la fourchette du nombre maximum de protophrases défini avec la macro NB_PROTO (Nous avons déjà rencontré la macro « irand ». Elle fait correspondre à « irand(n) » « rand()%n » ). Ce nombre est affecté à la variable « choix ».

(5) Un switch, fondé sur la valeur de la variable « choix », dirige vers l’appel de la fonction de fabrication de protophrase correspondante.

(6) Le nombre de protophrases qui restent à fabriquer et ajouter à la liste est diminué de 1.

(7) L’action reprend tant qu’il y a des protophrases à ajouter à la liste, c’est-à-dire que le nombre des protophrases à faire est supérieur
à 0.

(8) Une fois le texte terminé, il y a à corriger les majuscules après les points. C’est le rôle de la fonction « correction_majuscules ».

Détail de la fonction « correction_majuscules »

Cette fonction parcourt la chaîne de caractères LISTE et à chaque fois qu’un point est trouvé, la première lettre du premier mot suivant est mise en majuscule :

(1)aaaa void correction_majuscules(void)
aaaaaa {
(2)aaaaachar *p = LISTE;
aaaaaaa int flag = TRUE;

(3)aaaaaaaaa do{

(4) aaaaaaaaaaaaaif( *p == '.' )
aaaaaaaaaaaaaaaaaaaaflag=TRUE;

(5) aaaaaaaaaaaaaif ( flag == TRUE && isgraph ( *p ) ){
aaaaaaaaaaaaaaaaaaaaflag = FALSE;
aaaaaaaaaaaaaaaaaaaaif ( islower ( *p ) )
aaaaaaaaaaaaaaaaaaaaaaaa*p -= 32;
aaaaaaaaaaaaaaaa}
(6)aaaaaaaaa }while ( *p++ );
aaaaaa }

(1) La fonction « correction_majuscules » ne renvoie pas de valeur et n’a pas de paramètre.

(2) Deux variables locales sont utilisées. La première « p » est un char* initialisé sur l’adresse de LISTE qui va servir à parcourir la liste. La seconde « flag » sert à marquer la rencontre éventuelle d’un point.

(3) (6) Boucle do-while avec pour test d’arrêt le fait d’arriver sur le caractère nul de fin de chaîne. L’expression « *p++ » incrémente p de 1 en 1 et est équivalente à *(p++). La valeur de *p++ est toutefois celle du caractère pointé avant incrémentation de p ; « ++ » placé après p ne le modifie qu’après utilisation du caractère pointé. Lorsque *p++ est égal à NULL c’es-à dire 0, le test devient faux (renvoie de la valeur 0) et la boucle s’arrête.


(4) La première instruction teste si à la position courante de la chaîne il y a un point. Si oui, la variable flag est mise sur TRUE (la valeur -1 dans l’environnement Allegro). Cette variable est initialisée à TRUE afin de repérer la première lettre du texte qui doit prendre une majuscule et n’est pas précédée d’un point.

(5) Deuxième test : si flag est sur TRUE, un point a été rencontré et il faut mettre en majuscule la première lettre qui arrive. Il faut attendre cette première lettre et c’est le rôle de l’appel de la fonction « isgraph » de la librairie <ctype.h> standard du C. Cette fonction retourne vrai si son argument est un caractère affichable différent d’un espace et faux sinon. Si flag est TRUE et « isgraph » retourne vrai alors les instructions sont effectuées :
flag reprend la valeur FALSE et si la lettre courante n’est pas déjà une majuscule elle est transformée en majuscule en modifiant simplement sa valeur ASCII (il y a un décalage de 32 entre les lettres majuscules et les minuscules).

Détail de la fonction « affiche_texte »

Une fois le texte mis en forme et corrigé, il n’y a plus qu’à l’afficher, c’est le rôle de l’appel de la fonction « affiche_texte »

(1)aaaa void affiche_texte( void )
aaaaaa {
(2)aaaa char *c, *p=LISTE;
aaaaaaachar *tmp=malloc(sizeof(char)*2);
aaaaaaaint x,y,tx,color=0;
aaaaaaaint i,pasx,pasy;

(3)aaaaaaaax=ECRAN_X/8;
aaaaaaaaaa tx=ECRAN_X-x;
aaaaaaaaaa pasx = 8;
aaaaaaaaaa pasy = pasx+8;
aaaaaaaaaa y = x*2;
aaaaaaaaaa tmp[ 1 ]='\0';

(4)aaaaaaaado{
(5)aaaaaaaaaaaaif(*p==32){
aaaaaaaaaaaaaaaaaac=p+1;
aaaaaaaaaaaaaaaaaafor( i=0; (*c != 32) && (*c != 0); c++,i++)
aaaaaaaaaaaaaaaaaaaaaa;
aaaaaaaaaaaaaaaaaaif( x + (i*pasx) >tx ){
aaaaaaaaaaaaaaaaaaaaaax=ECRAN_X/8;
aaaaaaaaaaaaaaaaaaaaaay+=pasy;
aaaaaaaaaaaaaaaaaaaaaap++;
aaaaaaaaaaaaaaaaaa}
aaaaaaaaaaaaaa}
(6)aaaaaaaaaaaa*tmp = *p;
aaaaaaaaaaaaaaatextout( screen, font, tmp, x, y, ( (++color)%255 )+1 );
aaaaaaaaaaaaaaax+=pasx;
aaaaaaaaaaaaaaarest( 10 );
(7)aaaaaaaa}while( *p++ && !key[KEY_ESC] );
(8)aaaaaaaafree( tmp );
aaaa }

(1) La fonction « affiche_texte » n’a pas de paramètre et ne renvoie aucune valeur.

(2) Plusieurs variables locales sont nécessaires à son fonctionnement. Trois chaînes de caractères dont une, « tmp », qui est initialisée avec une taille de deux caractères (un caractère plus le caractère nul). En fait le texte va être affiché lettre par lettre. La variable « tmp » va permettre d’isoler chaque lettre et d’utiliser une fonction de la librairie Allegro pour l’affichage d’une chaîne de caractère. La chaîne de caractère « p » est initialisée sur LISTE et va servir à parcourir le texte. Les variables « x » et « y » correspondent à la position dans l’écran. « tx » va servir à délimiter la taille horizontale pour le texte. Comme le texte sera toujours relativement court il n’ y a pas de limite verticale. Les variables « pasx » et « pasy » déterminent les pas d’avancement horizontaux et verticaux pour chaque lettre. La variable « color » est pour la couleur.

(3) La variable x, position horizontale, est initialisée pour la marge gauche sur un huitième de l’écran. Pour la marge droite également avec la taille de l’écran moins un huitième. La marge en haut est deux fois la marge horizontale. Il n’y a pas de marge en bas mais les textes restent au centre de l’écran. Le pas d’avancement est de 8 pixels à l’horizontal. Il correspond à la taille de chaque lettre de la police utilisée (une police de base comme celle du « Bios » et fournie par l’environnement ). Il est le double en vertical.

(4) (7) Boucle do-while. Le test d’arrêt est du type de celui vu avec la fonction précédente « correction_majuscules » et comporte un test supplémentaire sur l’état de la touche Echap afin de pouvoir quitter le programme sans attendre la fin de cette boucle si demandé par l’utilisateur.

(5) Le premier point est de savoir dès le début de chaque mot s’il va tenir sur la ligne ou pas, et s’il faut passer à la ligne suivante en incrémentant y. Pour ce faire, à partir de chaque espace trouvé (valeur ASCII 32), le nombre de lettres est compté jusqu’à l’espace suivant ou la fin éventuelle du texte. C’est le rôle des variables « i » et « c » dans la boucle for. Une fois ce nombre obtenu, il est multiplié par le pas d’avancement, additionné à la position horizontale courante et le résultat est comparé à la borne horizontale droite. S’il est supérieur, il faut passer à la ligne c’est-à-dire augmenter y de pasx et faire revenir la position horizontale sur la marge de gauche.

(6) Quoiqu’il en soit, la lettre courante est affectée à la première position de la chaîne tmp. Ensuite appel de la fonction « textout » qui peut afficher une chaîne de caractère dans une bitmap. Cette fonction prend plusieurs arguments et voici son prototype :
void textout(BITMAP *bmp, const FONT *f, const char *s, int x, y, int color);

Le premier argument est la bitmap de référence, le second la police utilisée, le troisième la chaîne de caractère à afficher, les quatrième et cinquième la position initiale et le dernier la couleur. Pour notre appel la fonction prend la bitmap écran, screen, la fonte « font » par défaut, la chaîne de caractère tmp c’est-à-dire la lettre courante à la position ( x, y ) et avec une couleur qui évolue de 1 à 255 et selon la palette.
Une fois cet appel effectué et la lettre affichée, la position en « x » est incrémentée de pasx. Un temps est ajouté afin de voir le mouvement d’affichage.

(8) Après fin de la boucle l’espace mémoire alloué à la variable tmp est libéré.

Eléments bibliographiques cités

BALPE Jean-Pierre, L'art et le numérique, Hermès, Paris 2000.

BRAQUELAIRE Jean-Pierre, Méthodologie de la programmation en C, Masson, Paris 1998.

DROUILLON Frédéric, Chimères et gargouilles informatique, thèse de Doctorat en Sciences de l’Information et de la Communication, Université de Paris 8 – Vincennes Saint Denis, octobre 2003.

GANASCIA, Jean-Gabriel, L’âme-machine, les enjeux de l’intelligence artificielle, Seuil, Paris 1990.

HUYGHE François-Bernard, La langue de coton, Robert Laffont, Paris 1991.

KERNIGHAN Brian, RITCHIE Dennis, Le langage C, Masson, Paris 1995, première édition 1978.