Planeta Aventurero

Distribuir contenido
Planeta aventurero de CAAD (en pruebas)
Actualizado: hace 16 horas 46 mins

Remake de El Genio

Hace 16 horas 46 mins

Ayer lo empecé, y acabo de terminarlo: el remake de El Genio. Aquella aventura de 1999, tan difícil debido sobre todo a la cutrez de la programación.
Ahora los eventos de tiempo funcionan como debieron hacerlo, y además he incluído sonidos.

El Genio 2011

Categorías: Planeta

Tabla periódica de los elementos aventureros

Hace 16 horas 46 mins
Pincha sobre la imagen para verla más grande

Categorías: Planeta

¿Qué hace la gente en las aventuras?

Hace 16 horas 46 mins

Cuando programaba mis aventuras en DISAC (un parser propio en C), incluí un contador interno de forma que conjuntamente con la posición se guardara una estadística de uso de cada acción, así como el número de veces que el jugador habia probado un verbo no contemplado... lo que vendrían a ser los "No entiendo lo que dices" o "No conozco ese verbo".
A grandes rasgos el resultado que ví, en las posiciones que pude conseguir de la gente, es que EXAMINAR era la acción estrella, y ésta conjuntamente con las acciones de movimiento (N,S,E,O) constituían más del 50% de las acciones de cualquier aventura.


Lo que por otra parte es bastante obvio ¿no? Antes de actuar hay que mirar, y después también, por si algo ha cambiado.
Desde otro punto de vista, también podríamos considerar a EXAMINAR como la acción más cansina. La abreviatura, por tanto, es fundamental, ex o x.

Categorías: Planeta

Parser para inútiles totales

Hace 16 horas 46 mins

-¡Quiero un parser para inútiles totales! en el que no haya que aprender a programar.
-Disculpe, si no me equivoco usted habla cinco idiomas (por lo menos).
-Así es.
-Manda narices que haya hecho el esfuerzo para aprender cinco lenguajes humanos con sus complejas gramáticas, sus declinaciones, sus irregularidades, su léxico infinito, sus construcciones caprichosas, sus modismos... y no esté dispuesto a hacer el pequeño esfuerzo de aprender un lenguaje de programación cuya sintaxis viene a ser ridícula en comparación con la de cualquier lenguaje humano, y cuya lógica cabe en el cerebro de una hormiga.
¿Qué tiene que decir a esto?
...
-Bueno... es que con los lenguajes humanos se puede ligar...

Categorías: Planeta

Formas de conversación

Hace 16 horas 46 mins

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:Post original sobre sistemas de conversación en el foro del CAAD

Categorías: Planeta

Saboteur

Hace 16 horas 46 mins

Ya puede considerarse terminada SABOTEUR, a falta de que decida hacer alguna ampliación.
Finalmente es una aventura 2D con interfaz conversacional, y no puede jugarse sin gráficos.

Las descripciones de las localidades no existen, salvo algunas impresiones del PJ en lugares relevantes. Las localidades se muestran gráficamente en 2D y tanto el PJ como los PNJ actúan en ellas conforme a los sucesos.
El resto de los textos usuales en una aventura conversacional sí que están incluídos.


Me ha costado horrores escoger el título, todos los que se me ocurrían me sonaban ridículos, demasiado largos, o pretenciosos, de modo que por pura desesperación he optado por "Saboteur", mismamente, como los juegos que inspiraron a éste.

[Actualización 14 de Septiembre]La aventura puede descargarse AQUÍ. Ocupa 7 megas.

Categorías: Planeta

Desastre

Hace 16 horas 46 mins

Blogger acaba de incorporar hace unos días una nueva funcionalidad para revisar el SPAM en los comentarios.
Entonces, de pronto aparecieron todos los comentarios del blog y me dió por pensar, así a las buenas, que sólo se trataba de una lista de revisión. Seleccioné todo y borré, pues no había nada que revisar.
Por hacer las cosas rápido y sin pensar resulta que se han borrado no sólo de la lista sino del blog.

Me he cargado sin pretenderlo los que cabían en la primera página de la lista de revisión, que son unos cuantos. Lo siento...

Categorías: Planeta

Los saboteurs

Hace 16 horas 46 mins

En los 80 estaban de moda los karatekas y los ninjas, y de esto trataba la saga Saboteur.
En la primera parte, que no jugué, pero pude ver en casa de un chaval con un CPC monocromo tomábamos el papel de un ninja. En la segunda parte, que sí que tuve en mi CPC, encarnábamos a la hermana del anteriormente citado que debía ¿vengarse? infiltrándose en un complejo de unas 500 pantallas para desactivar el lanzamiento de un misil.
Los movimientos eran un petardo, propio de los juegos de la época, pero aun así la acción era trepidante, echándole imaginación y, en el caso de la segunda parte, gracias a la banda sonora, compuesta por Rob Hubbard (aunque mola más esta versión mejorada made in Spain).



El caso es que pretendo hacer algo parecido pero en plan conversacional.
Un ninja (o agente de negro) que se infiltra en una base enemiga compuesta por localidades bidimensionales, aunque en este caso con la posibilidad de traspasar puertas hacia terceras dimensiones.

No habrá animaciones, pero sí representaciones del personaje en las distintas acciones y posiciones.
Así, cada parte del decorado tiene sus coordenadas, y los objetos que dejamos también memorizan el lugar donde quedaron.
Al igual que en Randolph Dwight, el personaje se desplazará (aparecerá gráficamente desplazado) por la pantalla hasta el lugar donde se encuentre el objeto a manipular y en la pose adecuada para dar el pego de que está realizando dicha acción.
Es una mezcla de animación por turnos y por eventos de tiempo.
Así, si escribimos "coger pistola", el personaje aparecerá agachado unos segundos en el lugar donde yace la pistola (que también aparecerá representada en el suelo, aunque sean 4 pixels) y posteriormente se reincorporará.
Si saltamos tanto de lo mismo, el personaje no va a permanecer en el aire hasta que nos dé por escribir la siguiente orden y pulsar return.

Se diferencia de Barbarian Quest en que el personaje no se mueve, sino que aparece desplazado, y en que el interfaz es 100% conversacional.
Hasta ahora sólo había estado haciendo gráficos, pero hoy he programado la interfaz con unas pocas acciones y funciona bastante bien.

Las pantallas son de 800 por 200 pixels.
Primeramente dibujé las piezas para componer escenarios, los "ingredientes", pero en lugar de dibujar las pantallas a la antigua - -, lo cual sería una tortura china y consumiría mucho tiempo de proceso, grabé varios modelos básicos de escenarios ya compuestos a los que sólo habrá que añadir algunas partes externas para personalizarlos.
Por ejemplo, a un pasillo le puedo añadir puertas, muebles, muros laterales, y configurar así la decoración y las salidas de la localidad.
Glulx no tiene las capacidades de un lenguaje de programación de videojuegos: redibujado selectivo o volcado de buffer, por tanto dibujar muchos gráficos significa un lapso que se acaba notando, más si son PNG que si son JPG. Por tanto pego de base un JPG, e intento que los añadidos sean también JPG, salvo cuando no hay tu tía y necesito las transparencias de los PNG.

Tambien estuve barajando si usar una malla definiendo el mapa cómodamente en una matriz, y por ahora lo he descartado, ya que lo que me ahorro en mapeado me lo como luego configurando decorados con numeritos.
Serán localidades donde el decorado base se define en una propiedad, y los complementos en propiedades especiales acompañadas de las coordenadas del añadido, que sirven tanto para saber dónde se dibujan, como para tener la referencia de dónde tiene que ir el personaje si esa parte añadida es interactuable.
Así:
localidad lugar1 "lugar"withel_grafico pasillo1,muroizquierdo 0,escalerabaja 340,;Sólo con esto puedo automatizar desde la clase localidad que hay salida hacia el oeste, pero no hacia el este (pues existe la propiedad muroizquierdo); y que además se puede bajar por una escalera y el personaje cuando vaya a bajar o acabe de subir por ella se dibujará en la coordenada x=340.

Las puertas las meteré desde fuera, ya que como pueden estar abiertas o cerradas, y haber más de una por localidad (les pondré distintos colores), deben constituír cada una un objeto.

En Regreso al Edén ya hice una escena aislada así, donde el personaje era representado como en un arcade 2D localizado en la pantalla en función de su posición (la escena de cruzar el puente). Ésta vez será la base de la aventura.

Aunque no haya animaciones, creo que la acción va a ser igual de trepidante. Los resultados de las órdenes son instantáneos, y no hay que esperar cansinamente a que el personaje eche a correr y desaparezca por un extremo de la pantalla.

En Saboteur 1 el ninja llega a la base en una barca, en Saboteur 2 en un ala delta; y escapan respectivamente el helicóptero y en moto.
¿cómo puedo superar tal espectacularidad ochentera?

Aquí el ninja llegará a la base escondido en el camión de reparto de Mahou y escapará en una superbicicleta con cuadro de carbono y tubeless... por un carril bici, que ahora están de moda XD (Es broma, en parte)

Actualizo:
El juego sí que tendrá algunas animaciones de desplazamiento, pero parciales. No será el movimiento completo desde un punto hasta el destino sino los primeros pasos a modo de insinuación.

No tendrá descripciones, salvo particularidades, todo habrá que verlo en la pantalla, aunque los resultados de las acciones sí se describirán en texto escuetamente.
Lo que sí que se listan son los objetos pequeños, ya que debido a su tamaño, aunque aparezcan representados, son imposibles de reconocer (por ejemplo, una moneda serían dos píxels, podría ser cualquier cosa, incluso una textura de la pared)
También, incluiré algún comando de rastreo que liste los objetos de escenario de cada localidad, que se podrá configurar como permanente, para que los jugadores invidentes no tengan que estar tecleándolo constantemente.
Aun así, el resultado sin gráficos será muy pobre. La gracia de la aventura está en el feedback visual, aunque la alimentación sea textual. Será como "leer un cómic" :P

[Actualizo]
Parece que existe desde hace tiempo un remake un tanto libre de los Saboteur con el remix de Saboteur 2 Marcel Donné como banda sonora. Ver aquí.
El resultado no me gusta mucho, usa gráficos fusilados de otros juegos y con animaciones bastante chuscas.

Categorías: Planeta

Desambiguando en Inform6 (II)

Hace 16 horas 46 mins
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

Categorías: Planeta

Desambiguando en Inform6 (I)

Hace 16 horas 46 mins

A raíz de este post de Mastodon sobre la ambigüedad, voy a escribir dos artículos sobre cómo tratarla en Inform6.
Aviso que el güeno güeno es el segundo, en éste sólo voy a marear un poco la perdiz y a hacer pruebas. Pero también explicar dos modificaciones muy útiles que hacer a la librería por defecto para facilitar la desambiguación.

Si vamos a programar con InformATE debemos descargar esta librería:
http://www.caad.es/informate/informate/IntNombre.zip

Si en cambio programamos con InfSP6, puedes descargarla desde aquí:
http://www.caad.es/jarel/trastos/IntNombre.h
O en este paquete de librerías y extensiones, donde viene incluída:
http://www.caad.es/informate/infsp/downloads/extensiones.rar

Y vamos con el código base de ejemplo, es para InfSP6, aunque al final explicaré las modificaciones no obvias que hay que hacer para InformATE.

global variable1 =0; !esta variable la usaremos más adelante
Constant Story "desambiguación";
Constant ADMITIR_COMANDO_SALIDAS;
Replace ChooseObjects;
#Include "Parser";
!! ATENCIÓN. USAR VALORES DE prioritario entre 1 y 7
[ChooseObjects obj code;
   if(code==2){
        if(obj has nombreusado){

                if(obj provides prioritario){
                      return (obj.prioritario+2);
                      }
                     
                    return 1;
                        }
            return 0;
        }
if (code<2) { if (obj has scenery || obj has static) return 2; rfalse; } !
  if (action_to_be==##Eat && obj has edible) return 3;
  if (obj hasnt scenery || obj hasnt static) return 2;
  return 1;
];

#Include "Verblib";
[ Initialise;
location=habitacion;

rtrue;
];
!###########################################
object limbo "limbo"
with
! Esto no sirve para nada, es para evitar un error si no declaramos
! al menos una vez la propiedad prioritario
prioritario 0,
;

object habitacion "habitacion"
with
description "...",

has light;

object jarradeleche "jarra con leche" habitacion
with name 'jarra',
adjectives 'leche' 'con' 'que' 'contiene',
description "Es una jarra que contiene leche",
has female transparent;

object jarradeagua "jarra con agua" habitacion
with name 'jarra',
adjectives 'agua' 'con' 'que' 'contiene',
description "Es una jarra que contiene agua",
has female transparent;

object lechedelajarra "leche de la jarra" habitacion
with name 'leche',
adjectives 'jarra' 'blanca',!prioritario 3,
description "Leche blanca, la jarra está llena de leche.",
has female;

!###########################################
! 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";

Destacar que vamos a reemplazar dos funciones de la librería estandar, Parsenoun y ChooseObjects, para crear la infraestructura que nos permita desambiguar correctamente. Aunque de momento dejaremos desactivado el reemplazo de Parsenoun y no utilizaremos aún la propiedad "prioritario", para ver cómo parsea Inform6 por defecto... sí... POR DEFECTO (soy totalmente subjetivo).

Si compilamos el código de arriba, y procedemos a examinar los tres objetos presentes, la jarra con leche, la jarra con agua y la leche que está dentro de la jarra (oculta dentro de la jarra), ocurrirá lo siguiente:

Puedes ver una jarra con leche y una jarra con agua.
>x jarra
¿Cuál concretamente, la jarra con leche o la jarra con agua?

Vamos bien, hay dos objetos con nombre jarra, y nos pide concretar como no podía ser de otra manera.

>leche
Es una jarra que contiene leche

Al responder acto seguido "leche", el parser entiende que nos referimos a la jarra de leche. Una pena que no entienda también "a la de la leche".

>x jarra de leche
Es una jarra que contiene leche

De esta forma el parser no nos pide desambiguación, ya le hemos aportado el dato de que es la jarra de leche y no la de agua.

>x leche de jarra
Es una jarra que contiene leche

Primer problema, el parser no diferencia la "jarra de leche" de la "leche de la jarra", ya que ambos objetos tienen la misma puntuación, pero elige el objeto "jarra de leche" debido a que la "leche" está dentro de la jarra (En caso de empate el parser considera más importantes los objetos que no están dentro de otro, y toma su propia decisión). 
Si moviéramos el objeto "leche" a la localidad, obtendríamos una pregunta de desambiguación, al existir empate: ¿Cuál concretamente, la jarra con leche o la leche de la jarra?

>x leche
Leche, la jarra está llena de leche.

Correcto

>x blanca
Leche blanca, la jarra está llena de leche.

Nuevo problema: el sistema de puntuación por defecto valora los adjetivos aun ante la ausencia de un nombre, dando lugar a extrañezas como ésta. O incluso cosas peores, como que al escribir "EXAMINAR CONTIENE", el parser detecte que nos referimos a alguna de las jarras:
>x contiene
¿Cuál concretamente, la jarra con leche o la jarra con agua?


Ahora vamos a modificar el código del ejemplo de arriba, descomentando las siguientes líneas:
Replace ParseNoun;
Include "IntnombreINFSP.h";
Compilamos de nuevo, y algo ha cambiado en el parseado, vamos a ver:
Puedes ver una jarra con leche y una jarra con agua.
>x blanca
No veo eso que dices.

Hemos arreglado lo de los adjetivos que querían tener demasiado protagonismo.

>x leche blanca
Leche blanca, la jarra está llena de leche.

Como vemos, los adjetivos funcionan sólo acompañados de alguno de sus nombres.

>x jarra
¿Cuál concretamente, la jarra con leche o la jarra con agua?
>agua
Es una jarra que contiene agua

La pregunta de desambiguación del parser ante objetos con nombre idéntico sigue funcionando correctamente.

>x  jarra de leche
Es una jarra que contiene leche

Y si desambiguamos nosotros directamente en la orden, nos ahorramos la pregunta del parser.

>x leche de jarra
Es una jarra que contiene leche

Seguimos con el mismo problema de antes. Ambos objetos tienen la misma puntuación, pero la leche está contenida dentro de la jarra y sale perdiendo.
En cualquier caso, el sacar la leche de la jarra no arreglaría el problema, entraríamos en un bucle de preguntas "leche de jarra" versus "jarra de leche" del que sólo saldríamos aportando una palabra que esté contenida en el campo name o adjectives de uno de los objetos y no en el otro, por ejemplo:
Con "x leche BLANCA de la jarra" ganaría la leche.
Con "x jarra QUE CONTIENE leche" ganaría la jarra.

Pero de todos modos, de la misma forma:
Con "x jarra de la leche BLANCA" seguiría ganando la leche.
Y con "x leche QUE CONTIENE la jarra" seguiría ganando la jarra.

De modo que nuestro problema sigue ahí.
Pero vamos a solucionarlo en seguida:
Para empezar descomentamos la línea que le otorga prioritario al objeto lechedelajarra:
object lechedelajarra "leche de la jarra" habitacion
with name 'leche',
adjectives 'jarra' 'blanca',prioritario 3,
description "Leche blanca, la jarra está llena de leche.",
has female;
De modo que a partir de ahora en caso de empate siempre ganará la leche sobre la jarra.
Si el objeto tiene la propiedad prioritario, al elegir el objeto ganador, en caso de empate éste recibirá unos puntos extra, por enchufe.
Según vemos en el código, en lugar de un punto por coincidencia, recibiría el valor de su propiedad prioritario más otros dos, un total de 5 puntos.
La función ChooseObjects se encarga de eso: de adjudicar puntos entre todos los objetos en base a las coincidencias de su vocabulario con lo que ha escrito el jugador para determinar cuál de ellos es el ganador.

Sólo con esto, la leche ganaría siempre con sólo cumplir que el jugador haya escrito "leche", y tampoco es lo que queremos, pues sería elegida al escribir "jarra de leche".

Vamos a plantear una regla sencilla tal que, si el jugador escribe jarra antes que leche, significará que se refiere a la jarra de leche; y si en cambio escribe leche antes que jarra, se referirá a la leche de la jarra.

Añadiremos este código detrás de la función Initialise de nuestro listado (ojo, sólo funciona para compilar en Glulx, al final incluiré las pequeñas modificaciones para que compile en Máquina-Z):

[BeforeParsing i j thisword thislength x exceso; !
    variable1=0;
    for (i=parse-->0,j=1:j<=i:j++) !ATENCIÓN; en Zcode es "parse->1", en Glulx es "parse-->0"
           !!!parse-->0 devuelve el número de palabras escritas,num_words = parse-->0;
    {!mnfo
        thisword = WordAddress(j);
        thislength = WordLength(j);
         if ( thisword -> 0 >= 'a' && thisword -> 0 <= 'z' )
            { !bucle
                  if(thisword->0=='j' && thisword->1=='a' && thisword->2=='r' && thisword->3=='r' && thisword->4=='a'){if(variable1==0)variable1=1;}
if(thisword->0=='l' && thisword->1=='e' && thisword->2=='c' && thisword->3=='h' && thisword->4=='e'){if(variable1==1)variable1=2;else variable1=3;}

            } !blucle
    }!mnfo
!Resumen:
!Si el jugador ha escrito jarra antes que leche, variable1 valdrá 2
!Si el jugador ha escrito leche antes que jarra, variable1 valdrá 3
!Si el jugador ha escrito jarra, pero no leche, variable1 valdrá 1
];
Ahora nos vamos a la función Chooseobjects, y añadimos una línea extra dejándola así:[ChooseObjects obj code;
   if(code==2){
        if(obj has nombreusado){
                if(obj==jarradeleche && variable1==2)return (20); !
                if(obj provides prioritario){
                      return (obj.prioritario+2);
                      }
                     
                    return 1;
                        }
            return 0;
        }
if (code<2) { if (obj has scenery || obj has static) return 2; rfalse; } !
  if (action_to_be==##Eat && obj has edible) return 3;
  if (obj hasnt scenery || obj hasnt static) return 2;
  return 1;
];Hemos introducido esta línea:
if(obj==jarradeleche && variable1==2)return (20);
Y lo que estamos haciendo, es decirle a la función que otorga puntuaciones que si el jugador ha escrito "jarra" y "leche", y además ha escrito "jarra" antes que "leche", que le dé 20 puntazos a la jarra ¡toma ya! ¡A ver qué otro objeto puede ganar ahora a la jarra por mucho prioritario que tenga!
No hace falta hacer lo mismo con la leche, pues recordemos que al haberle dado la propiedad prioritario, la leche ya recibirá una puntuación extra (menor, pero suficiente para ganar a la jarra) caso de que la premisa anterior no se cumpla.
A continuación compilamos y crucemos los dedos (aún no sé si funcionará):

>x jarra de leche
Es una jarra que contiene leche
>x leche de jarra
Leche blanca, la jarra está llena de leche.


¡¡¡Toma, toma y toma!!!

Por si acaso, comprobamos que la jarra no recibe los 20 puntacos cuando no especificamos que es la jarra de leche:
>x jarra
¿Cuál concretamente, la jarra con leche o la jarra con agua?

Y funciona correctamente.

A todo esto, igual alguien se está preguntando quién diablos va a utilizar "leche de la jarra" para referirse a la leche. Como esto es un ejercicio de desambiguación, vamos a imaginar que, además de la jarra con leche y la jarra con agua, existe un vaso con leche. Entonces, al escribir simplemente "leche", recibiríamos la pregunta ¿Cuál concretamente, la leche de la jarra o la leche del vaso? y de ahí que pueda ser importante que el parser diferencie la leche de la jarra de la leche del vaso, de la jarra de leche, y del vaso de leche.

Éste es el código definitivo:

global variable1 =0;
Constant Story "desambiguación";
Constant ADMITIR_COMANDO_SALIDAS;
Replace ChooseObjects;
#Include "Parser";
!! ATENCIÓN. USAR VALORES DE prioritario entre 1 y 7
[ChooseObjects obj code;
   if(code==2){
        if(obj has nombreusado){
                if(obj==jarradeleche && variable1==2)return (20); !
                if(obj provides prioritario){
                      return (obj.prioritario+2);
                      }
                     
                    return 1;
                        }
            return 0;
        }
if (code<2) { if (obj has scenery || obj has static) return 2; rfalse; } !
  if (action_to_be==##Eat && obj has edible) return 3;
  if (obj hasnt scenery || obj hasnt static) return 2;
  return 1;
];

#Include "Verblib";
[ Initialise;
location=habitacion;

rtrue;
];

[BeforeParsing i j thisword thislength x exceso; !
    variable1=0;
    for (i=parse-->0,j=1:j<=i:j++) !ATENCIÓN; en Zcode es "parse->1", en Glulx es "parse-->0"
           !!!parse-->0 devuelve el número de palabras escritas,num_words = parse-->0;
    {!mnfo
        thisword = WordAddress(j);
        thislength = WordLength(j);
         if ( thisword -> 0 >= 'a' && thisword -> 0 <= 'z' )
            { !bucle
                  if(thisword->0=='j' && thisword->1=='a' && thisword->2=='r' && thisword->3=='r' && thisword->4=='a'){if(variable1==0)variable1=1;}
if(thisword->0=='l' && thisword->1=='e' && thisword->2=='c' && thisword->3=='h' && thisword->4=='e'){if(variable1==1)variable1=2;else variable1=3;}

            } !blucle
    }!mnfo
!Resumen:
!Si el jugador ha escrito jarra antes que leche, variable1 valdrá 2
!Si el jugador ha escrito leche antes que jarra, variable1 valdrá 3
!Si el jugador ha escrito jarra, pero no leche, variable1 valdrá 1
];

!###########################################
object limbo "limbo"
with
! Esto no sirve para nada, es para evitar un error si no declaramos
! al menos una vez la propiedad prioritario
prioritario 0,
;

object habitacion "habitacion"
with
description "...",

has light;

object jarradeleche "jarra con leche" habitacion
with name 'jarra',
adjectives 'leche' 'con' 'que' 'contiene',
description "Es una jarra que contiene leche",
!prioritario 2,
has female transparent;

object jarradeagua "jarra con agua" habitacion
with name 'jarra',
adjectives 'agua' 'con' 'que' 'contiene',
description "Es una jarra que contiene agua",
!prioritario 2,
has female transparent;

object lechedelajarra "leche de la jarra" jarradeleche
with name 'leche',
adjectives 'jarra' 'blanca',
prioritario 3,
description "Leche blanca, la jarra está llena de leche.",
has female;

!###########################################
! 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";
Pero no cantemos victoria. Este método, además de ser un tanto chusco, no es el correcto para este caso, ya que en cuanto intentemos "sacar la leche de la jarra de la leche" veremos cómo se nos cae todo el tinglao.
Por tanto no es correcto para objetos que puedan aparecer combinados dentro de la misma orden.
Ya avisé que iba a marear la perdiz.

Pero sí que existe una solución que funciona y realizada de forma más limpia, que explicaré en el segundo capítulo de desambiguación, con un ejemplo que apareció en los foros del CAAD:
¡¡¡¡LA CAJA DE CERILLAS!!!!

Por último pondré una par de ejemplos donde el uso de la propiedad prioritario es bastante interesante:
Tenemos dos PSIS, uno se llama Jose, y el otro Jose Luís.
object jose "Jose"
with name 'jose',
prioritario 2,
has animate proper;

object joseluis "Jose Luís"
with name 'jose' 'luis',
has animate proper;
De esta forma, cuando se encuentren ambos presentes, al escribir "Jose" detectaremos a Jose, pues al tener prioritario, es el "enchufado" por encima de Jose Luís. Y para Jose Luís deberemos escribir "Jose Luís", o "Luís".
Por otro lado, cuando sólo esté presente Jose Luís, podremos llamarle "Jose" a secas tranquilamente.

Otro ejemplo menos rebuscado es un objeto "rama suelta" con name 'rama', y un objeto "rama del árbol" con name 'rama' 'ramas' y adjectives 'arbol', que a diferencia de la primera es una rama que aún permanece unida a su árbol de origen.
Le ponemos la propiedad prioritario a la rama suelta, y cuando estén ambas presentes, al escribir ex rama, examinaremos la rama suelta. ¿Que queremos examinar la rama que pertenece al árbol? pues escribimos ex rama del árbol.
Si la rama suelta no está presente podemos escribir ex rama y el parser detectará la del árbol, pues no hay más.
También sería interesante que la rama del árbol dispusiera en su propiedad adjectives de vocabulario como 'otra' 'otras' y 'mas', por si el jugador quiere arrancar otra rama, o arrancar más ramas.

Si no deseamos intervenir y que sea el parser el que lance la pregunta de desambiguación, entonces no usaremos la propiedad prioritario, pero nos aseguraremos de que la rama suelta tenga en adjectives 'suelta' 'cortada' o 'arrancada', para disponer de vocabulario exclusivo que la diferencie de la otra.

Habiendo parcheado el modo de parseado por defecto que daba puntuación  a los adjetivos al margen de los nombres, con la librería Intnombre.h, la propiedad adjectives se abre para usos más allá de los adjetivos. Así podemos incluir palabras variopintas en la propiedad adjectives:
object corona "corona del Rey Cucufato"with name 'corona',adjectives 'que' 'fue' 'pertenecio' 'al' 'rey' 'cucufato' 'vieja' 'antigua' 'propiedad',description "Vieja corona que perteneció al rey Cucufato.",has female;
Notas importantes:
* Para que el BeforeParsing funcione en Máquina-Z hay que cambiar esto:
for (i=parse-->0,j=1:j<=i:j++) !ATENCIÓN; en Zcode es "parse->1", en Glulx es "parse-->0"por esto otro:
for (i=parse->1,j=1:j<=i:j++) !ATENCIÓN; en Zcode es "parse->1", en Glulx es "parse-->0"
* Si estamos programando con InformATE en lugar de con InfSP6, la función Chooseobjects cambia, porque para empezar, se llama EligeObjetos, quedando así:
[ EligeObjetos obj codigo prio;   
    prio=ElegirObjetos(obj,codigo);
    if (codigo>=2)
    {
        if (obj has nombreusado){
         if (bandera_todo_vale==0) prio=prio+10;
    if(obj==jarradeleche && variable1==2)return (20); !!!!!     
    if(obj provides prioritario)prio=prio+obj.prioritario;  
    }
    if ((obj == jugador)||((obj has escenario)&&(obj notin brujula)))
      prio=prio-10;
    }   
    return prio;
];

Categorías: Planeta

Legend of Djel

Hace 16 horas 46 mins

Legend of Djel es una aventura francesa de "click & point" de 1987, donde encarnamos el papel de Djel, el brujo del aire.

La presentación del juego nos deleita con una horrible banda sonora que no distaría mucho de las primeras composiciones que hicimos cuando nos regalaron el órgano CASIO por nuestra primera comunión, acompañada de unos gritos estúpidos. Son gritos sin vehemencia ni credibilidad, bien podían haber gritado ¡repollo!, ¡bonobús!, y sonaría igual de ridículo.
Estoy hablando de la versión de Amiga, en la de PC a través de DOSBOX no suena nada, salvo pitidos.
En cualquier caso, cuando nos enfrascamos en el juego acabamos conciliándonos con la banda sonora: es ideal para ambientar este disparate.
Tras el título nos aparece el gráfico de una especie de dragón que la verdad es que no pinta nada en la historia. Doy por hecho que alguien había hecho el gráfico de un dragón y, pues vamos a meterlo.

La presentación es muy muy lenta para lo poco que aporta, y no se puede saltar pues a continuación viene la pregunta de seguridad antipiratería. Aún recuerdo que un pirata me dijo en su tiempo que por probabilidad había que seleccionar siempre el color rojo. Será potra pero en las cuatro cargas que he hecho hoy tanto en PC como en Amiga he acertado a la primera.

En la carátula del juego aparecen un par de pantallas a todo color, pero lo cierto es que en la versión de Amiga se emplearon sólamente 8 colores para los gráficos, y en la de PC tanto de lo mismo, a pesar de seleccionar VGA 256 colores. Y es que el juego cabe en un disquete. En aquella época no había JPGs ni cosas por el estilo, serían gráficos tipo TIFF, sin compresión: un gráfico con 8 colores ocupa 3 bits por pixel, uno de 16 son 4 bits por pixel. Para una pantalla de 320x200, tenemos 24 Kb contra 32 Kb, y en un disquette cabían si no recuerdo mal 1,44 Mb. El caso es que pasar a una paleta más rica disparaba el consumo de memoria.

Los preámbulos son escuetos, Azeulisse (Joseluís para los amigos), señora de los 100 reinos, nos informa que ha desaparecido su hija, y que si no nacen niños en nuestras tierras es a causa de la tristeza que ello le causa. De modo que ya sabemos...
Nada más colgar Azeulisse por la bola de cristal (el teléfono móvil de la época), aparece KAL, que nos pide todo el oro que podamos fabricar para paliar el hambre del pueblo y evitar una revuelta.

Y empieza el juego. En el típico estudio de un brujo, con una mesa llena de papeles y cachivaches, entre ellos una estatuilla y un jarrón que nos teletransportarán a los mundos de Azeulisse y Kal respectivamente, y una pantalla al fondo sobre la que la estatua de un grifo escupe planetas a los que podemos viajar.

Para los que no hayan estado nunca en el estudio de un brujo, son así.
Por la puerta del fondo accedemos al alambique, donde podemos fabricar oro alquímicamente con los ingredientes correctos, que son: murciélagos y flores de plomo.

Viajamos al primer y único planeta que aparece inicialmente en la pantalla y nos encontramos con un paraje volcánico.
La interacción consiste únicamente en mover el puntero por la pantalla y pinchar aquí y allá, para ver qué pasa. En este mundo conseguimos una cosa rara que no sé lo que es, y el poder del volcán, para lanzar bolas de fuego o algo así, lo que nos vendrá muy bien para cazar murciélagos o matar abejas.

De regreso a nuestro estudio, dos nuevos mundos han aparecido en la pantalla.
El primero de ellos tiene un puzzle bastante interesante. El mago dueño de ese mundo nos informa de que no está demasiado contento con él, y nos pide que se lo redecoremos. Entonces, pinchando sobre las diversas secciones del paisaje vamos moviendo las cosas: esta planta aquí, esta cosa rara allá.
Son cinco piezas y seis espacios, de modo que cada vez que pinchamos en una sección de paisaje ésta se mueve al hueco dejado tras el último cambio. Es bastante fácil la resolución, pues cada vez que una pieza cae en el lugar correcto el mago nos avisa con un ridículo grito (como los de la banda sonora de presentación).

"Me has dejao el planeta muy cuco, pero ahora quiero un joyita antes de decirte lo que sé"
El juego está lleno de decisiones trampa. Así, una vez ordenado el mundo, en lugar de un mago aparecerán dos magos ¿cuál elegimos? Uno nos dará la información a cambio de una joya (que no debemos entregar) y el otro a cambio de un combate.

Al regresar a nuestro estudio, en la sala del alambique aparece una horrenda mujer diciendo que es la hija de Azeulisse, presa de un encantamiento. ¿Debemos aceptarla? En verdad es una falsa hija, debemos rechazarla y luchar inexorablemente contra ella tras su rebote.

"Noble Djel, soy la hija de Azeulisse, aunque no me puedasreconocer porque me acabo de levantar y aún no me he maquillado"
Y podríamos seguir. Vas pinchando por las pantallas con el ratón para que ocurran cosas porque sí, sin demasiada lógica, y de vez en cuando tienes que tomar una decisión: ¿confías en esta criatura? ¿por cuánto oro compras esto? ¿robas este objeto o esperas a que más adelante surja otra vía para conseguirlo? ¿aceptamos cambiarle a esa criatura la Daga de Demonio por el Atlas?
Vete a saber. Igual la estás cagando...

"Estoy tan desesperado que te vendo una diadema de diamantes por diez pesetas, ¿aceptas?"
La caza de murciélagos es un auténtico ful de Estambul. Van saliendo de uno a uno y tienes que acertarles lanzando bolas de fuego. El problema es que los murciélagos se mueven por la pantalla a su aire, cambiando de trayectoria y velocidad sin previoaviso, y las bolas las lanzas desde abajo y van lentísimas, luego sólo hay que disparar en los escasos momentos en que la trayectoria de un murciélago parece predecible, y rezar para que no le dé por cambiarla.

En cambio, los combates, si seleccionamos la lucha psíquica, no están del todo mal. Luchamos jugando a un juego de tablero de estrategia, donde en cada turno movemos nuestra ficha y a la vez coloreamos una casilla. El adversario hará lo mismo. Gana el primero que bloquee al contrincante, sepultándole entre casillas coloreadas. El único problemilla es que si no mueves rápidamente se te pasa el turno, y en la versión PC, a través de DOSBOX, que no admite ratón y has de mover el puntero con los cursores, es fácil que esto ocurra varias veces.

En cuanto a jugabilidad esta aventura es una patata podrida, pero tiene el atractivo -al menos para mí- de ser rara de narices.
"Me has caído bien, y por hacer click aquí te voy a regalar un poco de oro"

Categorías: Planeta

la interfaz conversacional

Hace 16 horas 46 mins

Descubrí las aventuras conversacionales a través de un compañero del colegio que me habló de un nuevo juego de "hablar con el ordenador", lo cual despertó mi curiosidad y quizá determinó el rasgo distintivo que para mí diferenciaría este género de otros: la interfaz conversacional.

El prompt que esconde tras de sí una base de datos de léxico, más o menos amplio, tiene un potencial que no siempre se aprovecha. Por ejemplo, cuando una aventura sólo responde a acciones simples como: Norte, Sur, coger, dejar, empujar, hablar con...; y objetos básicos, el sistema conversacional está desperdiciado. No es más que una lista escueta de acciones o íconos escondida.

El potencial de la interfaz conversacional está en permitirnos/obligarnos a expresarnos más específicamente en nuestro idioma, yendo más allá de un lenguaje pobre y multifuncional.
Bien es cierto que para comunicarse de forma básica hace falta muy poco vocabulario. La mayor parte de las conversaciones se resuelven con una pequeña parte del léxico total de un idioma, limando el contexto las imprecisiones de las palabras multisignificado que comunmente usamos. Y las usamos porque por el precio de tener en la punta de la lengua una palabra de esas, nos ahorramos tener cinco más concretas y normalmente más largas. A éstas últimas más bien recurrimos cuando nos expresamos por escrito, pues no contamos necesariamente con ese gran aliado que es contexto, que convierte una palabra imprecisa y genérica en inequívoca, y cuando tenemos que rellenar una hoja o un tiempo de exposición, o quedar como cultos y formados.

Por ejemplo, la palabra "pillar" nos vale para expresar: coger, agarrar, atrapar, robar, comprar, sorprender, sostener, atropellar, reservar, tomar un vehículo o ruta, contraer una enfermedad o estado de ánimo, alcanzar, ligar, recibir algún golpe en una pelea...
A grandes rasgos podríamos sacar una consecuencia común de todos los significados: hacerse con el dominio de un ente, situación o lugar... o un moratón.

En las aventuras gráficas tenemos interfaces basadas en un idioma simplificado, en diversos grados. Desde las aventuras con unas pocas acciones: coger, examinar, accionar y usar; a otras como las de Lucasfilms con una lista más amplia.
Pero a fín de cuentas es una simplificación, una selección que se traduce en una comunicación pobre que se perdona aceptando las reglas del juego.

Así, "reventar la puerta con el pico" se traduciría en este microlenguaje como "usar pico con puerta"; "engrasar las bisagras de la puerta"  como "usar aceite con puerta"; "pintar la puerta de rojo" como "usar bote de pintura roja con puerta"; y "pintar un círculo rojo con un puntito en medio en la puerta" como... "usar bote de pintura con puerta", curiosamente exactamente igual que "pintar una equis en la puerta".

El sistema conversacional, además de la posibilidad de obligarnos a usar un lenguaje más rico, ofrece este potencial de especificidad y de ir más allá de forma inequívoca.

Por supuesto, aquí existe un conflicto entre el ideal y la práctica.
Volvemos al ejemplo de engrasar las bisagras de la puerta.
Si admitimos "echar aceite en puerta" como acción sinónima de "engrasar las bisagras", abrimos la puerta (nunca mejor dicho) al microlenguaje genérico y pobre de la aventura gráfica. En cambio, si somos estrictos-puñeteros, nuestra aventura será muy poco interactiva, se convertirá en un "adivina la palabra exacta".

Personalmente creo que lo mejor es admitirlo todo (excepto "usar"). Al menos en este ejemplo, no cabe duda de que el jugador que escribe "echar aceite en puerta" no tiene otro fin que engrasar las bisagras... ya sería raro que hubiera alguno que tuviera en mente barnizar la madera de la puerta con esa acción.
En otros casos la aceptación sería más discutible.

Categorías: Planeta

Aventuras online

Hace 16 horas 46 mins

Re-cayendo en la posibilidad de ofrecer las aventuras online, como hace Incanus en su página o Literactiva, he colocado la única que tenía en Máquina Z: "El caso del misterioso asesinato del domador de pulgas del circo de Montmartre", así como "Venenarius Verborum", que he tenido que convertir previamente eliminando toda la morralla de GLK. Y también resolver un pequeño quebradero de cabeza por exceso de anidaciones (branches), ya que por lo visto la Máquina Z tiene unas limitaciones a ese respecto inferiores a GLULX.

En la citada página de Incanus se exponen dos formas de colocar una aventura online:

  • Mediante un applet java, ZPlet, para lo que necesitas tener instalado un Java Runtime.
  • Mediante, Parchment, que ha sido la opción que he escogido, ya que mi intención era que el potencial jugador no tuviera que descargar ni instalar absolutamente nada, simplemente hacer click y empezar a jugar.
Usar Parchment es tan sencillo como añadir el siguiente enlace en tu web:
http://parchment.googlecode.com/svn/trunk/parchment.html?story=http://misitioweb.com/aventura.z5

Más fácil imposible.
Por ahora este intérprete online sólo admite aventuras compiladas para Máquina-Z, y ralentiza un poco.
Desde luego que es mucho más cómodo descargarse la aventura al ordenador y jugar con un intérprete en condiciones como Winglulxe, Frotz, o Gargoyle.

Pero pongámonos en la piel de un visitante extraño, o imaginemos que somos nosotros ese extraño. Llegamos a la página de un fulano que no conocemos de nada, y que nos dice que tenemos que descargar nosequé programa extraño para jugar a sus juegos.
Tanto por pereza como por la sospecha de que nos intenten colar un troyano, es posible que valoremos que no merece la pena . A fín de cuentas, ¿quiénes son esos tipos? ¿qué garantías ofrecen?
Incluso si ya sabemos de qué va la cosa y nos fiamos de esta gente, igual queremos probar antes el juego, a ver si nos atrae la temática.

Otra opción online es programar una aventura directamente en Kenshira, que al estar orientado a la publicación web ofrece una excelente puesta en escena.

El tema se ha comentado en este posteo del foro.

Actualizaciones:
Presi ha habilitado un redirector para Parchment dentro de CAAD, que se actualizaría en el caso de que la url de Parchment cambiase.
De modo que podemos poner el siguiente enlace para meter una aventura de Máquina-Z online en nuestro sitio web:
http://www.caad.es/jugar?aventura=http://sitioweb.com/aventura.z5

Uto acaba de crear un lanzador GLULXE para ZAG.
El lanzador, a diferencia del intérprete web, lo que hace es automatizar el proceso de descarga y ejecución de la aventura y el intérprete ZAG en una ventana JAVA dentro de nuestro ordenador, por lo que tendremos que tener instalado JAVA.
Para meter una aventura GLULXE en nuestro sitio web auto-lanzable, colocaremos el siguiente enlace:
http://www.caad.es/superglus/online/jugarglulx.php?aventura=http://sitioweb.com/aventura.blb

O mejor lo cargaremos especificando además un fichero de configuración .cfg, que básicamente contendrá esto:
WindowBorders=no
Para que no aparezcan los poco estéticos bordes entre ventanas.
Así:
http://www.caad.es/superglus/online/jugarglulx.php?aventura=http://sitioweb.com/aventura.blb&config=http://sitioweb.com/aventura.cfg

El problema es que estamos hablando de ZAG. ¡¡ZAG!! Sí, un intérprete Glulxe que no admite sonidos OGG y sólo AIFF y MOD de 4 canales, además de ofrecer una visualización horrorosa con parpadeos en algunos refrescos.
Así era en el 2005, y así es, pues no ha habido nueva versión desde entonces.
Categorías: Planeta

El nuevo blog de Melitón

Hace 16 horas 46 mins

Golpéalo!, Hit it!. Así se llama el nuevo blog de Mel, que sin abandonar del todo su Guarida, dedica este nuevo espacio a "golpear" y hablar sin pelos en la lengua. Haciendo crítica de aventuras y de tendencias.

Hace tiempo tuve un capítulo con Mel en el foro del CAAD que fue algo así como que él hizo una crítica de una aventura mía achacándole ciertas carencias, a lo que yo le respondí que no es que la aventura careciera de aquello, sino que él no lo había visto. Recuerdo que se lo tomó como si hubiera herido mi susceptibilidad, demostrando una vez más, que no se podía hacer crítica libre dentro del CAAD, e hizo mutis por el foro creando una entrada en su blog para expresar su decepción. Yo me lo tomé como que la susceptibilidad herida fue la de Mel, que no aceptaba la contracrítica o las puntualizaciones.

Pensando sobre eso, creo que metí un poco la pata. Es mejor callarse (aunque cueste) y dejar que la crítica fluya sin obstáculos, que buena falta hace. Es preferible crítica demasiado subjetiva (y quizá injusta desde el punto de vista del autor) que el "buenapintismo". (Además, releyendo, mi respuesta fue bastante pendenciera)
Es decir, ¿que alguien dice que le falta tal a la aventura? Perfecto, aunque uno crea que es incorrecto, esa persona no lo ha visto, y lo que está relatando es su experiencia ante esa aventura. Y en su experiencia personal eso no apareció.

Prefiero leer críticas de lengua larga (no necesariamente de mis aventuras, sino de las demás) que comentarios vagos y diplomáticos. Dicen mucho más, aunque sean poco científicas.
Por ejemplo, la crítica negativa que ha escrito Mel sobre "A veces", de Jenesis, da muchas más ganas de rejugarla que un comentario sospechosamente moderado.

Categorías: Planeta

Trofeo Orcoscomp

Hace 16 horas 46 mins

Esta mañana Planseldon me ha hecho entrega de mi trofeo de la Orcoscomp, por El Cristal Rojo:

Categorías: Planeta

Videos musicales con Pyhton

Hace 16 horas 46 mins

A falta de un editor de vídeo que permita mezclar animaciones sobre fondos y moverlas, me puse con pyhton-pygame a crear un vídeo musical.
Es todo parecido a crear un juego, con la diferencia de que no hay control externo.

Como ha de sincronizarse con una pista musical, tengo una serie de variables que me van diciendo en qué punto de la canción estoy, de acuerdo con la base musical en MIDI.

Así hay una variable tiempo que va corriendo hacia adelante en décimas de segundo.
Cada 16 tiempos sube un valor la variable tiempo_cuarto, que sería aproximadamente el tiempo transcurrido entre un bombo y una caja en ritmo simple,
y cada dos tiempo_cuarto sube un valor la variable tiempo_measure.

El "measure" es la unidad de medida de mi secuenciador, y podría asimilarse a hojas de partitura. Cuando termina un measure aparece otro con nuevas notas.
Normalmente relleno los measures con 4 bombos y cajas a ritmo simple, aunque este es de los pocos casos en los que el measure contiene la mitad de información (y lógicamente tocado al doble de velocidad): bombo-caja-bombo-caja, fín del measure.

Con estas variables, empiezo a lanzar las animaciones:
En la segunda mitad del octavo measure empieza a cantar, entonces:
if tiempo_measure==8 and tiempo_cuarta==2:
  Player.accion_cabeza=1
  Player.accion=2
  Player.escala=2
  Player.x=200
  Player.vx=4

La cabeza está separada del cuerpo, y tiene cuatro tipos de acciones:
0. callao
1. mueve la boca abriéndola bastante, prononciando aes, o como gritando.
2. mueve la boca con proliferación de dientes apretados, como pronunciando eses.
3. mueve la boca con proliferación de "morritos", oes o plosivas.

El cuerpo tiene 8 tipos de animaciones, depende del que seleccione se pone a bailar de una forma o de otra, o bate una espada, se pone a saltar... así sin parar hasta que le de otra orden con otro tipo de movimiento

Vx es el desplazamiento horizontal en pixels que hará el personaje cada fracción de tiempo, hasta que le mande otro valor o le pare, con Vx=0.

La escala determina el factor de escala, valga la redundancia, por el que se multiplicará el tamaño del cuerpo y la cabeza, recalculando la posición. Para hacer primeros planos.

En fín, que con la partitura MIDI de la canción, es como si le dieses un guión al protagonista de lo que tiene que hacer en cada momento... y hasta nueva orden.
Ponte a saltar... ahora mueve la boca... ahora cállate, ahora por el fondo de la cueva. Ahora colócate a la derecha y avanza hacia la izquierda levantando las piernas, ahora acércate, ahora aléjate y pon el fondo del bosque...

Con la canción sonando al lado hay que ajustar el metrónomo de Pygame para que vaya acompasado.. y aquí está el único problema.
El tiempo de python no es exacto, cada vez que cargas el programa su velocidad de ejecución varía ligeramente, sin importancia para un juego, pero un desastre cuando todo tiene que ir sincronizado. Si antes te iba más o menos bien ese metrónomo, ahora descubres que el video adelanta a la música...o que se queda atrás.
Por lo que al final habrá que reajustar el tiempo de todo el vídeo, con algún programa que te permita algo del tipo "convertir la duración de 2 minutos a 1:58:32"

Si el vídeo corriera al tiempo con la música, se podría meter un control de la boca mediante teclado, de forma que mientras canta le vas indicando al muñeco por golpes de teclado cómo ha de abrir la boca, clavándolo con la letra. Y grabando el vídeo en vivo... sería como un directo XD

La canción del vídeo es ésta, una parodia de David el gnomo, David el orco, con un orco como protagonista. Aunque para el vídeo sonará en versión revolucionada, a más velocidad y con el pitch agudo cuatro tonos subido, que queda más hilarante, sin llegar a sonar a "pitufos maquineros".

Aún no la he terminado porque me falta meterle a los orcos coristas.

 
 

Rectificación: Hoy lo he probado varias veces y va clavado con la música, de modo que no necesitaré ningún programa externo para mezclarlo. Igual tiene que ver con la librería time que le metí a última hora para dejar un lapso antes de que empezara el vídeo.

Aquí está:

Categorías: Planeta

Elige tu propia orcoaventura

Hace 16 horas 46 mins

Quizá haga algo para la orcoscomp. Estoy planeando un mix entre aventura conversacional y librojuego con combates. Gracias a Eliuk ya tengo el código funcional para lo que es el motor, formado con extractos de la librería Fhablao.h, un parche de Datoki y sustituyendo los "Quips" por flags, usando la librería NewFlags.h

Ahora se me plantea la dificultad de crear las escenas librojuego. El concepto de puzzles es completamente diferente aquí que en modo conversacional. De hecho es complicado implementar algo parecido a un puzzle con un sistema de opciones, casi estás dando la solución.
Por tanto, por un lado los textos, el relato, cobran más importancia. Por otro, cuando el jugador debe decidir, las opciones deben de tener algún sentido e interés, el cual estará fuertemente ligado al interés que despierte el propio relato.

Lo cual no ocurre en la conversacional, donde tenemos multitud de opciones invisibles, y de entre ellas muy pocas que sean realmente prácticas.

Al principio pensé en esto con la idea de que sería más rápido y sencillo hacer un librojuego que una aventura conversacional corta, pero me estoy temiendo que no va a ser así, aun con el motor listo.
Podría "fusilar" algún libro de Lobo Solitario... XD
A parte de tener que pensar todas las subtramas, esto va a ser como volver a programar por tablas, haciéndote un mapita en árbol de localidades y comunicaciones entre ellas (opciones).
Claro, que todo se puede simplificar:




En cuanto al sistema de combate, se basará en turnos alternativos de atacar-defenderse, entrando en juego la Destreza en el Combate, la Energía, la ruleta, bonificaciones o penalizaciones en función del tipo de ataque, y penalizaciones por luchar contra más de un enemigo.

La energía mide tanto la vida como el cansancio, cuanto más fuerte estés mejor peleas.
Cuando la energía llega a cero mueres, incapaz de defenderte más.
.
Atacar a más de un enemigo supone que tienes que dividir tus puntos de destreza a la hora de defenderte entre el número de amenazas.

Las tiradas de la ruleta son de 0 a 9, siendo 1 pifia (fallo seguro) y 0 (10) acierto seguro. Además, sacar un 0 atacando a un enemigo que anteriormente obtuvo pifia supone un crítico: daño x 3.

Las tácticas de combate son 4:
1. defensiva.
2. equilibrada.
3. ofensiva.
4. sucida (berserker).
La defensiva es la única que no consume energía, se basa esquivar y buscar buenas posiciones para atacar sólo ante oportunidades claras. Como compensación, en el turno siguiente tendremos una bonificación de defensa.
La sucida es la que más aumenta la probabilidad de acierto en el enemigo, a costa de que en el turno siguiente, cuando seamos atacados, sufriremos una penalización en nuestra defensa, y además consume más energía.
El resto de las opciones tienes sus bonificaciones y penalizaciones igualmente.

Lor rivales mayormente serán orcos, lo que se traduce en una destreza en el combate inferior a la del protagonista, pero una fuerza mayor. Es decir: tienen menos probabilidad de acertar, pero cuando aciertan, su daño base será mayor. En la característica de Fuerza están incluídas tanto la potencia del atacante como la del arma con la que golpea.

Todas las reglas son ambivalentes, se aplican por igual al protagonista y a los enemigos, que también decidirán individualmente la táctica de combate que más les conviene en cada turno.




Categorías: Planeta

Orcoscomp

Hace 16 horas 46 mins





OrcosComp
Fecha límite de recepción de obras 31 de Diciembre de 2009.
Categorías: Planeta

20 aniversario del CAAD

Hace 16 horas 46 mins
Categorías: Planeta

La Venganza de Yan, release 0

Hace 16 horas 46 mins


Acabo de subir al CAAD La Venganza de Yan.

Yan, ausente durante años labrándose un futuro en el ejército, regresa unos días a su isla natal.
Y en ella descubre que de la aldea sólo quedan ruinas y brasas aún calientes y humeantes.
¿Quién ha podido hacer esto?
¡Quien haya sido lo pagará!

Ocupa unos 10 megas, de los cuales la mitad son gráficos, luego sonido, y por último el código en sí, que son 500 Kb.

Para más información podéis leer el comentario de Urbatain en SPAC, tras testear la aventura, o entradas anteriores de este mismo blog para detalles técnicos.

Categorías: Planeta

(c)1998-2019 CAAD

Todos los contenidos de esta web son propiedad de CAAD. Las colaboraciones son propiedad de sus respectivos autores.