El bosque encantado [en OPL+]
Descripción del contenido de la página
Juego de aventuras de texto escrito en OPL+ para Psion 5mx.
Proyecto abandonado. Iniciado en 2010-07-25. 60% completado.
Este proyecto empezó como un pasatiempo de una tarde del verano de 2010. Iba a ser una aventura conversacional escrita en OPL+ para mi inseparable Psion 5mx. Entre otras muchas ventajas, una 5mx proporciona un entorno de desarrollo completo en la palma de la mano, ideal para programar en los autobuses, en los trenes, en los parques... Gracias a eso, en pocos días el código creció hasta tener cerca de un millar de líneas.
El reto de OPL+
OPL es un lenguaje de programación que ha ido evolucionando con las sucesivas máquinas de Psion y sus sistemas operativos. Es un lenguaje peculiar, difícil de clasificar. Es una mezcla entre BASIC estructurado y lenguaje de gestión de bases de datos. Está orientado a facilitar la creación de aplicaciones con interfaz gráfica. Es amigable, está muy bien documentado y permite crear programas «resultones» con poco esfuerzo. Y es lento porque, como Java, es seudo-compilado (o seudo-interpretado, que viene a ser lo mismo): el compilador crea un código de octetos portable que deberá ser interpretado por una máquina virtual.
OPL+ no es un lenguaje de programación distinto, es tan solo un preprocesador de OPL: traduce el código fuente a OPL y después lo envía al seudo-compilador original de OPL. En la práctica funciona como una versión muy mejorada de OPL, con nuevos comandos, nuevas funciones, nuevos operadores, macros y otras funcionalidades; e incluye un entorno de desarrollo integrado mucho más avanzado y cómodo que el que viene de serie para OPL. En su día OPL+ se vendió como un programa comercial, pero desde hace años está disponible gratuitamente (como cientos de programas para EPOC, plataforma comercialmente muerta).
Después de probar OPL+, regresar a OPL se hace duro. No obstante ambos tienen limitaciones importantes: Sus estructuras de control son escasas y poco flexibles, y el manejo de textos y matrices no es muy versátil. Esto hace que a veces sea incómodo implementar ciertas ideas, aunque nunca imposible.
Para mí no era una novedad, pues había utilizado OPL+ durante años en un proyecto de gran complejidad, Forth 5mx; pero sí era un reto interesante programar un aventura conversacional en un lenguaje tan peculiar. Además, prácticamente desde que di por terminado el desarrollo de Forth 5mx, tenía ganas de volver a programar en OPL+
El objetivo
El proyecto inicialmente consistía en hacer una versión en OPL+ de El bosque encantado, escrita por Kevin Jones en el BASIC de la computadora Commodore 64 y publicada a mediados de los 1980 en la enciclopedia Mi computer, página 1478 (Mi computer es la versión española de la enciclopedia Home Computer Course). Era la misma sencilla y rudimentaria aventura de la que había partido para El bosque encantado [en FreeBASIC] (proyecto al poco «congelado», y sin fecha prevista de «descongelación»).
Mi primera intención era reescribir en OPL+, desde cero, el código original, de forma clara y estructurada, para refrescar mis conocimientos del lenguaje. Por tanto en principio no pretendía añadirle características nuevas al juego.
En enero de 2011, con cerca ya de tres mil líneas de código, la cosa empezó a parecerse muy poco al programa original, ni en el fondo ni en la forma: de El bosque encantado solo quedaba parte del mapa.
El objetivo había cambiado; el programa se había convertido en un laboratorio de ideas nuevas, y por tanto de nuevos algoritmos, que siempre se podrían aplicar a otros proyectos. Por ejemplo:
- Historial de comandos, para facilitar la repetición de órdenes recientes. Es algo normal en las aventuras modernas y no tan modernas, pero que yo nunca había considerado. Es una idea tomada de los intérpretes de comandos de algunos sistemas operativos, y de algunos lenguajes de programación interpretados. El código está tomado de la implementación que hice para Forth 5mx.
- Todas las descripciones (de escenarios, y de algunos objetos importantes) están escritas a partir de textos extraídos de varias obras literarias (que por supuesto serán citadas con todos los detalles). El trabajo está bastante avanzado.
- Descripción de las direcciones, una idea que me rondaba desde hacía tiempo en otros proyectos. Significa que el protagonista puede mirar en cualquier dirección y así tener una idea de qué se encontrará.
- Descripciones de transición de escenario. Otra idea similar a la anterior. Son descripiciones propias de cada transición entre escenarios, y que preceden a la descripción propia del escenario destino. A su vez ambas varían en función de si el camino o el escenario ya son conocidos por el protagonista. Esto contribuye a la variedad y naturalidad de los textos, y a la ambientación.
- Textos variables al azar. Esto siempre lo he usado en mis proyectos, pero en este programa lo he hecho de forma muy elaborada. Así, un mismo texto, por ejemplo un mensaje en respuesta a una acción imposible, adopta muchas formas (a veces decenas) combinando diferentes elementos en diferente orden.
Estado del proyecto
El sistema operativo EPOC (de las máquinas Psion Series 5, Series 7 y NetBook), antecesor de Symbian, lleva años comercialmente muerto y prácticamente desaparecido de escena; queda poca gente que use o programe estas máquinas (por otra parte aún excelentes).
Para mí en principio nada de eso resta atractivo a programar en OPL+ para una computadora de bolsillo tan buena como la 5mx, solo por la inquietud y el reto en sí. Por otra parte, gracias al emulador de EPOC es posible programar en OPL+ sin la máquina original; e incluso sería posible (aunque altamente improbable) que algún día alguien aparte de mí jugara a esta aventura...
No obstante, en 2012-02, tras un tiempo sin poder utilizar mi 5mx, moví el proyecto a la categoría de «abandonados» para centrar esfuerzos en otros proyectos.
Pantallazos
Dos muestras de octubre de 2010, con los textos originales (de la vieja escuela «Estoy en») y diferentes tamaños de letra (el jugador puede cambiar el tamaño de letra «en caliente», como es costumbre en EPOC):
En enero de 2011 la interfaz de menú estaba terminada. Las imágenes que siguen muestran el programa funcionando en el emulador de EPOC, (en Debian. con Wine). Como se aprecia en los textos, ya están también implementadas (no totalmente) las ideas experimentales antes citadas.
Código fuente
Algunas pequeñas muestras del código en su estado actual.
// El bosque encantado de Oupouèle
// Una aventura conversacional escrita en OPL+
// para la computadora Psion 5mx
// (y compatibles, con sistema operativo EPOC R5).
// Copyright (C) 2010,2011 Marcos Cruz (http://programandala.net)
// Licencia/Permesilo/License: http://programandala.net/licencia
El texto de introducción (tomado literalmente del juego original) y el bucle principal:
// Presentation
tell:("El bosque encantado de Oupouèle")
newLine:
tellNL:("Al despertarme de un profundo sueño, el suelo del bosque está suave y seco.")
tell:("No sé cómo he llegado hasta aquí, pero sé que debo encontrar el poblado que linda con el bosque para estar a salvo.")
tell:("Miro a mi alrededor, tratando de orientarme.")
// Game loop
do
if formerLocation%<>location%
describeLocation:
formerLocation% = location%
endif
command$ = accept$:("",screenWidth%)
parser:(command$)
until gameOver:
El procedimiento que lista las salidas de un escenario:
proc listExits:
local i%
local exits%,printedExits% // counters
local exit%(KExits%)
local message$(KMaxStringLen%)
exits% = 0
i% = 1
do
exit%(i%) = locationExit%:(location%,i%)
exits% += iabs(exit%(i%)>1)
i%++
until i%>KExits%
if exits%
message$ = "Hay salida"+left$("s",iabs(exits%>1))+" hacia"
printedExits% = 0
i%=1
do
if exit%(i%)
printedExits%++
// Put a comma or a conjuction before the exit direction:
#if 0
// (the elegant way, but it's too dense and it always does two calculations:)
message$ += left$(",",iabs(printedExits%>1 and printedExits%<>exits%)) // comma
message$ += left$(" y",2*iabs(printedExits%>1 and printedExits%=exits%)) // final conjuction
#endif
#if 0
// (a lighter way:)
if printedExits%>1
message$ += left$(",",iabs(printedExits%<>exits%)) // comma
message$ += left$(" y",2*iabs(printedExits%=exits%)) // final conjuction
endif
#endif
// (the clasical way is faster:)
if printedExits%>1
if printedExits%=exits%
message$ += " y"
else
message$ += ","
endif
endif
// Add the exit direction to the final message:
message$ += " el "+exitName$(i%)
endif
i%++
until i%>KExits%
tellNLI:(message$+".")
else
tellNLI:("No hay salidas visibles.")
endif
endp
Parte del analizador lingüístico:
// Step 1: get the known words
do
//print "Command:","«";command$;"»" // debug!!!
wordBoundary% = loc(command$," ")
//print "Word boundary:",wordBoundary% // debug!!!
wordFound$ = left$(command$,wordBoundary%-1)
//print "Word found:","«";wordFound$;"»" // debug!!!
command$ = right$(command$,len(command$)-wordBoundary%)
//print "Letters remaining in the command:",len(command$) // debug!!!
wordRecognized% = inVocabulary:(wordFound$)
if wordRecognized%
//print "Word recognized:",wordRecognized% // debug!!!
known%++
//print "known counter:",known% // debug!!!
word%(known%) = wordRecognized%
//print "Stored!" // debug!!!
endif
// print "end condition", (len(command$)=0) or (known%=KMaxKnown%) // debug!!!
until (len(command$)=0) or (known%=KMaxKnown%)
// Step 2: get the syntax elements
// print "Known words:",known% // debug!!!
// print "Second step" // debug!!!
actionTerm% = 0
objectTerm% = 0
syntaxError% = 0
i% = 1
while i%<=known%
recognizedCommand$ += termWord$(word%(i%))+" "
// tell:("recognized so far: "+recognizedCommand$) // debug!!!
syntax% = termSyntax%(word%(i%))
if (KVerb% and syntax%)
if actionTerm%
if (KNoun% and syntax%)
goto checkObject
else
syntaxError:(KError2Actions%)
break
endif
endif
actionTerm% = termAction%(word%(i%))
actionSyntax% = syntax%
elseif (KNoun% and syntax%)
if actionTerm%=0
syntaxError:(KErrorObjectFirst%)
break
endif
checkObject::
if objectTerm%
syntaxError:(KError2Objects%)
break
endif
if actionSyntax% and KNoObject%
syntaxError:(KError1Object%)
break
endif
objectTerm% = termAction%(word%(i%))
objectSyntax% = syntax%
endif
i%++
endwh