Álvaro González Sotillo

Juego tipo escape room virtual

Durante la cuarentena del COVID-19 ha habido tiempo para casi todo. Después de jugar a un par de juegos escape room virtuales (paradoxroom, escaperoomdigital) me ha parecido interesante inventarme el mio propio.

El secreto de Despair Corp

El código fuente está oculto, para que no se vean las soluciones a los acertijos.

Opciones para la implementación del juego

Solo dispongo de alojamiento web estático (Github Pages y similar), así que hay que implementar la lógica en el lado cliente. Para que cada página no tenga la solución en el código html, hay dos soluciones

  • Ofuscar la respuesta correcta: La respuesta puede estar incluida en la página, pero no en texto legible.
    • Usando un sistema reversible como base64 se oculta de forma superficial. No costaría demasiado saber la respuesta.
    • Se puede guardar su hash, de forma que no se pueda saber la respuesta a partir del hash.
  • Preguntar al servidor si la respuesta es correcta: Aunque sea un hosting estático, el servidor responde con un código HTTP 200 si el recurso existe, u otro código si no existe.

Opción utilizada

Cuando el jugador introduce la respuesta, la normalizo pasándola a mayúsculas y quitando caracteres sobrantes, como espacio, guión, acentos…

    function normalizaRespuesta(str){
        let sustituciones = {
            " " : "",
            "-" : "",
            "," : "",
            "." : "",
            "Á" : "A",
            "É" : "E",
            "Í" : "I",
            "Ó" : "O",
            "Ú" : "U",
            "Ü" : "U",
            "Ñ" : "N"
        };

        letras = Array.from(str.toUpperCase());
        let sanitized = letras.map( 
          c => typeof sustituciones[c] != "undefined" ? 
               sustituciones[c] : 
               c ).join("");
        return sanitized;
    }

Cada página web de la aventura está en un directorio que tiene por nombre la solución del paso anterior. Por ejemplo, si el paso 1 tiene como respuesta correcta El cantar del mío Cid, esta respuesta se normaliza a ELCANTARDELMIOCID y el siguiente paso se almacena en la URL ../ELCANTARDELMIOCID/index.hml.

Utilizo el siguiente script para saber si un recurso existe:

    function existeURL(url){
        let request = new XMLHttpRequest();  
        request.open('GET', url, false);
        request.send(null);
        let found = request.status != 404;
        return found;
    }

De esta forma, se detecta una respuesta correcta porque existe una página en el servidor con ese nombre, y se salta a esa página. Más serverless no puede ser 😎

Generación de páginas

Cualquier generador de sitios estáticos valdría para implementar el juego. Yo he elegido org-publish, para no salir de emacs.

La configuración se basa en dos proyectos: uno que transforma los ficheros org en html, y otro que copia el resto de recursos (js, css, zip…)

(setq org-publish-project-alist
      '(
        ("aventura-org"
         :base-directory "/home/alvaro/github/aventura/org/"
         :base-extension "org"
         :publishing-directory "/home/alvaro/github/aventura/public/"
         :recursive t
         :publishing-function org-html-publish-to-html
         :headline-levels 4             ; Just the default for this project.
         :auto-preamble t
         )

        ("aventura-static"
         :base-directory "/home/alvaro/github/aventura/org/"
         :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|html\\|svg\\|zip\\|ttf"
         :publishing-directory "/home/alvaro/github/aventura/public/"
         :recursive t
         :publishing-function org-publish-attachment
         )

        ("aventura"
         :components ("aventura-org" "aventura-static")
         )
        
        ))

Para generar las páginas utilizo esta función interactiva:

(defun publicar-aventura()
  (interactive)
  (delete-directory "/home/alvaro/github/aventura/public/" t)
  (org-publish-remove-all-timestamps)
  (org-publish-project "aventura" t))