Un Sudoku, un Nonogram y un Slitherlink pueden aparecer uno junto a otro en la misma web, pero por dentro no se parecen demasiado. Uno trabaja con dígitos y restricciones en filas, columnas y cajas; otro convierte pistas numéricas en bloques que deben rellenarse; el tercero construye un único lazo a partir de relaciones locales. Si intentáramos meter esas reglas en una sola abstracción universal, acabaríamos inventando un lenguaje tan genérico que sería difícil de entender y aún más difícil de mantener. La solución útil no es forzar a todos los puzzles a ser el mismo juego. Es decidir con cuidado qué partes sí pueden compartir.

Ese es el corazón de la arquitectura de Blupoli Puzzles. Cada puzzle mantiene un motor capaz de entender su propio estado, sus acciones y su condición de finalización. Alrededor existe una plataforma que resuelve necesidades repetidas: navegación, presentación, integración con el catálogo, controles comunes, temas, persistencia cuando corresponde, accesibilidad, localización y otras piezas de producto. La separación nació durante la etapa en la que el proyecto aún se llamaba Blupoli, como contamos en la historia del primer Sudoku, pero sigue siendo relevante porque el problema no desaparece cuando el catálogo crece; se vuelve más importante.

Un motor no es una miniaplicación escondida

La forma más sencilla de imaginar un motor es pensar en una conversación. La plataforma pregunta «este es el estado actual y el jugador ha hecho esto; ¿qué ocurre ahora?». El motor responde con un nuevo estado, o indica que la acción no tiene sentido. También puede decir si el puzzle está resuelto, qué información debe mostrarse y qué restricciones son relevantes. Lo importante es todo lo que el motor no necesita saber. No debería decidir dónde está el menú, cómo funciona la navegación del sitio o qué color exacto usa la cabecera.

Esa ignorancia intencional es una ventaja. Cuantas menos responsabilidades ajenas conoce el motor, más fácil resulta probar su lógica sin abrir un navegador y más fácil es cambiar la interfaz sin tocar las reglas. También permite que el mismo marco de producto aloje juegos con estructuras radicalmente distintas. Un tablero puede ser una cuadrícula, una red, una lista de elementos o una composición de regiones. La plataforma necesita saber cómo presentarlo y conectarlo con el resto del producto, pero no debería fingir que todas esas estructuras comparten la misma semántica interna.

El estado es el contrato entre lógica y experiencia

Separar motor e interfaz solo funciona si ambos comparten un contrato claro. Ese contrato suele ser el estado: una representación de lo que existe en este momento y de la información necesaria para dibujarlo. El estado de un Sudoku puede incluir valores, notas y celdas fijas. Otro puzzle puede necesitar conexiones, marcas, regiones o posiciones. La interfaz lee esa información; el motor la transforma cuando recibe una acción. En vez de que un clic modifique directamente elementos visuales y reglas a la vez, la acción atraviesa una frontera explícita.

Esta forma de trabajar tiene una consecuencia que el jugador no ve, pero sí nota cuando algo sale mal: reduce los estados imposibles. Si la interfaz pudiera cambiar por su cuenta una celda sin que el motor supiera nada, lo que se ve y lo que las reglas creen que existe podrían divergir. Cuando el motor es la autoridad de la lógica, la pantalla se convierte en una representación de una única fuente de verdad. No evita todos los errores, pero hace que muchos sean más fáciles de localizar porque existe un lugar concreto donde decidir qué transición era válida.

Compartir un renderer no significa compartir un tablero

En una plataforma de puzzles aparece pronto la tentación de construir «el componente de tablero» definitivo. Funciona bien mientras todos los juegos se parecen. Después llega uno con pistas externas, otro con celdas triangulares, otro con segmentos entre nodos y otro que no necesita una cuadrícula en absoluto. Si el renderer compartido intenta conocer cada caso, se transforma en una colección de condicionales. En lugar de simplificar, concentra complejidad y hace que cada incorporación tenga miedo de romper a las anteriores.

La estrategia más sana consiste en compartir patrones de renderizado y comportamiento, no una forma única obligatoria. Podemos compartir selección, estados visuales, gestión de eventos, responsividad o utilidades de dibujo, mientras cada juego conserva la libertad de componer su superficie. El marco común dicta cómo se integra una experiencia; el puzzle decide cómo se expresa. Esta diferencia parece pequeña en una arquitectura sobre el papel, pero determina si un catálogo puede seguir incorporando ideas raras sin convertirlas en versiones deformadas del primer juego.

Diagrama sin texto con varios motores distintos que convergen en una capa compartida de experiencia y catálogo
Los motores no tienen que parecerse entre sí. Lo compartido es la capa que los convierte en experiencias coherentes dentro del mismo producto.

La metadata también es arquitectura

Nombre, slug, descripción, familia o instrucciones pueden parecer contenido editorial, pero en una plataforma funcionan como parte de la arquitectura. Si esos datos están dispersos por archivos y páginas, cada nueva característica necesita buscarlos de una manera diferente. Si existe una fuente de verdad consistente, el catálogo puede construir listados, las rutas pueden generarse de forma predecible, las páginas editoriales pueden enlazar a destinos reales y los procesos automáticos pueden comprobar que nada queda huérfano.

Esto es especialmente importante cuando el proyecto deja de ser una colección pequeña. El código del motor sabe jugar; la metadata sabe situar ese juego en el producto. Confundir ambas cosas sería tan problemático como meter el menú global dentro del solver. La separación hace posible cambiar una descripción o una categoría sin tocar la lógica, y mejorar la lógica sin reescribir la forma en que el catálogo la presenta. La arquitectura no solo organiza código: organiza responsabilidades y fuentes de verdad.

El host es la pieza que evita que cada juego reinvente la aplicación

Entre el motor y la página completa existe una capa útil que podemos llamar host: el lugar donde el juego entra en la aplicación. El host se encarga de crear o cargar una partida, conectar controles generales, presentar la información del puzzle, coordinar reinicios y resolver detalles que serían repetitivos si cada motor tuviera que implementarlos. Su valor está en conocer lo suficiente de ambos lados sin apropiarse de las reglas específicas.

Cuando esa capa está bien diseñada, añadir un juego se parece menos a crear una miniaplicación y más a registrar un nuevo participante en un sistema conocido. El motor aporta sus capacidades. La metadata explica quién es. El host proporciona el entorno. La interfaz específica dibuja lo que haga falta. Esta composición evita que una mejora global —por ejemplo, un cambio de navegación o un nuevo patrón de ayuda— obligue a abrir decenas de implementaciones independientes para repetir la misma corrección.

La consistencia reduce el tiempo que se pierde antes de empezar a pensar

La ventaja de una experiencia compartida no es solo estética. En un puzzle, la atención es un recurso. Si cada juego cambia de lugar los controles, usa reglas distintas para reiniciar o expresa la selección con convenciones incompatibles, el jugador dedica parte de su memoria de trabajo a aprender la aplicación en vez de resolver el reto. Una plataforma coherente puede enseñar una gramática básica una vez y dejar que la diferencia interesante esté en las reglas del puzzle.

Eso no significa eliminar toda singularidad. Un juego puede necesitar botones propios, símbolos específicos o una forma distinta de manipular el tablero. La consistencia útil está en aquello que el usuario espera que pertenezca al producto: volver al catálogo, empezar de nuevo, entender el estado, reconocer qué es interactivo, usar teclado o tacto y recibir feedback. Al estabilizar esas capas, cada motor puede ser más atrevido en su lógica sin aumentar de forma proporcional la fricción de aprendizaje.

La accesibilidad mejora cuando la solución puede propagarse

Una arquitectura compartida tiene otra ventaja menos visible: hace que las mejoras transversales sean económicamente posibles. Si cada juego tiene su propio sistema de foco, etiquetas, tamaños táctiles y navegación, corregir un problema de accesibilidad significa resolverlo una y otra vez. Si los elementos comunes viven en componentes o convenciones centrales, una mejora puede llegar a muchas experiencias al mismo tiempo. La plataforma se convierte en multiplicador de calidad, no solo de cantidad.

Hay límites. Un Nonogram necesita información accesible que un Sudoku no necesita y un juego basado en conexiones puede requerir una estrategia completamente diferente. La capa común no sustituye el trabajo específico. Lo que hace es retirar del camino lo repetible para que el esfuerzo pueda concentrarse en lo singular. Es la misma lógica que aplicamos al motor: compartir donde existe una responsabilidad común real, especializar donde las reglas o la interacción lo exigen.

Los errores también enseñan dónde está mal situada una responsabilidad

Cuando un bug obliga a tocar cinco motores para corregir un comportamiento idéntico, probablemente esa responsabilidad estaba demasiado abajo. Cuando un componente compartido acumula excepciones para diez juegos, quizá estaba demasiado arriba. Los fallos no solo revelan código incorrecto; también revelan fronteras incorrectas. Una arquitectura que evoluciona presta atención a esos patrones y mueve responsabilidades cuando la evidencia contradice la organización original.

Por eso no tratamos la separación motor-host-interfaz como una ley inmutable. Es un modelo que se revisa. Algunos puzzles necesitan capacidades que al principio parecían específicas y luego demuestran ser comunes. Otras abstracciones nacen compartidas y terminan dividiéndose porque ocultan diferencias importantes. El objetivo no es mantener un diagrama perfecto, sino mantener bajo el coste de entender y cambiar el sistema. Una frontera es buena mientras reduce acoplamiento sin esconder el comportamiento que necesitamos razonar.

Generar, resolver y validar son funciones relacionadas, no idénticas

Otro lugar donde las responsabilidades importan es la creación de partidas. Un solver responde a la pregunta «¿existe una solución y cómo se alcanza?». Un generador responde «¿cómo produzco una instancia?». Un verificador de calidad puede preguntar además si esa instancia tiene una única solución, si evita configuraciones degeneradas o si presenta una dificultad razonable. Hacer que una sola pieza de código cargue con las tres funciones parece conveniente al principio, pero dificulta comprobar cada una por separado.

La separación ayuda a reutilizar conocimientos sin confundir objetivos. Un solver puede servir para validar un generador, pero no convierte automáticamente cualquier salida en un buen puzzle. Este tema merece su propia profundidad y lo desarrollamos en el artículo sobre generación y resolución. Desde el punto de vista de arquitectura, lo importante es reconocer que dos algoritmos pueden compartir reglas y aun así necesitar contratos distintos porque responden preguntas distintas.

Una plataforma no debería conocer el futuro para estar preparada

Es fácil caer en la sobrearquitectura cuando se piensa en escalabilidad. Si imaginamos todos los tipos posibles de puzzle antes de implementar el segundo, terminamos construyendo extensiones para problemas que quizá nunca existan. El enfoque que mejor ha funcionado es más pragmático: mantener fronteras simples, registrar patrones repetidos y generalizar cuando existen ejemplos suficientes. La extensibilidad nace más de responsabilidades claras que de anticipar una lista infinita de opciones.

Esto también reduce el coste de equivocarse. Una interfaz pequeña entre motor y host se puede cambiar. Un sistema genérico con decenas de conceptos hipotéticos tiende a convertirse en infraestructura que nadie se atreve a tocar. Prepararse para crecer no significa predecir cada juego; significa evitar que el primero capture decisiones que pertenecen al producto entero. El resto puede evolucionar con evidencia.

Los tests ganan valor cuando prueban capas con propósitos claros

Un motor separado se puede probar con estados y acciones sin depender de píxeles. La interfaz se puede revisar con estados conocidos sin tener que generar una partida compleja. Los procesos de catálogo pueden comprobar rutas y metadata sin resolver puzzles. Esta división permite que cada prueba falle por una razón más concreta. No elimina la necesidad de pruebas integradas, pero evita depender exclusivamente de ellas para saber si una regla básica se rompió.

Para una biblioteca grande, esta característica se vuelve esencial. Los cambios compartidos tienen un radio de impacto amplio. Necesitamos confianza para mejorarlos sin asumir que cualquier modificación global puede haber roto un juego que no abrimos manualmente. Las verificaciones automáticas no reemplazan una revisión real de experiencia, pero proporcionan una red que hace viable mantener muchas piezas. Arquitectura y calidad están conectadas: es más fácil verificar un sistema cuando sus partes tienen contratos observables.

La IA funciona mejor cuando el repositorio explica sus fronteras

Las herramientas de IA hacen todavía más evidente el valor de una arquitectura explícita. Un agente puede producir código rápidamente, pero necesita saber qué archivo es fuente de verdad, qué responsabilidad pertenece al motor, dónde se registra un juego y qué comprobaciones definen una integración completa. En un repositorio lleno de convenciones implícitas, la velocidad de generación produce inconsistencias. En uno con contratos claros, la IA puede repetir patrones sin inventar una arquitectura nueva en cada tarea.

Eso no convierte el diseño del sistema en una tarea automática. Al contrario, desplaza el valor hacia las decisiones que el agente no puede inferir por sí solo: si dos juegos comparten realmente un patrón, si una abstracción mejora la experiencia, si una excepción merece convertirse en una capacidad o si un cambio simplifica hoy a costa de complicar mañana. En cómo usamos IA durante el desarrollo hablamos de esa relación entre velocidad y criterio.

El crecimiento del catálogo fue una prueba de estrés

Las arquitecturas pequeñas pueden parecer elegantes porque todavía no han encontrado un caso que las contradiga. Cada nuevo puzzle actúa como una prueba de estrés. Un juego introduce una entrada que no es una celda. Otro necesita dos fases. Otro utiliza un tablero irregular. Otro obliga a mostrar información fuera de la cuadrícula. Si la plataforma solo acepta lo que ya conoce, cada incorporación se convierte en una lucha. Si acepta cualquier cosa sin contrato, deja de ofrecer valor.

El equilibrio se consigue observando dónde se repiten los problemas. Cuando varias experiencias necesitan la misma capacidad, la plataforma puede asumirla. Cuando una necesidad solo tiene sentido en un juego, el motor o su presentación específica la conservan. Esta estrategia explica mejor el paso de un puzzle a decenas de juegos que cualquier cifra: crecer fue un proceso continuo de encontrar la frontera correcta entre variedad y coherencia.

La arquitectura también decide cuánto cuesta mejorar algo existente

Añadir novedades suele recibir más atención que mejorar lo que ya existe, pero un sistema sostenible debe hacer ambas cosas baratas. Si cambiar un patrón de interacción exige editar cada juego individualmente, la plataforma crea una deuda que penaliza cualquier refinamiento. Si la interacción compartida vive en el lugar adecuado, una sola mejora puede elevar el conjunto. Lo mismo ocurre con estilos, navegación, telemetría, localización o políticas de accesibilidad.

Esta es una de las razones por las que no medimos el valor de la arquitectura solo por la velocidad de incorporar juegos. También importa la velocidad de elevar el estándar. Una buena capa común hace que una idea aprendida tarde pueda beneficiar a trabajo hecho antes. Esa propiedad es especialmente valiosa en un producto que está evolucionando: permite que el catálogo antiguo no quede congelado en la calidad del momento en que cada pieza fue creada.

Lo mejor de la arquitectura debería ser casi invisible al jugar

Un jugador no necesita saber si una transición la resolvió el motor, el host o un componente compartido. Necesita que el puzzle responda, que los controles tengan sentido y que pasar de un juego a otro no exija reaprender la aplicación. La arquitectura existe para producir esa sensación de naturalidad y para hacerla mantenible. Si una capa técnica se vuelve protagonista en la experiencia, probablemente ha filtrado una complejidad que debería permanecer interna.

Por eso este tema pertenece al Blog aunque hable de software. La historia interesante no es una lista de clases ni una guía de API. Es la relación entre una decisión interna y algo que cualquier persona puede percibir: variedad sin caos, consistencia sin uniformidad, crecimiento sin convertir el catálogo en un museo de páginas independientes. La arquitectura importa cuando permite que el producto sea más simple de usar a pesar de ser más complejo por dentro.

Un hogar compartido para juegos que no se parecen

Blupoli Puzzles puede reunir experiencias muy diferentes precisamente porque no exige que todas sean variantes de un mismo molde. El motor protege la identidad lógica. La presentación específica protege la interacción. El host y las capas comunes protegen la coherencia del producto. La metadata protege la capacidad de descubrir y organizar. Las verificaciones protegen la confianza para seguir cambiando. Ninguna de esas piezas tiene sentido aislada; juntas convierten una colección en una plataforma.

Si quieres ver el resultado en vez del diagrama, la mejor prueba es abrir Blupoli Puzzles y pasar entre juegos que obligan a pensar de formas distintas. La arquitectura cumple su cometido cuando esa transición se siente natural. Y si te interesa la historia de cómo esta estructura permitió ampliar el catálogo, continúa con «De un puzzle a una plataforma». El código cambia; la idea de fondo permanece: compartir lo común para dejar espacio a lo diferente.