Mostrando entradas con la etiqueta PSI. Mostrar todas las entradas
Mostrando entradas con la etiqueta PSI. Mostrar todas las entradas

miércoles, 20 de octubre de 2010

Formas de conversación

1. Conversación automática.
La conversación se desarrolla siguiendo un guión al margen de lo que pretendiera decir el jugador, detonada por una acción de hablar o cualquier otra.

2. Conversación por menús.
Al iniciarse una conversación se despliegan una serie de opciones con temas entre los que el jugador puede elegir, cada uno de los cuales generará una respuesta/reacción en el interlocutor que a su vez modificará (o no) el repertorio de opciones para el siguiente turno de habla del jugador.
En los modelos más simples las opciones simplemente van desapareciendo a medida que son escogidas y respondidas, de modo que el jugador sólo está eligiendo el orden en el que quiere que le cuenten las cuatro cosas que tiene que decir el PSI. No habría mucha diferencia entre esto y mostrar el diálogo entero de una tacada, a modo de conversación automática.
Los modelos más elaborados se asemejarían a un librojuego, donde la elección de una opción u otra abre y cierra caminos futuros.

3. Conversación por detección  de palabras clave.
Este es el sistema más conversacional, y quizá el más usado. En teoría es el más difícil pues supone un parser dentro del parser, y una aproximación a la programación de inteligencias artificiales; pero en la práctica nadie llega hasta tal punto y el programador se conforma con preveer un pequeño lexicón con lo importante.
Sus formas suelen ser del tipo DECIR A PERSONAJE "MENSAJE" o PERSONAJE, MENSAJE
Puede subdividirse en:
3.1. Reconocimiento de palabras clave estricto: Se buscará una o varias cadenas de texto, produciéndose error (mensaje tipo "no entiendo lo que dices") caso de que el jugador haya escrito algo que el parser no entienda. Da lugar a conversaciones muy pobres y básicas, del tipo:
DI A PERSONAJE "HOLA"
PREGUNTA A PERSONAJE POR LLAVE

3.2. Reconocimiento de palabras clave no estricto: Se buscará una o varias cadenas de texto, pudiendo generarse múltiples respuestas en función de las combinaciones de cadenas encontradas. El texto no reconocido se ignora. Mal visto tiene un gran margen de error, bien visto tiene un gran beneficio de la duda.
Nos permite diferenciar por ejemplo "Dónde está la espada" de "dame la espada" buscando las palabras que deben de aparecer fijo:
"donde" "espada" "dame" "entregame" "quiero"...
e ignorando las menos seguras y prescindibles, como:
"está", "(se) encuentra","(se) halla"...

3.3. Reconocimiento gramatical: es sencillo para órdenes (imperativas) simples usando el mismo motor que el analizador sintáctico del parser (que a fin de cuentas es lo que hace: reconocer un microlenguaje de órdenes imperativas VERBO+CD/CI/CC). Para otro tipo de enunciados sería más práctico recurrir al reconocimiento de palabras clave múltiples no estricto.

4. Conversación no lingüistica.
Acciones que provocan reacciones en los PSIS, como si fueran órdenes habladas. Mostrar objetos para obtener información sobre ellos, señalar una puerta, comunicarte por pitidos, golpes, claves...


Las conversaciones no dirigidas, es decir, aquellas en las que se detectan palabras clave, tendrán numerosas lagunas ante la inviabilidad de programar una inteligencia artificial que lo entienda todo. Podemos intentar disminuir, justificar o disimular el exceso de frases del tipo "No entiendo lo que dices" de diversas formas:

1. El PSI activo (no pasivo).
Si es el jugador quien tiene que abordar al PSI, quedarán más patentes sus defectos y sus fallas de programación, pero si es el PSI el que toma la iniciativa y dirige/orienta al jugador en todo momento sobre lo que se puede esperar de él, qué se le puede preguntar, qué le interesa... habrá menos margen de "Filomeno no parece haber entendido lo que dices".
Se trata de que el PSI hable al jugador sin necesidad de que éste le pregunte primero, y realice actividades por su cuenta en lugar de permanecer en una localidad a la espera.

2. El PSI sueco.
La lengua nativa del PSI es otra, y sus conocimientos sobre la lengua del jugador son escasos. Esto justificará que no reconozca la mayor parte de las cosas que se le digan.
El PSI también puede ser medio sordo o existir cualquier factor distorsionador que sea la causa del mal entendimiento.
El caso extremo es que el PSI no entienda abosultamente nada y por tanto responda inútilmente en su desconocido idioma. En este caso tan sólo se obtendrán reacciones útiles mediante comunicación no lingüística, al pronunciar nombres propios reconocibles al margen de la lengua (por ejemplo el nombre de alguien o de un lugar), o al hablar con el limitado léxico que el jugador aprenda del idioma de los PSIS.

3. El PSI autista.
No es más que sustituir las respuestas de error que informan que el PSI no nos ha entendido por otro tipo de respuestas que no digan expresamente que lo que hemos dicho no va a misa. "Filomeno espera a que acabes de hablar, y te da la espalda para asomarse por la ventana", "Filomeno medita unos segundos tras escucharte, y acto seguido vuelve a sus quehaceres.", "Por el gesto de aburrimiento, adivinas que a Filomeno no le ha interesado mucho lo que acabas de decir.". Da igual que las reacciones no concuerden con lo que se esperaría, el PSI te ha escuchado pero no has dicho nada que le interese o le motive a tener una reacción más propia.
Otra técnica en la misma línea son las respuestas evasivas, muy utilizadas en los programas de conversación con una IA, con frases como: "Muy interesante", "Cuénteme más sobre eso de 'quiero la llave'", "Estoy de acuerdo con lo que acaba de decir", "¡Eso es evidente!", "Cuando usted dice cosas como 'ayudame a abrir la puerta' yo cierro los ojos y escucho el mar"...

Enlaces:

lunes, 6 de abril de 2009

La Venganza de Yan

Estos días estoy programando una historia que se me ocurrió el jueves. La programación va a buen ritmo, sólo que me falta por planear las secuencias finales (que no el final). Realmente ha ido saliendo todo improvisado a partir de la idea general.
La ambientación está inspirada en un antiguo largometraje animado chino llamado "Taro el niño Dragón (Tatsunoko Taro)"... aunque de la inspiración a la expiración hay un trecho ;D

Por fín voy a implementar para algo más que para juegos de palabras lo que aprendí del código de "Ad Verbum": para conversaciones con PSIS y como apoyo a otros objetos del juego, para poder especificar detalles sin necesidad de crear nuevos objetos ligados.

Se trata de un parseado en paralelo, que no suplanta sino que complementa, un parseado de reconocimiento de cadenas o de trozos de texto.
Inform funciona igual que siempre, sólo que cuando quiero le consulto al otro parseado qué ha encontrado, y así puedo dar mejores respuestas a las cosas que me interesen.

He dispuesto 5 variables que recolectan los valores coincidentes del parseado en paralelo, y son estas variables las que se consultan luego desde el código de los objetos y las funciones.

Es un sistema a la vieja usanza. Te tienes que haces una tabla y apuntar qué número está representando a qué concepto (uso "concepto" para describir un grupo de palabras o cadenas que vienen a ser sinónimas en el contexto del juego). Y a la vez escoger bien cómo haces las listas, en qué orden pones las cadenas en el código y cuál de las 5 variables será la encargada de almacenar cada tipo de concepto. En principio una variable cazará conceptos referidos a pronombres, otra cazará partículas interrogativas, otra acciones, otra nombres y otra adjetivos, en principio, porque en la práctica se mezcla todo un poco para aprovechar donde no se necesita y no se prevee que haya conflicto.

La detección de cadenas se realiza por barrido secuencial, no por el orden en que ha sido escrito, sino por el orden en el que cada cadena está escrita en el código. Cada variable adquiere el valor del último concepto coincidente de su lista personal. De modo que si dos cadenas vitales están en la misma lista, la variable sólo almacenará la que esté más abajo en el código.

Así he empezado a programar y ya es tarde para cambiarlo, pero explicaré un sistema mejor:
En lugar de unas pocas variables para muchos conceptos...
Una variable para cada concepto, y claro, no usaremos variables sino Flags (banderas), que apenas usan memoria. Puedes crear 5000 flag y el parser ni pestañea.

De esta forma, antes de cada input ponemos todos los Flags encargados de pescar conceptos en false, y procedemos a hacer el barrido.

En nuestro código del PSI, tendremos cosas como éstas:
react_before[;
hablar:
print "Pepito dice:^";
if(FlagOn(100) && FlagOn(156)) "Me llamo Jhames.";
if(FlagOn(101) && FlagOn(157)) "El pescador se llama Josema.";
if(FlagOn(111) && FlagOn(157)) "¡¿Cómo que quieres matar al pescador?!.";
],


Como decía, hay que tener la chuleta al lado, y veremos las cadenas que activan cada flag:
Flag 100: llama*, nombre
Flag 111: mata*, asesina*, liquid*
Flag 156: tu, usted, te
Flag 157: pesc*d*r



(*) Como esto va por fragmentos, los asteriscos los he puesto a modo representativo para que se vean las partes que se pueden omitir sin problemas. Para palabras largas, para preveer varias flexiones... simplemente detectas las letras de la palabra que son fijas y que son imprescindibles para distinguirla de otra similar.
También se puede fijar la longitud.
Podemos detectar de un plumazo "amigo" y "amiga", programando la detección de "a"+"m"+"i"+"g+"+longitud=5.
Aunque para este ejemplo, lo dejaríamos en "a"+"m"+"i"+"g". Ya que la secuencia "amig" no presenta demasiado riesgo de confundirsem (o estar fundida dentro) con otra palabra. Sin determinar la longitud, el Flag saltaría a la que el jugador escribiera "amigo", "amiga", "amigos", "amigas", "amigable", "amigablemente"... etc

Y así funciona. Que el parser detecte que quieres que "Fulano te acompañe hasta el rio cantando una canción, con las manos en la cabeza y dando saltitos" es posible técnicamente, aunque aviso que no pienso implementar chorradas de ese estilo XD.

La máxima utilidad es distinguir intenciones y preguntas respecto a sujetos.
Puedes decir "Fulano"... ¿Fulano qué?
Dónde vive Fulano.
Quién es Fulano el de la derecha.
Quién es Fulano el de la izquierda.
...
La espada de Fulano... ¿qué pasa con la espada de fulano?
Dame la espada de Fulano
Encuentra la espada de Fulano
Fulano no tiene espada
...
Quiero matar a Fulano.

Hoygan nesesito un krak para Fulano es urjente.

Fuera de las conversaciones, esto sigue siendo muy útil. Por ejemplo, para un combate:
Tenemos un objeto "troll" y queremos poder elegir a dónde le atizamos con la espada.
Normalmente habría que crear un objeto "mano", un objeto "brazo", un objeto "pestaña"...

Con este sistema simplemente creamos el objeto "troll", así:
object Troll "Troll" limbo
with name 'troll' 'planseldon',
adjectives 'mano' 'manos' 'brazo' 'brazos' 'pierna' 'piernas' 'derecha' 'izquierda' 'pestaña',
before[;
Attack: print "Golpeas al troll en ";
if(FlagOn (50))"una mano.";
if(FlagOn (51))"un brazo.";
if(FlagOn (52))"una pierna";
!etc
"una parte al azar de su cuerpo.";

],
has animate scenery talkable;


Nuevamente tendríamos que mirar la lista para ver qué activa cada flag. Pero en este caso, del propio código se deduce:
Flag 50: mano*, dedo*
Flag 51: brazo*, codo*
Flag 52: pie/, pies/, pierna*
!etc

Resúmen de lo que hemos hecho:
  1. Un único objeto
  2. Introducimos los nombres de todos los blancos o subelementos en la propiedad 'adjectives' para que el parser trague con ellos (recordemos cómo funciona Inform, el parseado en paralelo "detectacascajos" actúa de apoyo, pero no sustituye) (*** ver NOTA1 al final)
  3. En el código del objeto consultamos las pesquisas que ha realizado el parseado en paralelo de detección de cadenas, a ver si "ha pescado algo". De ser así, podemos programar fácimente una respuesta personalizada, y si no, dejamos que salte la respuesta estandar.

*** NOTA1:
El código del Troll arriba expuesto sólo es viable-razonable en un modo de parseado tal que la propiedad adjectives no tenga la misma jerarquía que la propiedad 'name' sino que sea dependiente. ('adjectives' sólo se consulta si antes ha habido coincidencia en la propiedad 'name')
En caso contrario, el código expuesto es inviable, pues estaría abierto a disparates tales como escribir "ex pestaña" y recibir la descripción del troll".

***NOTA2:
El código para realizar este parseado "bruto" tipo PAWS en paralelo está expuesto AQUÍ, aunque en la versión "poco óptima", usando unas pocas variables en lugar de millones y millones de Flags.

jueves, 30 de octubre de 2008

Un sistema de conversación decente

De rebote, gracias al código de Ad Verbum, al fín he encontrado -casi, pero me vale- lo que estaba buscando de una forma más sencilla a como lo tenía planteado meses atrás: una forma de leer muchos trocitos dentro de una cadena de texto, y con todos los "objetos-texto reconocido" que quiera. Y lo mejor de todo es que esos trocitos ni siquiera son objetos, luego no interfieren con nada, sólo pueden interferir entre ellos, y aquí entra la buena estrategia para diseñar las raíces de vocabulario de conversación.

Para empezar unifico todos los dispares y poco útiles -a mi juicio- modos de comunicarse que hay en inform:

Extend only 'grita' replace
* -> chillar
* noun -> chillar
* 'a//' creature -> chillar
* 'a//' creature topic -> hablar
* topic -> chillar;
verb 'chilla' = 'grita';

extend 'habla' replace
* ->hablar
* noun->hablar
* topic ->hablar;

extend 'pregunta' replace
* ->hablar
* noun->hablar
* topic ->hablar;

extend 'dile' replace
* ->hablar
* noun->hablar
* topic ->hablar;

extend 'pide' replace
* ->hablar
* noun->hablar
* topic ->hablar;

[hablarSub;
"Hablas, pero nadie te responde.";
];

[comohablarsub;
"Para hablar utiliza la fórmula: DECIR TAL";
];


A continuación viene el detector de cadenas, con la pequeña pega de que hay que escribir los caracteres uno a uno, y que no puedes detectar concatenación con espacios... salvo enrevesando la fórmula con más loops.

[BeforeParsing i j thisword thislength x;
bla1=0; !marca de texto detectado
bla2=0; !marca de texto detectado
bla3=0; !marca de texto detectado
bla4=0; !marca de texto detectado
for (i=parse-->0,j=1:j<=i:j++)
{
thisword = WordAddress(j);
thislength = WordLength(j);
if ( thisword -> 0 >= 'a' && thisword -> 0 <= 'z' ){ if(hablador in location){ if(thisword->0=='v' && thisword->1=='e'
&& thisword->2=='t' && thisword->3=='e') bla1=10;

if(thisword->0=='r' && thisword->1=='i'
&& thisword->1=='o') bla2=1;

if(thisword->0=='t' && thisword->1=='o' && thisword->2=='r'
&& thisword->3=='r' && thisword->4=='e') bla2=2;

!...etc


Y por fín, el personaje hablador. El nuevo sistema de hablar se detectará en el reaccionar_antes (react_before) del personaje, ya que de momento no necesito múltiples interlocutores, y todo lo que hables te lo replicará el único PSI presente, que traga con todo, hasta con su propio nombre si lo pones. Y de paso, me fusilo el Order y el Answer, que también forman parte del club de los modos de hablar múltiples:

object hablador "PSI hablador"
with name 'psi',
react_before[;

answer, order: <
ComoHablar>;rtrue;

hablar:
if (bla1==10)print "Has dicho vete";
if(bla2==1)print "Has dicho río";
if(bla2==2)print "Has dicho torre, torreón, o torrecita";
],
!...etc



Por ahora, mucho mejor que lo que tenía, y... hecho y funcional.
(El código es para compilar en Glulx, para Z-code cambian ciertas cosas)

martes, 12 de agosto de 2008

IA en el juego de mesa-estrategia

Técnicas de estrategia.
Por ahora la IA de los emperadores rivales manejados por el ordenador funciona así, en base a un sistema de puntuación:
1. Valorar movimiento de tropas.
-Busca territorios propios
-Busca territorios accesibles desde los propios
-Comprueba que hay tropas que mover
*Valora el número de tropas enemigas en esos territorios.
-Comprueba que nuestras tropas son suficientes para ganar. ++ o --
*Valora riqueza de ese territorio. ++
*Valora si es capital de otro reino. ++
-Si no hay tropas para mover desde el territorio que da acceso al movimiento de máxima puntuación, valorar un posible paso de tropas desde otro territorio colindante, para en el siguiente turno...

2. Valorar lo expuesta que está nuestra capital
-Busca el territorio donde está nuestra capital
-Busca territorios accesibles desde/hacia
-Comprueba si son nuestros o enemigos
-Si son enemigos valora el número de tropas y nuestra fuerza defensiva.
-Si la capital está en peligro:
A-Busca territorios colindantes con aquel que supone la mayor amenaza
-Busca tropas propias en esos territorios
-Valora si alguna de esas tropas es suficiente para acabar con la amenaza
->Ataca
B-Comprueba cuantas tropas podemos reclutar en la capital
-Valora si son suficientes para resistir el posible ataque.
-> Arma las tropas necesarias en la capital
C-Busca territorios colindantes con nuestra capital
-Busca tropas propias en ellos
-Valora si con su regreso a la capital, ésta estará suficientemente defendida
-> Mueve tropas hacia la capital
(no valora la posibilidad de múltiples amenazas sino que busca la mayor de entre ellas; en el caso de múltiples, las dos últimas opciones, B y C, serían preferibles a la primera, A)

3. Valora reclutamientos.

De momento lo he puesto a funcionar saltando el turno del jugador para ver a toda velocidad el transcurso de la guerra, y la cosa funciona poco inteligentemente; se crean circuitos repetitivos que tardan mucho en deshacerse. Lo cual es normal, si todos los emperadores tienen el mismo criterio de ataque y defensa, parece que juegan al gato y al ratón mordiéndose las colas, y sólo tras mucho tiempo alguna capital es tomada y cambian los ciclos por unos momentos para volver a hacerse regulares.

Hay que ampliar la IA con variaciones personalizadas para valoraciones secundarias. Por ejemplo, entre varios posibles territorios de avance, tender a elegir el más rico, el que posea más enemigos, el que pertenezca al emperador que más nos ha atacado...

También con objetivos a largo plazo: La estrategia principal del juego será tomar las capitales, que son el punto flaco de los demás imperios, pero también conquistar pro el camino el mayor número de territorios para obtener más rentas y más soldados. Por tanto, buscar recorridos óptimos hacia las capitales más cercanas y débiles pasando de camino por los territorios más fáciles de conquistar.

Y con autocorreciones: si tras varios turnos, el promedio de territorios, riqueza y tropas sale negativo en comparación con la media de los demás emperadores, algo está haciendo mal, luego debe cambiar sus criterios de valoración secundarios (los que no tienen que ver con la defensa de la capital, que son prioritarios)

sábado, 26 de abril de 2008

sistema de conversación por adivinación

Para mi próxima aventura, he determinado utilizar como sistema de conversación aquel que ya esbocé hace años y he ido ultimando gracias a los avances de la tecnología. No es otra cosa que el
Sistema de adivinación pulsométrico.

Las sinapsis neuronales del jugador generan unas imperceptibles interferencias con los campos energéticos del ordenador. Con el algoritmo adecuado, desarrollado gracias a una red neuronal bien entrenada para interpretar esas interferencias y traducirlas, podemos adivinar las intenciones del jugador.

De modo que la conversación se reduce al comando "HABLAR CON", sin necesidad de especificar más.
...
Lamentablemente no es así... quiero decir afortunadamente. No creo que a nadie le hiciera gracia que apareciera una tecnología capaz de leerle el pensamiento (lo que no quita que nos gustase leer el de los demás, en exclusiva).

miércoles, 2 de abril de 2008

PSIS en mallas

Tengo dos proyectos abiertos donde juego con el movimiento masivo de PSIS. Cada uno tiene una peculiaridad distinta.

El proyecto A, para la Xcomp seguramente, es una malla bastante regular, con un cuello de botella cuyo paso se regula un poco a lo bruto, redirigiendo a los PSIS cuando su meta requiere atravesarlo.
Los límites de la malla se detectan de la misma forma bruta: a mano.
Los PSIS se mueven por objetivos. Una vez definidos los límites del mapa y la redirección por el cuello de botella, basta con asignar a cada PSI un valor en su propiedades objetivox y objetivoy, y éste realizará el trayecto automáticamente.
Cuando se cumple un objetivo se reasigna otro, y así mismo existen acontecimientos masivos que afectan a bastantes PSIS: relevos de guardias, reuniones, accesos a edificios...

Los PSIS están contínuamente moviéndose por la malla de la ciudad, deteniéndose de vez en cuando unos turnos, con bastante autonomía.

(Pulsa aquí para ver el movimiento en vídeo)

Los PSIS cuentan con un generador pseudoaleatorio de rostros (con varios tipos de ojos, cejas, narices, rostros, pelos, complexiones, distancias entre los rasgos...) y nombres.
Antes de comenzar cada partida se define aleatoriamente una "semilla del kaos", y en base a ese valor se determinarán las cosas que son diferentes en cada partida, pero que permanecerán constantes en el transcurso de la misma. Incluso el jugador será diferente: su rostro, su nombre y su género.


En el proyecto B los movimientos son más burdos, se basan en detectar como objetivo al jugador (cosa factible también en el proyecto A, aunque no vaya a cuento con la historia), olfateándole para converger hasta él y atacarle. El típico movimiento del Minotauro en su laberinto.
Su punto fuerte es la automatización de los límites. En este caso la malla no es regular, sino que es un laberinto de libre diseño.
Usando la librería Newflags.h, reservo 10x10=100 flags para crear una máscara con la forma del minilaberinto: 0=vacío, 1=sólido.
De este modo, tanto en los movimientos del jugador como en los de los PSIS se puede saber automáticamente las salidas disponibles en cada casilla, hacia dónde se puede pasar y hacia dónde hay pared.

Cada PSI es una unidad-horda de enemigos compuesta por un número de ellos. Debido a la anchura de los túneles del laberinto, no cabe más de una horda en cada casilla, o en cada frente (cuatro frentes posibles) desde el que se aborde al jugador, por lo que unas hordas así mismo hacen de obstáculo a otras hordas, que aguardarán detrás para atacar. No obstante, según liquidemos enemigos y a medida que quede espacio, se producirán transvases de indivíduos de las hordas que esperan detrás hacia las hordas en primera fila.
Aunque tengo que reconocer que desde el punto de vista conversacional este proyecto es un coñazo de jugar, por lo que está orientado a una parodia.
Los mapas son para testeo, no salen en la perspectiva del juego. Aunque en el primer proyecto incluiré un acceso al mapa, porque merece la pena poder observar el deambular de los ciudadanos.

martes, 5 de febrero de 2008

conversaciones con PSIs

Intento sacar un buen sistema de conversación con PSIs, para lo cual el sistema de lectura de objetos de Inform me resulta fatal. Lo suyo sería implementar un modo de parseado alternativo dentro del propio parser, un sistema PAWSlike de reconocimiento de cadenas de texto, de chorizos de texto no acotados necesariamente por espacios ni por palabras completas.

Mi antiguo parser en C, DISAC funcionaba así:
Tenía 4 tablas de vocabulario: Verbos, Nombres, Objetos y Adjetivos, teniendo Verbos la obligación de ser la primera cadena de la orden, y Objetos estando en principio reservada para objetos cogibles.
El parser buscaba coincidencias letra a letra sin importarle los caracteres que sobraran: signos de puntuación, espacios, sufijos, declinaciones, palabras extra... Cuando los espacios fueran importantes, sobre todo en palabras cortas que pudieran estar contenidas en otras, el espacio iba en la propia definición de vocabulario de esa palabra, con un cuadradito, tal que '#asi#'
De modo que era vocabulario válido cosas como:
'bebe', 'di#', 'por#que', 'te#llama', 'destruy', 'llave#roja', '#con#', '#en#'...

No hacía falta ni introducir las palabras completas ni una a una: las "palabras" eran a veces cachos o trozos de frase.

A la hora de buscar coincidencias, se jugaba con cuatro posibles valores de cada tabla:

1- Vocabulario concreto encontrado.
2- Indiferente de si ha habido coincidencia.
3- No debe haber habido coincidencia.
4- Debe haber habido alguna palabra de esa lista detectada, da igual la que sea.

De cara a las conversaciones, esto permitía jugar con términos y trozos de frase por los que existiera una alta probabilidad de que el jugador tuviera que pasar para formular una pregunta concreta.
Por ejemplo, para detectar una pregunta de ¿cómo te llamas?, la mayor parte de los casos con tuteo se podrían cazar definiendo dos cadenas:

'te#llama' -> Cómo te llamas, cómo te llaman.
'tu#nombre' -> cúal es tu nombre, dime tu nombre, quiero saber tu nombre.

Teniendo en cuenta los posibles errores:
-te llaman para cenar, no quiero saber cómo te llamas, tu nombre es muy feo.

Pero prefiero que se conceda el beneficio de la duda.


Volvamos a Inform, aquí existe la librería Etemas que permite jugar con UN objeto con tres modos distintos de parsing:
-parsing estricto: Deben aparecer todas las palabras de vocabulario del objeto-tema
-parsing normal: Pueden aparecer algunas, aunque falla si metes palabras no reconocidas delante.
-parsing flexible: Con que aparezca una vale, las palabras desconocidas se ignoran.

El problema es que las palabras deben estar enteras y no contener espacios, cuando ya he explicado por qué creo más útil un vocabulario definido a trozos e incluso incompleto: 'por#que' 'donde#est' ...

Descartando una inteligencia artificial capaz de reconocer las gramáticas, cosa que hace el parser pero en un argot limitado a órdenes, veo más viable basarse en trozos. Como cuando estamos rodeados de ruido y tan sólo escuchamos fragmentos de las palabras de nuestro interlocutor, y a partir de trozos clave -y del contexto- tratamos de recomponer el mensaje completo.

...

He hecho un apaño en Etemas para poder jugar con cinco listas de vocabulario independientes por objeto, y barajo incluir más de un objeto en el parsing para poder tener 5+5+5... de forma independiente.
Dos o tres sectores de vocabulario: uno para órdenes y encabezados de preguntas (averiguar si el jugador quiere saber dónde, cuándo, por qué, o si ordena que se vaya, que se quede, que vaya con él, que le dé algo); y el otro para saber el sitio, el objeto... etc.
El orden en el que se escriban dará igual, los diversos objetos se sacarán del mismo texto barriéndolo repetidamente, será lo mismo escribir: di a pepe dame la manzana, que di a pepe la manzana dame

Para eso, se añade a Etemas un cuarto modo de parsing: parsing_multiple, basado en el parsing_flexible:

[ FlexIntnombreMultiple obj u i w n;
for ( i = 0 : i < consult_words : i++ ) {
w=NextWordStopped();
if( u== 1 )if (WordInProperty (w, obj, name)) n++;
if( u== 2 )if (WordInProperty (w, obj, name_f)) n++;
if( u== 3 )if (WordInProperty (w, obj, name_mp)) n++;
if( u== 4 )if (WordInProperty (w, obj, name_fp)) n++;
if( u== 5 )if (WordInProperty (w, obj, adjectives)) n++; }
return n; ];


Ésta función será llamada con dos parámetros, el objeto obj, y un valor 'u' que indica qué lista de vocabulario de ese objeto se consultará. En una primera fase se consultarán todas y se sumarán todos los valores para descubrir el objeto-tema ganador... excepto la lista de adjetivos, cuya puntuación no será tenida en cuenta, y si aparece en la función es porque sí que será necesaria para la segunda fase. Se puede ver en AveriguarTema cómo se realiza la suma disgregada como si se tratara de un objeto-tema normal con una sola lista name.

[ AveriguarTema T i n k max tmax nn;
max=0; ! La mejor puntuación de momento
tmax=0; ! El objeto correspondiente a esta puntuación
k=wn;
objectloop (i in T) {

!...etc

if (i has parsing_multiple){
wn=consult_from; nn=0;n=0;
nn=FlexIntnombreMultiple (i,1);
n=n+nn;wn=consult_from;
nn=FlexIntnombreMultiple (i,2);
n=n+nn;wn=consult_from;
nn=FlexIntnombreMultiple (i,3);
n=n+nn;wn=consult_from;
nn=FlexIntnombreMultiple (i,4);
n=n+nn;wn=consult_from;
! nn=FlexIntnombreMultiple (i,5);
! n=n+nn; !adjectives no suma de cara a la palabra ganadora
}
!...


Y ahora la fase 2, justo antes de devolver el objeto ganador, analizamos nuevamente ese objeto, pero ésta vez dejando constancia de qué listas de ese objeto contienen vocabulario escrito por el jugador y cuales no:

!... if(tmax>0){
i=1;
wn=consult_from; !name
if(FlexIntnombreMultiple(tmax,1)>0)i=i*2;
wn=consult_from; !name_f
if(FlexIntnombreMultiple(tmax,2)>0)i=i*3;
wn=consult_from; !name_mp
if(FlexIntnombreMultiple(tmax,3)>0)i=i*5;
wn=consult_from; !name_fp
if(FlexIntnombreMultiple(tmax,4)>0)i=i*7;
wn=consult_from; !adjectives
if(FlexIntnombreMultiple(tmax,5)>0)i=i*11;
}
switch (T){
temas:temas.capciones=i;
temas2:temas2.capciones=i;
!temas3:temas3.capciones=i; !...etc
}
return(tmax); ! Y retornar el mejor hallado
! Observar que no hay intento de aclarar
! ambigüedades si dos o más coincidieran en
! puntuación
];


Usamos números primos para otorgar el valor final de la variable(propiedad) temas.capciones.
Posteriormente, podremos jugar con ese valor, por ejemplo:
Si temas.capciones vale 2, significa que sólo ha habido coincidencia en la lista de name.
Si temas.capciones vale 66, significa que ha habido coincidencias en la listas de name, name_f y adjectives.

Aquí un ejemplo de un tema, con sus valores de consulta:

xTema tSaludos "" Temas
with name 'hola' 'saludos' 'tal' 'como',
name_f 'dias' 'noches' 'tardes',
name_mp 'adios' 'voy' 'chao' 'adios' 'luego' 'vista' 'otra' 'vemos',
name_fp 'haces' 'haciendo' 'pasa' 'ocurre' 'pasado' 'ocurrido',
adjectives 'que' 'estas' 'hasta' 'buenas' 'buenos',
;
! (i==2) !hola
! (i==22) !qué tal estás
! (i==5 || i==15) !Adios
! (i==77) !¿Qué haces?
! (i==33) !Buenos días, tardes, noches


Es conveniente al menos una segunda vuelta, para detectar otro objeto de la misma forma, y poder elaborar estructuras más complejas con objetos INDEPENDIENTES. Con una sola vuelta, aunque cada objeto tenga 5 listas, éstas dependen de estar contenidas en el mismo objeto.

Ejemplo de objeto de primera vuelta:

xTema tIR "" Temas1
with name 'vete' 've' 'ponte' 'colocate' 'situate' 'subete' 'sube' 'dirigete' 'largate' 'marchate' 'metete' 'entra' 'pirate' 'sal',
name_f 'vente' 'ven' 'acompaname',
name_mp 'espera' 'esperame' 'quedate' 'aguarda',
!name_fp 'xxx',
!adjectives 'xxx',
;
!(i%2==0) !vete, ve a...
!(i%3==0) !ven, acompañame
!(i%5==0) !espera
!(i%7==0) ! sin uso aún
!(i%11==0) ! sin uso aún


Ejemplo de objeto de segunda vuelta:

xTema tsitios1 "" Temas2
with name 'rio',
name_f 'camino',
name_mp 'casa',
name_fp 'norte',
adjectives 'sur',
;
!(i%2==0) -> en algún río
!(i==6) -> en/a el camino del río
!(i==105) -> en/a el camino de la casa de pepe
!(i==35) -> en/a la casa del norte
!(i==55) -> en/a la casa del sur.
!(i%2==0) -> en algún río
!(i%35==0) -> en la casa del norte, o en el camino de la casa del norte, o al sur de la casa del norte, o en el río de la casa del norte
!...etc

Los lugares que no quepan aquí se meten en otro tema del mismo objeto Temas y listo.
Se podría crear una tercera vuelta de consulta, una cuarta... todos los objetos que se quieran a costa de velocidad de parseado, simplemente llamando repetidamente a AveriguarTemas(), con objetos contenedores de temas independientes, y previendo en el código el almacenamiento en variables diferentes del objeto ganador de cada ronda, así como sus capciones, para luego poder consultarlos en la función de conversación.

Pongamos que tenemos tres objetos contenedores de temas:
Temas1, Temas2, Temas3
Cuyos objetos ganadores se almacenan en las variable: To1, To2 y To3
Y las capciones en: Cap1, Cap2, Cap3
Podríamos tener un código de conversación tal que:

[xxxhablarSub tema i;
print "", (name)noun, " ";
print "responde: ";
to1=AveriguarTema(Temas1);
to2=AveriguarTema(Temas2);
to3=AveriguarTema(Temas3);

if(to1==tIR){!a
if(to2==tsitios1){!a1
!entiende "espera" y "casa" y "norte"
if(cap1%5==0 && cap2==35)"Me estás diciendo que espere en la casa del norte.";
}!a1
!entiende "ve" y ningún sitio de la lista, luego suponemos un ¡márchate!
if(cap1%2==0 && to2==0)"Me estás diciendo que me vaya.";
}!a
if(to2==tsitios1){ if (cap2%3==0)"Quieres saber algo referente un camino ¿eh?";}
!...etc

Otra corrección es unificar el sistema de conversación, que en Inform es disparatado, estando dividido en decir, preguntar sobre, ordenar, pedir...

Mi sistema ideal es: PERSONAJE, MENSAJE
aunque de momento lo he unificado como: DI A PERSONAJE MENSAJE