Extension pour I6 : gestion des adjectifs pour le parser

Avant-propos

Lors de mon premier projet de jeu (toujours à l’état d’ébauche) j’ai très vite buté sur un problème. J’étais dans un tramway, il y avait donc :

  • le tramway
  • le conducteur du tramway
  • les vitres (du tramway)…

Je voulais que « regarder le tramway » porte sans ambiguïté sur l’objet tramway, pas qu’on me demande « le tramway ou le conducteur du tramway ? », mais que je puisse aussi désigner le « conducteur du tramway ». Je ne pouvais pas donc donner au conducteur comme propriété name : « conducteur » « du » « tramway » (« regarder du » était équivalent à « regarder conducteur », en plus). J’ai ainsi vite voulu que certains mots permettant de désigner un objet soient optionnels et de faible priorité.

Après avoir essayé l’extension pname.h qui ne répondait que partiellement au problème, j’ai fini par m’en inspirer pour ajouter aux objets qui le nécessitent une propriété « adjective ». Les mots définis dans cette propriété sont acceptés pour désigner un objet, à condition qu’au moins un mot de la propriété « name » soit présent également.

Exemple raccourci de code (initialement) :

Object conducteur_tramway1 "conducteur" tramway1
  with name 'conducteur' 'chauffeur' 'homme' 'bonhomme' 'mec' 'type' 'gars' 'traminot' 'etre',
        adjective 'de' 'du' 'tramway' 'tram',
        description "Malgré son sourire bienveillant, vous voyez bien que le conducteur commence à s'impatienter.", 
[...]

Puis lors du projet lieux communs, où cette gestion des adjectifs a été (un peu) utilisée, j’ai trouvé que les petits mots « de », « des », « du », « à », « aux », « en », etc… gagneraient à être tout simplement ignorés quand ils sont entre deux mots désignant le même objet.
La création d’une fonction ParseNoun à cet effet simplifie la déclaration de l’objet puisqu’il n’est plus nécessaire de se préoccuper de ces petits mots de liaison (attention, ils ne sont acceptés que s’ils sont entre deux mots reconnus pour le même objet, sinon ils sont traités comme d’habitude) :

Object conducteur_tramway1 "conducteur" tramway1
  with name 'conducteur' 'chauffeur' 'homme' 'bonhomme' 'mec' 'type' 'gars' 'traminot' 'etre',
        adjective 'tramway' 'tram',
        description "Malgré son sourire bienveillant, vous voyez bien que le conducteur commence à s'impatienter.", 
[...]

L’extension « adjectifs.h » (nom temporaire)
Pour pouvoir utiliser la gestion des adjectifs (et compléments du nom), il faut :

  1. Ajouter la ligne suivante juste avant l’inclusion de Parser :
Replace Identical; ! pour la gestion des adjectifs

Cette ligne dit juste qu’on va re-définir la routine Identical, qui est normalement définie dans les libs inform de base.

  1. Ajouter le code suivant juste après l’inclusion de Parser
!--------------------------------------------
! Attributs et propriétés
!--------------------------------------------
Property adjective; ! pour la gestion des adjectifs

!--------------------------------------------
! Directives de remplacement
!--------------------------------------------
! (Samuel Verschelde, tiré de pname.h)
! Cette fonction est recopiée de parserm.h, avec un ajout pour tenir
! compte de la propriété "adjective."
[ Identical o1 o2 p1 p2 n1 n2 i j flag;
    if (o1 == o2) rtrue;  ! This should never happen, but to be on the safe side
    if (o1 == 0 || o2 == 0) rfalse;  ! Similarly
    if (parent(o1) == compass || parent(o2) == compass) rfalse; ! Saves time

    !  What complicates things is that o1 or o2 might have a parsing routine,
    !  so the parser can't know from here whether they are or aren't the same.
    !  If they have different parsing routines, we simply assume they're
    !  different.  If they have the same routine (which they probably got from
    !  a class definition) then the decision process is as follows:
    !
    !     the routine is called (with self being o1, not that it matters)
    !       with noun and second being set to o1 and o2, and action being set
    !       to the fake action TheSame.  If it returns -1, they are found
    !       identical; if -2, different; and if >=0, then the usual method
    !       is used instead.

    if (o1.parse_name ~= 0 || o2.parse_name ~= 0) {
        if (o1.parse_name ~= o2.parse_name) rfalse;
        parser_action = ##TheSame; parser_one = o1; parser_two = o2;
        j = wn; i = RunRoutines(o1,parse_name); wn = j;
        if (i == -1) rtrue;
        if (i == -2) rfalse;
    }

    !  This is the default algorithm: do they have the same words in their
    !  "name" (i.e. property no. 1) properties.  (Note that the following allows
    !  for repeated words and words in different orders.)

    p1 = o1.&1; n1 = (o1.#1)/WORDSIZE;
    p2 = o2.&1; n2 = (o2.#1)/WORDSIZE;

    !  for (i=0 : i<n1 : i++) { print (address) p1-->i, " "; } new_line;
    !  for (i=0 : i<n2 : i++) { print (address) p2-->i, " "; } new_line;

    for (i=0 : i<n1 : i++) {
        flag = 0;
        for (j=0 : j<n2 : j++)
            if (p1-->i == p2-->j) flag = 1;
        if (flag == 0) rfalse;
    }

    for (j=0 : j<n2 : j++) {
        flag = 0;
        for (i=0 : i<n1 : i++)
            if (p1-->i == p2-->j) flag = 1;
        if (flag == 0) rfalse;
    }

    ! DEBUT MODIFICATION POUR LES ADJECTIFS
    ! test sur la propriété adjective :
    ! deux objets qui ont le même "name" mais
    ! pas le même "adjective"
    if ((o1 provides adjective) ~= (o2 provides adjective))
        rfalse;

    if (o1 provides adjective && o2 provides adjective)
    {
        n1=(o1.#adjective)/WORDSIZE; n2=(o2.#adjective)/WORDSIZE;

        ! If there are a different number of words in the adjective
        ! properties, they are distinguishable.
        if (n1 ~= n2)
            rfalse;

        p1=o1.&adjective; p2=o2.&adjective;

        for (i=0 : i<n1 : i++)
        {
            flag = 0;
            for (j=0 : j<n2 : j++)
                if (p1-->i == p2-->j) flag = 1;
            if (flag == 0) rfalse;
        }

        for (j=0 : j<n2 : j++)
        {
            flag = 0;
            for (i=0 : i<n1 : i++)
                if (p1-->i == p2-->j) flag = 1;
            if (flag == 0) rfalse;
        }
    }
    ! FIN MODIFICATION POUR LES ADJECTIFS

    !  print "Which are identical!^";
    rtrue;
];

Il s’agit de la définition de la propriété « adjective » que l’on pourra ensuite définir pour chaque objet qui le nécessite, et du code de la routine Identical, modifiée pour gestion des adjectifs. Pour information cette routine sert juste à dire si deux objets sont identiques, en comparant en plus de leur propriété « name », la propriété « adjective » si elle est définie.

  1. Inclure le code suivant après l’inclusion de VerbLib
! (Samuel Verschelde, tiré et amélioré de pname.h) gestion des adjectifs
! et gestion aussi des mots de liaison comme dans "boule DE cristal"
! => de est accepté, ainsi que d'autres mots que l'on peut trouver
! entre deux mots désignant un même objet.
[ ParseNoun obj n m p w dict_flags_of_noun;
    dict_flags_of_noun = 0;

    w = NextWord();
    while((WordInProperty(w,obj,name) == 1) || (WordInProperty(w,obj,adjective) == 1) || (w=='de' or 'du' or 'des' or 'd^' or 'la' or 'le' or 'l^' or 'en' or 'a//' or 'à//' or 'au' or 'aux'))
    {
        ! remarque : si un mot est à la fois dans name et adjective, il ne sera
        ! compté qu'une fois, pour la propriété name
        if (WordInProperty(w,obj,name) == 1)
        {
            dict_flags_of_noun = ((w->#dict_par1) & $$01110100);
            if (dict_flags_of_noun & 4) ! si c'est un pluriel (//p)
            {
                parser_action = ##PluralFound; ! notifier qu'un pluriel pouvant désigner
                                               ! plusieurs objets a été trouvé
            }
            m++;
            p=0;
        }
        else
        {
            if (WordInProperty(w,obj,adjective) == 1)
            {
                n++;
                p=0;
            }
            else
            {
                ! Si le mot est 'de', 'du', 'des', 'la', 'en', 'a', 'au' ,'aux' :
                if (w=='de' or 'du' or 'des' or 'd^' or 'la' or 'le' or 'l^' or 'en' or 'a//' or 'à//' or 'au' or 'aux')
                {
                    ! ajouter 1 à la variable p, qui contient le score à retirer
                    ! au final (afin de revenir en arrière en cas d'occurrences qui ne
                    ! sont pas comprises entre deux noms reconnus).
                    n++; ! compter ce mot comme pertinent est discutable,
                         ! mais sans cela cela ne fonctionne pas
                    p++;
                }
            }
        }

        w = NextWord();
    }
    wn = wn -p -1; ! retour arrière car le dernier mot lu n'a pas été reconnu
    if (m == 0) return 0; return (m+n-p);
];

Il s’agit de la routine ParseNoun, tirée de pname.h puis modifiée pour gérer les adjectifs et accepter les petits mots de liaison au milieu d’une expression.

Voilà, vous avez tout ce qu’il faut pour supprimer une sérieuse limitation d’inform 6!

On peut maintenant : définir une « porte en bois » et une « table en bois » à côté d’un « bois de hêtres » sans la moindre difficulté, une « cuillère à pot » à côté d’un « pot de crème » (dans ce dernier cas crème sera mis dans name plutôt que adjective pour permettre de désigner le pot par le mot crème seul).
Dans le cas de la place centrale dans le jeu Ma princesse adorée, cela permet de définir facilement :

  • la taverne (name : « taverne »)
  • la porte de la taverne (name : « porte », adjective : « taverne »)
  • l’école (name : « école »)
  • la porte de l’école (name : « porte », adjective : « école »)
  • la boutique (name : « boutique »)
  • la porte de la boutique (name : « porte », adjective : « boutique »)
    Chacun pourra être défini sans ambiguïté, et si un joueur tape « ouvrir la porte », si chaque porte a un nom du type « porte de la taverne », le parser demandera : « Précisez : la porte de la taverne, la porte de l’école ou la porte de la boutique ». Le joueur répondra par exemple « de la taverne » et hop…

On dirait que Jon Ingold vient de faire une extension Inform 7 qui ressemble un peu à ton extension pour Inform 6 ?

Non, c’est assez différent. Moi je propose juste (et en français seulement), de permettre de déclarer des mots permettant de préciser un objet (adjectifs ou compléments du nom) mais qui ne sont pas prioritaires sur les mêmes mots déclarés en tant que nom de l’objet. Et au passage je tolère des « des », « les », « au », etc… entre les mots.

L’extension de Jon Ingold porte sur la résolution des ambiguïtés en général, en tentant de corriger certains comportements regrettables du parser (mais je ne suis pas sûr que certains ne puissent pas être résolus au niveau du codage sans son extension, les exemples qu’il a donnés ne m’ont pas parus très parlants).

Cependant certains exemples qu’il donnent peuvent en effet être résolus en mettant certains mots en adjectifs plutôt qu’en nom (je pense notamment à tree house, qui pourrait être déclaré avec le nom house et l’adjectif tree, il serait donc moins pertinent que le chêne, oak tree).

Annonce : je viens de mettre à jour cette extension. J’ai modifié le code de la fonction ParseNoun, afin qu’elle gère correctement les les pluriels pouvant désigner plusieurs objets à la fin (par exemple « pieces//p » pour désigner plusieurs objets « piece ») (voir le chapitre 29 du DM4 : inform-fiction.org/manual/html/s29.html)

Riez tant que vous pouvez, vilains bugs, Stormi n’aura pas la paix avant de vous avoir éradiqués jusqu’au dernier ! :wink:

Bravo pour ce travail en tout cas.

tu n’as pas placé cette extension sur le svn ? Je ne la trouve pas. On pourrait peut-être essayer de l’inclure dans « lieux communs » également ?

à la réflexion, je pensais qu’il était possible de facilement réutiliser les bibliothèques actuelles dans « lieux communs », mais j’avais oublié qu’on les avait séparées. Je pense qu’essayer de les réintégrer risquerait plus de casser des choses que d’être bénéfiques, idem pour rajouter d’autres extensions.

Par contre si cela t’intéresse de tester ton extension en cas réel, sans risquer grand chose, tu peux le faire par exemple sur ce jeu qui est prévu pour cela :
svnweb.tuxfamily.org/listing.php … rev=0&sc=0

Ce qui est difficile, c’est qu’il faut insérer des bouts de code à divers endroit, ce qui nécessite de faire plusieurs fichiers. Mais en effet il faudrait que je le fasse, avec un petit lisez-moi qui explique où inclure les fichiers.

Eh si l’adjectif est placé avant le nom de l’objet!?
Si il y a une porte et une petite porte.
<< ouvrir la petite porte >>
Il me semble que toutes ces routines attendent en paramètre le nom de l’objet. Or « petite » n’est pas un nom d’objet et n’est donc pas reconnu.

L’adjectif peut bien sûr être avant le nom de l’objet, la fonction ParseNoun n’oublie pas ce cas.

Note : Je viens de corriger une coquille dans la fonction ParseNoun

Je viens de corriger un problème soulevé par Adrien : pour un objet ayant ‹ montre › en nom et ‹ gousset › en adjectif, il était impossible de le désigner par « montre à gousset » (avec ou sans accent).

C’est corrigé, et j’en profite pour en pester une fois de plus sur inform et sa gestion archaïque voire barbare des chaînes de caractère :slight_smile:

Le code du premier post de ce sujet a donc été mis à jour. Le changement concerne uniquement la fonction ParseNoun.