Los arquitectos del Castillo
El Castillo nació como Proyecto Fin de Carrera en la Facultad de Informática de la Universidad de Málaga. Dos catedráticos de disciplinas distintas —lógica filosófica y sistemas informáticos— se unieron para crear algo que iba mucho más allá de un trabajo académico: un puente entre el razonamiento formal y la tecnología emergente de la web interactiva.
La base artística original se mantiene como referencia histórica: los dibujos de 1997, realizados a mano por Elena Bueno Vallejo, siguen siendo el origen visual del universo de El Castillo.
Alfredo Burrieza Muñiz
Concibió y diseñó todos los acertijos lógicos del juego, inspirándose en los trabajos del matemático y lógico Raymond Smullyan —célebre por sus libros de puzzles de caballeros y escuderos que siempre dicen la verdad o siempre mienten—. Cada uno de los doce niveles esconde un teorema de lógica proposicional envuelto en la piel de un enigma narrativo.
Ricardo Conejo Muñoz
Director de la implementación y de la tesis doctoral del autor. Fue el webmaster del departamento cuando en España apenas existían cinco páginas web, sentando las bases para que David comenzara su andadura en la programación web. Su visión de la informática educativa y su conocimiento pionero de las tecnologías de Internet hicieron posible que este proyecto existiera.
Elena Bueno Vallejo
Los personajes y escenas del juego original fueron dibujados a mano por Elena Bueno Vallejo, pieza esencial para la identidad visual del proyecto. Su trabajo artístico fijó la estética que esta versión 2026 respeta y homenajea.
Sobre Raymond Smullyan (1919–2017): Matemático, lógico y mago estadounidense, autor de ¿Cómo se llama este libro? (1978) y El Señor de las Islas Misteriosas. Sus puzzles de islas donde los caballeros siempre dicen la verdad y los escuderos siempre mienten son hoy un clásico de la lógica recreativa. Los acertijos de El Castillo siguen exactamente esa tradición: premisas condicionales reales cuya solución requiere razonamiento deductivo puro.
Cuando el servidor era el único inteligente
En 1997 la World Wide Web estaba apenas en pañales como plataforma de aplicaciones interactivas. Los navegadores dominantes eran Netscape Navigator 3 e Internet Explorer 3, y JavaScript —recién nacido en 1995— era considerado inseguro, inconsistente entre navegadores y totalmente inadecuado para manejar lógica de negocio real.
La respuesta arquitectónica de la época fue clara: toda la inteligencia vive en el servidor. Cada interacción del usuario —pulsar un botón, marcar un checkbox, elegir una opción— enviaba un formulario HTTP al servidor. Una aplicación en C leía los parámetros, calculaba el nuevo estado, y devolvía HTML puro generado dinámicamente. Esto es lo que se denominó CGI: Common Gateway Interface.
HTTP sin estado
Cada petición era absolutamente independiente. No existían sesiones nativas. El estado del usuario (nivel actual, vidas restantes) debía pasarse explícitamente en cada URL o en campos ocultos del formulario.
Latencia como Gameplay
Cada click en una "zona sensible" enviaba el formulario al servidor, que generaba la pantalla siguiente. El tiempo de respuesta de la red era parte involuntaria de la experiencia de usuario.
HTML 3.2 estático
Sin CSS en cascada, sin layout moderno. Tablas HTML para maquetar, atributos WIDTH y BGCOLOR directamente sobre los tags. Dos niveles de fuente. Nada de animaciones.
experto.c: el corazón del castillo
El fichero EXPERTO.C es el alma original del proyecto. Un programa ANSI C compilado como ejecutable
que el servidor web invocaba en cada petición. Recibía parámetros vía stdin (método POST)
o la variable de entorno QUERY_STRING (método GET), procesaba la lógica del juego
y escribía HTML completo a stdout. El servidor enviaba esa salida directamente al navegador.
El protocolo CGI era brutalmente simple: el servidor ejecutaba el binario,
pasaba los datos como variables de entorno y capturaba su stdout.
La primera línea era siempre Content-type: text/html seguida de una línea en blanco.
El resto era HTML puro. No había framework, no había ORM, no había nada.
/* ============================================================ EXPERTO.C — Motor principal del juego El Castillo (1997) Compilado con Borland C para Windows NT / UNIX CGI ============================================================ */ #define NIVE 1 /* Solicitud de pantalla de nivel */ #define NUEVO 2 /* Creación de usuario nuevo */ #define NUEVAVIDA 6 /* El jugador ha perdido una vida */ #define VERRECORDS 8 /* Mostrar tabla de records */ #define MARCA '{' /* Carácter centinela → sustituir por ID */ #define MARCACGI '^' /* Centinela → sustituir por URL del CGI */ #define MARCAVIDAS '`' /* Centinela → inyectar corazones de vida */ #define NUMVIDAS 3 typedef struct NIV { int n_opc; /* Número de opciones del nivel */ OPCION opc[5]; /* Hasta 5 puertas posibles */ char lista[4]; /* Selección del jugador (N7 y N12) */ } NIVEL; int main() { read_cgi_input(&entries); /* Lee parámetros POST/GET */ printf("Content-type : text/html \n\n"); /* Cabecera HTTP obligatoria */ tipo = atoi(cgi_val(entries, "tipo")); switch(tipo) { case NIVE: analizar_nivel(); break; case NUEVAVIDA: ir_sgte_nivel(); break; case VERRECORDS: mostrar_records(); break; } list_clear(&entries); /* Liberar memoria (no había GC) */ exit(0); }
El programa mantenía la lista de jugadores en ficheros de texto planos.
Cada usuario tenía un fichero usuN.dat con su nivel actual, variante, vidas y el historial
de los niveles donde había muerto. No había base de datos relacional: el sistema de ficheros era la base de datos.
El truco de los caracteres centinela: { } ^ `
Sin lenguajes de plantillas, sin Jinja2, sin Handlebars, sin JSX —ni siquiera PHP existía en producción estable— el desarrollador de 1997 necesitaba algún mecanismo para parametrizar los ficheros HTML. La solución fue tan sencilla como brillante: reservar caracteres raros como marcadores de inserción.
La función mostrar_nivel() leía el fichero HTML carácter a carácter.
Cuando encontraba un carácter especial, lo sustituía por el valor correspondiente calculado
en tiempo real. Era un motor de plantillas artesanal de 40 líneas de C.
/* Los ficheros HTML contenían caracteres especiales: { → sustituir por el ID de usuario (estado de sesión) } → sustituir por la URL base del servidor ^ → sustituir por la URL del ejecutable CGI ` → inyectar los corazones de vida (IMG tags) + formulario ñ → mostrar botones de Fin de Juego */ char car = fgetc(f); while(!feof(f)) { switch(car) { case MARCA: /* '{' = ID del usuario → sostiene el estado de sesión */ printf("%s", cad); /* ej: "usuario=157" */ break; case MARCABASE: /* '}' = URL base del servidor */ printf("%s", DIRBASE); break; case MARCACGI: /* '^' = URL del ejecutable (ej: /cgi-bin/experto.exe) */ printf("%s", DIRCGI); break; case MARCAVIDAS: /* '`' = Inyectar corazones ❤️ y barra de nivel */ for(int i=0; i<vidas; i++) printf("<IMG SRC=\"pantallas/vida.gif\" ALIGN=MIDDLE>"); printf("<IMG SRC=\"pantallas/barra%s.gif\">", usu->niv_actual); break; default: putchar(car); /* El resto del HTML se copia tal cual */ } car = fgetc(f); }
Este patrón —leer stream, detectar marcador, sustituir por valor dinámico— es exactamente
lo que décadas después popularizarían Mustache (2009), Handlebars (2011) o Jinja2 bajo los nombres
de "interpolación de variables" y "motor de plantillas",
con sus dobles llaves {{ variable }}, sus filtros y su herencia de bloques.
Mustache / Handlebars (2009–11)
Popularizaron {{ nombre }}. La misma idea: un marcador que el motor sustituye por un valor. La diferencia: eran agnósticos al lenguaje y tenían loops y condicionales.
Jinja2 / Django Templates
Llevaron el concepto al extremo con herencia de bloques ({% extends %}), filtros ({{ var | upper }}) y macros. El equivalente moderno del switch de C.
JSX / React (2013)
En JSX la interpolación usa {variable}, una sola llave. El abuelo C de experto.c usaba { por la misma razón: es el carácter más raro que no aparece en HTML puro.
Zonas sensibles: el padre del click-to-play
Antes de que existieran los div posicionados con coordenadas porcentuales,
la única forma de crear áreas clicables sobre una imagen era el Image Map de HTML 3.2.
Se definía un elemento <MAP> con áreas rectangulares, circulares o poligonales,
cada una con sus coordenadas absolutas en píxeles y su enlace destino.
<!-- La imagen de 693×287 píxeles tenía dos zonas clicables: una para usuarios nuevos (izquierda) y otra para veteranos (derecha) --> <map NAME="MAPA"> <area SHAPE="RECT" COORDS="131,102,251,273" HREF="nuevousu.html"> <area SHAPE="RECT" COORDS="432,101,549,274" HREF="oldusu.html"> </map> <img BORDER="0" SRC="pantallas/principi.gif" USEMAP="#MAPA" WIDTH="693" HEIGHT="287">
El gran problema: las coordenadas eran absolutas en píxeles. Si la imagen se escalaba (por resolución de pantalla diferente, o un usuario con zoom), las zonas quedaban desplazadas por completo. No había concepto de "porcentaje" ni "responsive". En El Castillo 2026 hemos resuelto esto con coordenadas porcentuales calculadas dinámicamente.
El guardián invisible: verifica_fich_usuario()
Un diseño interactivo en web siempre enfrenta el mismo dilema: ¿cómo impedir que un jugador
manipule la URL para saltar directamente al nivel 12 sin haber superado los anteriores?
En 1997 no existían JWT, ni HMAC, ni tokens firmados. La solución de EXPERTO.C
era elegantemente simple: el estado real nunca salía del servidor.
En la URL solo viajaba un número de usuario entero autoincremental (usuario=157).
El nivel donde estaba realmente ese jugador vivía en un fichero en disco del servidor (usu157.dat).
Aunque el usuario tecleara nivel=12 en la barra de direcciones,
el servidor lo ignoraba olímpicamente y leía la verdad desde el fichero.
El principio de seguridad: Zero-Trust Client. Todo parámetro que viene del navegador se considera potencialmente falso. La única fuente de verdad es el estado que el servidor custodia. Este principio —que hoy llamamos server-side authoritative state— sigue siendo el fundamento de la seguridad en juegos online modernos como los MMORPGs.
/* Llamada en cada petición de nivel antes de mostrar nada. Si el usuario manipuló la URL, es expulsado con un aviso. */ void verifica_fich_usuario(char *fichero) { /* Leer el estado REAL del jugador desde su fichero en disco */ leer_fichero_usuario(&usuario); /* Comprobación 1: ¿El nivel de la URL coincide con tu nivel real? */ if (atoi(usuario.niv_actual) != atoi(nivel_en_url)) → Error: el jugador intenta ir a un nivel que no le corresponde /* Comprobación 2: ¿Es este subnivel uno donde ya moriste? */ if (muertoen(&usuario, nivcompleto)) → Error: intentas resetear una pantalla que ya perdiste /* Comprobación 3: ¿Te quedan vidas? */ if (usuario.vidas == 0) → Redirige a pantalla de "has perdido todas tus vidas" /* Si todo es correcto → mostrar el nivel */ /* Respuesta al intento de trampa (estaba en mensaje1.htm): */ /* «No intentes engañarme. Tú no vas por ahí.» */ } /* muertoen(): devuelve 1 si el subnivel pedido es uno donde ya cayó */ int muertoen(USUARIO *usuario, char *subnivel_pedido) { for(int i=0; i < (NUMVIDAS - usuario->vidas); i++) if (atoi(usuario->muerte[i]) == atoi(subnivel_pedido)) return 1; /* Trampa detectada */ return 0; }
Adicionalmente, la función alea() garantizaba que al reaparecer en un nivel
después de un fallo nunca te tocaba la misma variante donde ya habías muerto.
Usaba srand(time()) con un bucle de reintento para esquivar las variantes
registradas en el historial del jugador —sin necesidad de ningún algoritmo sofisticado.
Estado autoritativo (1997)
El fichero usu{N}.dat en el servidor era la única fuente de verdad. El cliente solo tenía su número de ID opaco. Imposible falsificar el progreso sin acceso directo al disco del servidor.
Anti-repetición con alea()
Al volver a un nivel tras un fallo, rand() % num_variantes con lista negra de subniveles muertos garantizaba que siempre veías un reto fresco, sin poder memorizar la solución.
El mensaje eterno
El fichero mensaje1.htm contenía el aviso de trampa. En cursiva, lacónico y contundente: «No intentes engañarme. Tú no vas por ahí.» En 2026 aún podría servir.
Cómo se rehizo El Castillo con Inteligencia Artificial
Veintinueve años después de la versión original, el proyecto fue resucitado usando un stack completamente diferente al de 1997: ninguna línea de C, ninguna de PHP, ningún servidor propio. En su lugar, dos herramientas de IA trabajaron en tándem para dar vida a la remasterización.
Antigravity
Encargado de toda la arquitectura de la aplicación 2026: diseñó e implementó el motor de juego
en Vanilla JS, la integración con Firebase, el sistema de coordenadas porcentuales,
el modo Editor para calibrar zonas táctiles, los niveles especiales (ciclo y multiselección)
y la migración del lore histórico desde los ficheros .htm de 1997 al nuevo levels.json.
Nanobanana
Responsable de la remasterización visual. Recibió las imágenes GIF originales de 1997 como referencia de escena y un prompt maestro de estilo para producir las 72 ilustraciones nuevas, una a una, manteniendo los mismos personajes y la misma arquitectura de sala en cada variante.
El reto: 72 imágenes con consistencia visual
El catálogo visual del juego es de 72 pantallas únicas (12 niveles × 6 variantes cada uno). Cada variante del mismo nivel muestra la misma sala pero con personajes distintos —distintas profesiones, edades o rangos, que son la clave del acertijo lógico—. El desafío: que el fondo, la iluminación, el estilo del bocadillo de diálogo y los personajes recurrentes (el Hada Benéfica, el Genio Maléfico) fueran idénticos en todas las variantes de un mismo nivel, pero distintos entre niveles.
Crónica real del proceso: primero generamos 30 niveles a mano, uno por uno, con paciencia monástica y café en vena. Cuando vimos que quedaban 42, activamos el modo “trabaja tú, máquina”: construimos un script con la API para automatizar el resto. Resultado: menos clics repetitivos, más control de calidad y cero riesgo de acabar hablando con las antorchas.
El prompt maestro de remasterización fue redactado para funcionar como una hoja de encargo a un director de arte. En lugar de describir “una sala de castillo” en abstracto, le pedía al modelo que analizara primero el GIF original, extrajera sus elementos clave (escenario, poses, texto de bocadillos, composición), y luego los trasladara al nuevo estilo manteniendo la coherencia espacial al mílimetro.
Instrucción: Actúa como un artista digital principal encargado de remasterizar una escena de un juego de aventura gráfica clásico (estilo pixel art de los 90) a un estilo de ilustración de fantasía moderno, detallado y cinematográfico. PASO 1 — Analizar la Imagen Antigua: ► Escenario: paredes, suelo, puertas, ventanas, antorchas y su ubicación exacta. ► Personajes: personaje femenino (hada/princesa) y personaje mágico (genio/mago). ► Textos: lee y transcribe verbatim cada bloque de texto de los bocadillos. ► Composición: mantén la disposición horizontal y la relación espacial. PASO 2 — Analizar la Imagen de Referencia (Estilo Objetivo): ► Estilo artístico: ilustración digital de alta calidad, renderizado vibrante, texturas detalladas (hiedra, piedra, terciopelo), iluminación ambiental profunda. ► Diseño de personajes: el hada moderna (vestido azul, tiara, varita) y el genio (humo verde, brazaletes, lámpara). ► Formato de bocadillos: nube limpia, tipografía sans-serif legible y uniforme. PASO 3 — Ejecutar la Remasterización: ► Escenario modernizado: recrea el entorno con texturas ricas, hiedra, musgo e iluminación ambiental caálida. ► Personajes actualizados: sustituye modelos antiguos por los modernos manteniendo poses y ubicación originales. ► Texto preservado: inserta el texto exacto en bocadillos modernos. VERIFICACIÓN: ► ¿Están todos los elementos de la escena original? ► ¿El texto es idéntico al original? ► ¿El estilo es indistinguible de la referencia? ► ¿Los personajes son los modernos, no redibujados?
El problema de la consistencia: la pesadilla de toda IA generativa
En generación de imágenes, la consistencia de personaje entre imágenes es el problema más difícil. Cada generación parte de ruido aleatorio: sin anclar el modelo a una referencia visual sólida, el Hada Benéfica puede cambiar de cabello entre el nivel 3 y el 4. La estrategia usada fue siempre adjuntar la primera imagen generada y aprobada de cada nivel como referencia de estilo para las 5 variantes siguientes, forzando al modelo a mantener el mismo Genio, la misma Hada y el mismo escenario.
En términos de producción, quedó así: fase artesanal (30 niveles), fase industrial (42 niveles por API). El equilibrio perfecto entre ojo humano y automatización: el humano decide el estándar; el script hace horas extra sin quejarse.
GIF → PNG: el salto de resolución
Los GIF originales eran de 640×480 en 256 colores. Las nuevas imágenes PNG generadas tienen resolución libre y full-color, adaptadas automáticamente a cualquier pantalla gracias al contenedor CSS de aspecto dinámico.
Reajuste de zonas táctiles
Al cambiar el tamaño y proporción de las imágenes, las áreas clicables de 1997 (en píxeles absolutos) no servían. Un Modo Editor integrado en la propia app permitió arrastrar y reposicionar cada zona sobre la nueva imagen hasta ajustarla con precisión porcentual.
El humano en el bucle
Cada imagen generada fue revisada manualmente: texto correcto, personajes coherentes, composición respetada. Lo que no era aceptable se regeneraba con el prompt refinado. La IA es rápida; el criterio artístico sigue siendo humano.
1997 vs 2026: la misma idea, otro universo técnico
| Característica | El Castillo 1997 | El Castillo 2026 |
|---|---|---|
| Lenguaje servidor | C compilado (experto.exe) | Sin servidor — lógica 100% cliente JS |
| Base de datos | Ficheros .dat en el filesystem | Firebase Firestore (NoSQL cloud) |
| Autenticación | Login/Password en fichero de texto | OAuth2 con Google (Firebase Auth) |
| Sesión de usuario | ID numérico en cada URL y campo oculto | JWT token gestionado por el SDK de Firebase |
| Zonas interactivas | Image Maps HTML con px absolutos | divs CSS con coordenadas % relativas |
| Plantillas | Caracteres centinela en ficheros .htm | Template literals ES6 + DOM dinámico |
| Generación de UI | printf() de HTML en C | createElement() + appendChild() en JS |
| Imágenes | GIF 256 colores, 640×480 | PNG generado con IA (resolución libre) |
| Despliegue | FTP manual a servidor Unix | Firebase Hosting + CDN global |
| Dependencias | cgi-lib.c, html-lib.c, llist.c (propias) | Zero dependencies — solo Firebase SDK |
Lo que no ha cambiado
Veintinueve años después, el reto sigue siendo el mismo: que el usuario piense, se equivoque y aprenda. Las herramientas han evolucionado de forma radical —de un binario C en un servidor Unix compartido a una SPA serverless con Firebase y assets generados por IA— pero el núcleo del diseño instruccional se mantiene intacto.
El mayor homenaje que podemos hacerle a EXPERTO.C es que los principios que lo guiaron siguen vigentes:
separación de datos y presentación (levels.json vs app.js),
estado mínimo y explícito (gameState),
y lógica en el punto de mínima latencia (cliente en 2026, servidor en 1997).
El Castillo no es solo un juego de lógica.
Es un documento vivo de treinta años de evolución de la ingeniería web,
guardado en una misma carpeta donde conviven EXPERTO.C de 1997
y app.js de 2026, separados por 48.000 bytes de historia.