miércoles, 4 de agosto de 2010

Desambiguando en Inform6 (II)

Vamos con el segundo ejemplo de desambiguación.
Esta vez vamos a hacer una caja de cerillas que contendrá cerillas de dos tipos:
1. Cerillas (esto no tiene ningún misterio)
2. Cerillas de la caja. (son las mismas cerillas de antes, sólo que están circunstancialmente dentro de la caja de cerillas, por si no quedaba claro).


Por tanto, tenemos que distinguir primeramente "cerillas de caja" de "caja de cerillas".
Esto ya lo hicimos en el ejemplo anterior con la leche y las jarras, pero con un mal resultado en cuanto intentamos interactuar con los dos objetos problemáticos a la vez en la misma orden. Ahora no ocurrirá así.

También debemos distinguir, como se anticipó, entre "cerillas" y "cerillas de la caja": las cerillas que tenemos en nuestra posesión o hemos sacado, de las que en esos momentos se encuentran en la caja.

El código, para InfSP, es el siguiente:

Constant Story "Desambiguación con Caja de Cerillas";
Constant ADMITIR_COMANDO_SALIDAS;
#Include "Parser";
#Include "Verblib";
[ Initialise;
location=habitacion;
rtrue;
];

!###########################################
object limbo "limbo";

object habitacion "habitacion"
with
description "...",
has light;

object jarron "jarrón" habitacion
with name 'jarron',
has container open;

object caja_de_cerillas "caja de cerillas" habitacion
with parse_name [ i j j2 j3 j4;
          j=NextWord();
          if (j=='caja'){!ee
                          i++;
                          j2=NextWord();
                          j3=NextWord();
                          j4=NextWord();
                          if(j2=='de'){if(j3=='las' && j4=='cerillas')i=i+3;
                                       if (j3=='cerillas')i=i+2;}
                          }!ee                             
          return i;
],


has female container openable ~open;

class cerillas
with short_name "cerilla",
plural "cerillas",
description [;
        if(self in caja_de_cerillas)"Una cerilla que está dentro de la caja de cerillas.";
        "Una cerilla normal y corriente.";
        ],

parse_name [ i j j2 j3 j4;
          j=NextWord();
          if (j=='cerilla')i++;
          if (j=='cerillas'){i++; parser_action=##PluralFound;}
          if (i>0 && action_to_be~=##Take or ##Remove){
                          j2=NextWord();
                          j3=NextWord();
                          j4=NextWord();
                    if(self in caja_de_cerillas){!¿está dentro de la caja?
                          if(j2=='de'){if(j3=='la' && j4=='caja')i=i+3;
                                       if (j3=='caja')i=i+2;}
                    }!¿está dentro de la caja?
                    }
          return i;         
],
has female;

cerillas c1 "" caja_de_cerillas;
cerillas c2 "" caja_de_cerillas;
cerillas c3 "" caja_de_cerillas;
cerillas c4 "" caja_de_cerillas;
cerillas c5 "" caja_de_cerillas;
cerillas c6 "" caja_de_cerillas;
cerillas c7 "" caja_de_cerillas;
cerillas c8 "" caja_de_cerillas;
!###########################################
! Procedemos a reemplazar el Parsenoun de la librería por el código de la
! librería Intnombre que hemos descargado. Con esto conseguimos que los
! adjetivos puntúen previa detección de un nombre.
Replace ParseNoun;
Include "IntnombreINFSP.h";
#Include "SpanishG";


Vamos a analizar este trozito de código del objeto caja_de_cerillas para ver lo que estamos haciendo:
with parse_name [ i j j2 j3 j4;
          j=NextWord();
          if (j=='caja'){!ee
                          i++;
                          j2=NextWord();
                          j3=NextWord();
                          j4=NextWord();
                          if(j2=='de'){if(j3=='las' && j4=='cerillas')i=i+2;
                                       if (j3=='cerillas')i=i+2;}
                          }!ee                
          return i;
],


En lugar de definir los nombres y los adjetivos definiéndolos previamente como de costumbre, vamos a mirarlos paso a paso con la función parse_name.

Nextword(); devuelve el valor de la siguiente palabra escrita, consecutivamente. Como la primera es el verbo (o cualquier texto conector de la gramática; o cualquier palabra supérflua que el parser decarte, como un artículo), el primer Nextword() leerá la segunda palabra.
Para que se entienda mejor, si de buenas a primeras escribiéramos:
Nextword(); Nextword(); j=Nextword();
la variable j leería la cuarta palabra que hemos escrito.

Bien, según el código de arriba, si la segunda palabra escrita es 'caja', le damos un punto y procedemos a comprobar las demás para aumentar la puntuación del objeto, utilizando las variables j2, j3 y j4 para almacenar la tercera, cuarta y quinta.
Y es que podemos referirnos a la caja (normalmente) con una cadena de hasta cuatro palabras:
caja
caja de cerillas
caja de las cerillas


De modo que si escribimos simplemente "caja", este objeto recibirá un punto; y si escribimos "caja de las cerillas" o "caja de cerillas" recibirá 1+2=3 puntos.
No conviene darle más puntos escribiendo sólo "caja", pues podría haber otra caja de otra cosa en la aventura, y nos cargaríamos la desambiguación. Así que sólo le subiremos la nota cuando detrás de caja hayamos escrito "de cerillas" o "de las cerillas".

Con el objeto cerillas:
parse_name [ i j j2 j3 j4;
          j=NextWord();
          if (j=='cerilla')i++;
          if (j=='cerillas'){i++; parser_action=##PluralFound;}
          if (i>0 && action_to_be~=##Take or ##Remove){
                          j2=NextWord();
                          j3=NextWord();
                          j4=NextWord();
                    if(self in caja_de_cerillas){!¿está dentro de la caja?
                          if(j2=='de'){if(j3=='la' && j4=='caja')i=i+3;
                                       if (j3=='caja')i=i+2;}
                    }!¿está dentro de la caja?
                    }
          return i;         
],


hacemos tanto de lo mismo, sólo que la palabra de vocabulario a detectar como inicio es 'cerilla' o 'cerillas'.
En cuanto detectamos la coincidencia le damos un punto y procedemos a rastrear el resto de las palabras que van detrás, de la misma forma. En este caso sólo les vamos a dar puntuación a las cerillas que además de haber sido descritas como "cerillas de la caja..." estén efectivamente dentro de la caja de cerillas.
De este modo, EX CERILLA y EX CERILLA DE LA CAJA nos dará dos descripciones distintas, tal como lo hemos programado.

¿Qué ocurriría si otorgáramos la puntución por igual a las cerillas dentro de la caja que a las de fuera?
Pues que por defecto, al examinar, el parser tomaría al azar una cerilla... pero priorizando las que están fuera de la caja (Ya hemos visto en el capítulo anterior que para el parser, ante la duda, la más salida, es decir, el objeto que no esté dentro de otro).
Por lo demás, meter y sacar cerillas funcionaría perfectamente.

Bien, vamos a compilar el código y a probar:

Puedes ver un jarrón (que está vacío) y una caja de cerillas (que está cerrada).
>x cerillas
No veo eso que dices.

Que no cunda el pánico. La caja está cerrada, y al no ser transparente no puedes ver las cerillas. De hecho no puedes saber si dentro hay cerillas, monedas o una araña famélica.

>abre caja
Abres la caja de cerillas, descubriendo ocho cerillas.

Ahora ya podemos verlas.

>x caja de cerillas
En la caja de cerillas ves ocho cerillas.

Correcto.

>x cerillas de caja
No puedes especificar objetos múltiples con ese verbo.

También es correcto, el parser ha detectado correctamente que nos referimos a las cerillas, sólo que no se pueden examinar en plural, y por eso lanza ese mensaje.

>x cerilla de caja
Una cerilla que está dentro de la caja de cerillas.

Probamos en singular, aunque esto no demuestra nada aún, ya que "cerilla de la caja" con cerilla en singular no entra en conflicto con "caja de cerillas" con cerillas en plural. Por tanto vamos a probar con una acción que a diferencia de examinar sí que admita plurales u objetos múltiples:

>sacar caja de cerillas
¡Pero si no está ahí ahora!

Primeramente hemos intentado el absurdo. El mensaje, aunque no muy claro, es correcto. Es el que lanza el parser por defecto cuando intentas sacar algo que no está metido en ningún sitio, y tal es el caso de la caja de cerillas.

>sacar cerillas de caja
cerilla: Sacada. cerilla: Sacada. cerilla: Sacada. cerilla: Sacada. cerilla: Sacada. cerilla: Sacada. cerilla: Sacada. cerilla: Sacada.

Como vemos, hemos sacado las 8 cerillas de la caja. Pero vamos a volver a meterlas para probar mejor la desambiguación:

>meter cerillas en caja
cerilla: Hecho. cerilla: Hecho. cerilla: Hecho. cerilla: Hecho. cerilla: Hecho. cerilla: Hecho. cerilla: Hecho. cerilla: Hecho.
>sacar cerillas de caja de cerillas / sacar cerillas de la caja de cerillas
cerilla: Sacada. cerilla: Sacada. cerilla: Sacada. cerilla: Sacada. cerilla: Sacada. cerilla: Sacada. cerilla: Sacada. cerilla: Sacada.

Ya, por fín, hemos enfrentado a las "cerillas de la caja" contra la "caja de cerillas" en la misma frase. Y como vemos el parser detecta correctamente a qué objeto nos referimos en cada una de las partes.

Alguien se podrá preguntar ahora... ¿Y qué pinta un jarrón ahí? Pues es para demostrar que no hay trampa ni cartón.
El jarrón es otro contenedor donde se pueden meter las cerillas. Si la caja de cerillas fuera el único contenedor presente, el parser no necesitaría detectar que nos referimos a ese objeto, y con escribir "sacar cerillas" o "meter cerillas" actuaría directamente sobre él. Por tanto no podríamos comprobar que efectivamente se está enterando de que nos referimos a ese objeto.
Es una más de esas características de Inform donde el parser se hace el listillo...

Pero aún no han terminado las comprobaciones. Vamos a volver a meter todas las cerillas en la caja y empezamos de nuevo:
>coge cerilla
Cogida.

(Aunque el mensaje por defecto -que podemos mejorar- no lo muestre, la cerilla cogida procede del interior de la caja, que es donde están todas.

>coge cerilla
Ya tienes la cerilla.

Obviamente ya tenemos la cerilla... aunque haya más dentro de la caja. Pese a que resulte pesado, vuelvo a recordar que en caso de empate Inform prioriza los objetos que no están dentro de otros, de modo que habiendo alguna cerilla fuera de la caja, ésta tendrá prioridad.
¿Qué ocurriría si hubiera más de una cerilla fuera de la caja? ¿Nos lanzaría el parser un mensaje de desambiguación para determinar a cuál de ellas nos referimos? No. Las cerillas están definidas como una clase, con las mismas propiedades de nombre (en este caso con la rutina parse_name), por tanto son equivalentes y se escoge una al azar, sin preguntar al usuario. No ocurriría así si hubiéramos definido alguna diferencia de vocabulario entre ellas, por ejemplo distintos colores. En ese caso el pasers nos preguntaría ¿A cuál te refieres, a la cerilla roja, a la cerilla azul, o a la cerilla verde?

Pero volvamos al tema:
>coge cerilla de caja / saca cerilla de caja
Sacada.

Si queremos coger las cerillas que están dentro de la caja, lo especificamos y listo.

>x cerilla
Una cerilla normal y corriente.

Ésta es la descripción de una cerilla que está fuera de la caja.

>x cerilla de caja
Una cerilla que está dentro de la caja de cerillas.

Ésta es la descripción de una cerilla que está dentro de la caja.

>coge dos cerillas de la caja
cerilla: Sacada. cerilla: Sacada.

Esto también funciona.

¿Queréis ver algo que no funcione?...
>mete en la caja de cerillas una cerilla
No entendí esa frase.

Pero esto no es un problema de desambiguación, sino de definición de gramática. Podemos ver cómo está definida la acción "meter" (siempre que hayamos compilado en modo debug) de la siguiente forma:

>xverbo mete
Verb 'coloca' 'echa' 'inserta' 'mete' 'pon'
     * multiexcept 'en' container -> Insert
     * multiexcept 'en' noun -> PutOn
     * multiexcept 'dentro' 'de' noun -> Insert
     * multiexcept 'sobre' noun -> PutOn
     * noun 'a' topic -> SetTo
     * 'a' creature 'en' container -> Insert
     * 'a' creature 'en' noun -> PutOn
     * 'a' creature 'dentro' 'de' noun -> Insert
     * 'a' creature 'sobre' noun -> PutOn
     * multiexcept 'encima' 'de' noun -> PutOn
     * 'a' creature 'encima' 'de' noun -> PutOn
     * 'cerrojo' / 'pestillo' / 'cierre' 'a' noun -> Lock
     * 'el' 'cerrojo' / 'pestillo' / 'cierre' 'a' noun -> Lock
     * 'cerrojo' / 'pestillo' / 'cierre' 'a' noun 'con' held -> Lock
     * 'el' 'cerrojo' / 'pestillo' / 'cierre' 'a' noun 'con' held -> Lock


Como vemos, no aparece por ningún sitio ninguna plantilla tal que 'en' container multiexcept.
Por tanto, si queremos que el parser entienda una orden con el complemento circunstancial por delante del complemento directo, debemos editar o ampliar esa gramática añadiendo las siguientes líneas:
* 'en' container multiexcept -> Insert reverse
* 'dentro' 'de' container multiexcept -> Insert reverse


Pero esto ya se sale del tema de la desambiguación y de la caja de cerillas.
Puedes descargar el código compilado para Glulx aquí:
http://www.caad.es/jarel/trastos/cajadecerillas.zip

No hay comentarios: