Baza: la Cuatrola con sus reglas intactas, en medio megabyte
Reconstruí la Cuatrola para YouTube Playables: una sola función de legalidad para humanos y bots, arte y sonido generados por código, seis idiomas y una Mano del día que el motor resuelve de forma exacta. Esto es lo que me enseñó la mesa y ningún reglamento habría podido.
Hay un juego de cartas de mi tierra que casi todo el mundo aprende en la mesa de la cocina y medio olvida. Veinte cartas, cuatro jugadores, cinco bazas. Una regla lo define todo: si puedes montar, montas. Y no existe un canon escrito. El catálogo de Fournier, con treinta y cinco juegos, tiene el Tute y el Guiñote pero no la Cuatrola; en Wikipedia no hay artículo; lo más parecido a unas reglas oficiales son las bases de torneos municipales, y se contradicen de un pueblo al siguiente.
Quería una versión con las reglas intactas y sin los deberes. Y quería que cupiese en YouTube Playables, que significa una descarga diminuta, un juego que arranca dentro de un WebView oculto en cualquier móvil y ni una sola llamada de red. Un desarrollador, todas las disciplinas.
Baza salió el 23 de agosto, dieciséis días después del primer commit, y seguí puliéndolo con quienes lo probaban durante dos semanas más. También está en itch.io. Este artículo va de las partes que fue interesante construir: el motor de reglas, la Mano del día resuelta, el arte que se distribuye como código y las reglas que tuve mal hasta que jugadores de verdad me corrigieron.

Primero las restricciones
YouTube Playables es un anfitrión estricto. El index.html tiene que estar en la raíz del zip y su script del SDK debe ser lo primero que cargue. Llamas a firstFrameReady() cuando hay una pantalla de carga visible y a gameReady() solo cuando el menú ya responde; llamarlo antes suspende la certificación. Cada archivo debería pesar menos de 512 KiB, el paquete inicial debe ser pequeño y cualquier petición a una URL fuera del paquete, incluida una fuente o un ping de analítica, es un rechazo. El juego se carga primero en un WebView con un viewport de 0×0, así que la maquetación tiene que sobrevivir a eso también.
Apunté esas restricciones antes de escribir una línea de código y fueron ellas las que decidieron la arquitectura. Sin fuentes de un CDN, había que empaquetar un subconjunto de Alegreya. Sin recursos remotos, la pregunta pasó a ser qué recursos necesitaba en realidad. La respuesta resultó ser casi ninguno.
El paquete alojado pesa hoy 1,7 MiB en bruto y 0,60 MiB con gzip. Phaser son 355 KB de eso; mi código, 52 KB. La imagen para compartir y el icono táctil nunca se descargan durante la partida, así que al jugador le llegan unos 0,45 MiB antes de que aparezca el menú.
Una sola función decide qué es legal
El motor es TypeScript puro sin un solo import de Phaser, probado en Node. La regla que hace el juego cabe en unas cincuenta líneas:
export function legal(hand: readonly Card[], trickCards: readonly Card[], trump: Suit): LegalResult {
if (trickCards.length === 0) return { cards: [...hand], reason: 'FREE' };
const lead = trickCards[0].suit;
const winner = trickCards[winningIndex(trickCards, trump)];
const inLead = hand.filter((c) => c.suit === lead);
if (inLead.length > 0) {
if (winner.suit === lead) {
const beat = inLead.filter((c) => c.strength > winner.strength);
if (beat.length > 0) return { cards: beat, reason: 'MUST_BEAT' };
}
return { cards: inLead, reason: 'MUST_FOLLOW' };
}
const trumps = hand.filter((c) => c.suit === trump);
if (trumps.length > 0) {
if (winner.suit === trump) {
const over = trumps.filter((c) => c.strength > winner.strength);
if (over.length > 0) return { cards: over, reason: 'MUST_OVERTRUMP' };
return { cards: [...hand], reason: 'FREE' };
}
return { cards: trumps, reason: 'MUST_TRUMP' };
}
return { cards: [...hand], reason: 'FREE' };
}
Esa función es toda la filosofía del diseño. Devuelve el conjunto legal y un código de motivo. La mesa usa el conjunto para apagar las cartas que no puedes jugar y el código para escribir una línea: «Tienes que montar el 3★ de copas si puedes». Los tres bots eligen únicamente de ese mismo conjunto. No hay un segundo sitio en el código donde se decida la legalidad, y un test de fuzzing juega 10 000 manos aleatorias comprobando que cada jugada de cada bot está dentro.
El resto del motor sigue la misma forma: resolución de bazas, cantes, subasta con su restricción por objetivo, puntuación con la regla de empate y un RNG con semilla para que cada reparto y cada jugada de bot sean reproducibles. Las reglas normativas viven en un documento de especificación, cada decisión marcada como [decision] tiene su test y la suite va por 303 tests. Las escenas de Phaser pintan el estado del motor y reenvían los toques. Nunca calculan una regla.
Una baraja que se explica sola
La mayor barrera para quien nunca ha visto una baraja española es que un tres gana a un rey y vale diez puntos. Así que cada carta imprime su propia fuerza como un numeral con estrella y su valor en puntos en la esquina. Solo las dos figuras llevan personaje, el rey y la reina, porque son la pareja del cante y el dibujo explica la regla. Los palos se distinguen por la forma, no solo por el color.

La primera mano es una mano real con nueve líneas de guía, de una en una, nunca un modal. El reparto lleva semilla para que todas las líneas salten, y un test fija esa semilla. Después la guía se apaga y la línea con el motivo sobre las cartas apagadas se queda para siempre. Las declaraciones se desbloquean jugando, así que la ronda de subasta nunca es lo primero que ve un jugador nuevo.
El panel de revisión me costó una discusión conmigo mismo. La regla tradicional dice que solo puedes mirar la última baza. Pero los bots registran qué asiento jugó qué carta en qué baza para calcular fallos, y ese conocimiento ya lo tienen gratis los jugadores de la mesa. El panel le concede al humano exactamente eso y nada más: las cinco bazas en orden de juego con una corona sobre cada ganador, y las cartas que aún no se han visto. Nunca dónde están.
El arte y el sonido son código
La build no tiene imágenes ni archivos de audio. Las veinte cartas, los reversos, los emblemas, el tapete, el viñeteado y los medallones de los asientos se dibujan en un canvas al arrancar. Los once efectos de sonido son osciladores de WebAudio con recetas cortas:
marriage: [
{ f: 523, at: 0, len: 0.12, type: 'sine', g: 0.13 },
{ f: 659, at: 0.1, len: 0.12, type: 'sine', g: 0.13 },
{ f: 784, at: 0.2, len: 0.2, type: 'sine', g: 0.13 },
],
Empezó como una decisión de tamaño y acabó siendo una regla para todo lo que rodea al juego. La portada de itch.io, el logotipo, la tarjeta de vista previa para redes y las cartelas finales del tráiler los dibuja el mismo código de texturas en una página solo de desarrollo, con más supermuestreo. Cuando arreglé la tiara de la reina, todo el material promocional se actualizó con ella.
El audio del tráiler es el del propio juego: parcheé AudioNode.connect al arrancar para desviar todo lo que llega al destino hacia un MediaStreamDestination, y arranqué un MediaRecorder en el mismo fotograma en que empieza el corte de vídeo. Nada de música de stock. Los clics discretos de la mesa son la identidad del juego.
La mesa manda sobre el libro
Dos reglas cambiaron después del lanzamiento, y las dos cambiaron porque los jugadores me dijeron que estaban mal.
La primera: qué pasa cuando alguien ha fallado y tú no puedes montar ese triunfo. Mi regla original te obligaba a soltar un triunfo más bajo de todos modos, «por coherencia con el Tute». Quienes probaban el juego protestaban cada vez que ocurría. Fui a las fuentes: cinco de cinco referencias sobre la Cuatrola dicen que puedes echar la carta que quieras y guardarte el pinte, y hasta la página de Tute de Ludoteka recoge la misma obligación condicional. La regla estricta no aparecía en ninguna parte. La revertí, reescribí la especificación con las citas y luego tuve que arreglar la inferencia de fallos de los bots, que con la nueva regla habría estado mal sin avisar: un descarte que no es triunfo solo demuestra que no te quedan triunfos si en ese momento no se había jugado ninguno.
La segunda: si una pareja puede cantar dos veces en la misma baza. Ludoteka dice que sí; las reglas del Tute de Fournier dicen que solo un cante cada vez. Pregunté en el grupo de WhatsApp de la familia. Ellos cantan uno por baza ganada, las cuarenta primero y las veinte en la siguiente baza que ganes, aunque las dos parejas las tengan jugadores distintos. Eso es lo que va en el juego. Y es además la regla más interesante: la defensa puede matar el segundo cante llevándose las bazas de en medio.
Una tercera regla la medí y la rechacé. Algunas bases de torneo permiten tirarse con una mano de 15 puntos o menos. En 50 000 manos simuladas, una mano tan floja aparece en el 3,2 % de los repartos, y su pareja gana igualmente el 38,7 % de esas manos. Es una regla de misericordia para el ritmo de barajar a mano, no de equilibrio, y borraría precisamente las manos en las que se nota la habilidad defensiva.
La Mano del día se resuelve, no se puntúa
La primera versión de la Mano del día puntuaba los puntos de cartas de tu pareja. Alguien que la probaba señaló que eso ignoraba por completo el Solo, la Cuatrola y la Quintola, y que había días en que ibas a perder hicieras lo que hicieras. Las dos cosas eran ciertas, y la segunda se puede medir.
Este juego es lo bastante pequeño como para resolverlo por fuerza bruta. Cinco bazas, y legal() es tan severa que en la mayoría de turnos tienes una o dos cartas legales. Los bots de la Mano del día no llevan aleatoriedad, así que una línea a través de la mano la determinan por completo las decisiones del humano. En un año de repartos diarios el árbol tiene una mediana de 38 líneas completas y un peor caso de 480, enumeradas en menos de 20 ms. No se estima nada.
Así que ahora la Mano del día te puntúa de 0 a 100 según lo bien que se jugó la mano, como tu percentil entre todas las líneas que podía haber seguido, con la mejor fijada en 100. Las líneas se ordenan primero por el vuelco en puntos de partida, de modo que una Cuatrola hecha cuenta cuatro veces más que una mano tranquila y una Quintola fallida cuesta cinco, y después por el margen de cartas.
El hallazgo que dio la razón a la queja: el 39 % de los días, el mejor resultado posible sigue siendo una derrota. Esos días ahora puntúan 100 si pierdes tan bien como se podía perder. Y como el árbol está enumerado entero, la autopsia puede señalar la decisión exacta: «Baza 3: el 4★ de espadas valía 11 puntos más». No «la primera desviación de la mejor línea», que estaría mal cuando varias líneas empatan, sino la primera decisión en la que bajó el techo.
Mide justo aquello de lo que estás seguro
El menú decía que una partida corta duraba entre cinco y ocho minutos, y una clásica entre diez y quince. Nunca lo había comprobado. Cuando lo hice, con 2000 partidas simuladas por objetivo, una partida a 11 dura una mediana de 16 manos y una a 21, de 32. Unos quince y treinta minutos. Un error de tres a cuatro veces, porque casi todas las manos valen un punto y se alternan entre las parejas.
Ese número se cargó una funcionalidad. La partida clásica estaba bloqueada hasta terminar una corta, que era pedir un cuarto de hora para desbloquear media hora. Nadie llegó, yo incluido, tras semanas jugando. El bloqueo desapareció y los botones llevan su duración real.
También probé un objetivo más corto, a 7, y los datos se lo cargaron igual. La subasta va atada al objetivo: no puedes declarar un contrato cuyo valor te lleve más allá, así que a 7 una Quintola solo es legal a 0-0. Once es más o menos el suelo en el que declarar todavía respira.
Un juego, tres tableros
Playables exige que se pueda jugar en horizontal. Una columna vertical con franjas laterales en un portátil dibujaba las cartas a 75 píxeles CSS con dos tercios de la pantalla vacíos. Así que hay dos tableros: la mesa vertical de 720×1280 y un tablero ancho de 1280×800 que la cámara dibuja con un zoom de 1,6× para encajarlo. El abanico de la compañera pasa junto a la baza en vez de encima, el panel de subasta se coloca sobre el área de la baza vacía, y la rotación es una instantánea y un reinicio de escena por el mismo camino con el que ya se reanuda una partida guardada. Espera a un momento tranquilo, así que una carta en vuelo siempre aterriza antes.

Los móviles alargados recibieron el espejo de esa palanca. Un móvil 20:9 dibujaba el mundo de 720 de ancho a escala 1, con el 30 % de la pantalla en tapete y las etiquetas de asiento a 8 píxeles CSS. Ahora el tablero vertical se estrecha hasta la forma de la propia ventana, con un mínimo de 600, y la cámara lo dibuja a 1,2×. Cada x de la maquetación ya era el eje central, un margen o un desplazamiento desde uno de ellos, así que nada cambió en 16:9, y un test lo fija al píxel.
La otra lección de Phaser que merece la pena contar: scene.start(key) sin datos conserva los datos de la visita anterior. A mi escena de ajustes se llegaba desde el menú de pausa con {returnTo: 'pause'} y desde el menú principal sin nada, y la segunda llamada heredaba la bandera de la primera. Al volver de ajustes aparecía una capa de pausa sobre una mesa detenida, y «Continuar» reanudaba una escena ya apagada, que Phaser marca como activa sin ejecutar nunca create(). Una pantalla verde sin un solo botón, a cuatro toques del menú. Pasa datos siempre, aunque sea {}.
Seis idiomas, seis vocabularios
Inglés, español, francés, alemán, portugués e italiano van en la misma build. La regla que los hace buenos: cada uno de esos idiomas tiene una tradición viva de juegos de bazas, así que el juego toma prestadas las palabras de esa tradición en lugar de traducir el inglés. Una baza es un pli en francés por la Belote, un Stich en alemán por el Skat, una vaza en portugués por la Sueca, una presa en italiano por la Briscola. Dos casualidades felices: el Schnapsen ya puntúa la pareja de rey y reina como Zwanziger (20) y la de triunfo como Vierziger (40), que son exactamente los valores de los cantes de este juego, y el dix de der de la Belote ya es el nombre de los diez de últimas.
La puntuación forma parte de la traducción. Diecisiete rayas no pintaban nada en las tablas que no eran inglesas: francés, alemán, italiano y portugués marcan esa pausa con un guion medio con espacios, y el español no usa una raya suelta para eso en absoluto. Ahora lo comprueba un test, porque es invisible para quien lee en inglés y evidente para todos los demás.
El turco es un mercado activo de Playables y no va incluido, porque el subconjunto de fuente empaquetado no tiene ş, ğ ni İ. Eso es trabajo de tipografía, no de traducción, y prefiero publicar seis tablas buenas a siete con Arial colándose a media palabra.
Dónde está
El juego está completo: 303 tests, un harness de navegador que juega la build minificada de principio a fin con ratón y con táctil en los tres tableros, y la Test Suite de Playables de Google superada. Está en baza.blugart.dev sobre Cloudflare Pages y en itch.io. El portal de Playables es solo por invitación; el formulario de interés se envió el 23 de agosto y el silencio es el resultado por defecto, así que sigo construyendo en vez de esperar.
CrazyGames lo rechazó con su frase comodín sobre la calidad general. Sus directrices priman el arte ilustrado, la música y un bucle que se entienda en diez segundos; Baza es arte procedural, sin música y con una primera mano que enseña las reglas. Retiré su SDK ese mismo día en vez de remodelar el juego alrededor de una plataforma que pone objeciones a su identidad. Hay preparada una solicitud para Coolmath Games, porque su brief describe un juego tranquilo de pensar y ellos pagan una licencia de alojamiento.
Buena parte del tecleo la hizo Claude Code. Cada regla se decidió en la mesa.
Lo que aprendí
Escribe las restricciones antes que el código. Las reglas de Playables decidieron la arquitectura, y el presupuesto de tamaño se convirtió en la dirección de arte.
Una función de legalidad, para todos. Las cartas apagadas del humano y las decisiones de los bots salen de las mismas cincuenta líneas. Los bugs de ahí los encuentra el fuzzing, no los jugadores.
La mesa viva manda sobre la fuente escrita. Las dos reglas que tuve mal eran decisiones «por coherencia» que ninguna fuente respaldaba. Pregunta a quien juega.
Mide justo aquello de lo que estás seguro. La duración de las partidas, los días imposibles de la Mano del día, la regla de tirarse: cada número que por fin comprobé cambió una decisión.
Un informe limpio de una comprobación que nunca ha pillado nada no es una prueba. Mi primer barrido de textos solapados no encontró nada porque los Graphics de Phaser no informan de sus límites. Mi primera limpieza de despliegues dijo haber borrado 23 builds y no borró ninguna. Demuestra que el detector falla antes de fiarte de que aprueba.
Juega una mano en baza.blugart.dev. Se tarda un minuto.