Saltar al contenido
Proyectos

Dominó dominicano · iPhone, web, iMessage

App Store v1.1.0 Disponible playcapi.com

Capi

Juega 1 contra 1 o por parejas con tus panas, en tiempo real en iPhone y la web o por turnos en iMessage. Sin crear cuenta.

Una partida 1 contra 1 de Capi en iPhone durante una ronda, con ambos puntajes en la parte superior.
App de iPhone
Rol
Solo: diseño, código y publicación
Período
Desde feb 2026
Stack
TypeScript, Next.js, React Native, Expo, Swift, Supabase, Postgres, Vitest

En el App Store, versión 1.1.0. Disponible en playcapi.com. Código público en GitHub. La extensión de iMessage viene con la app de iPhone. Estado verificado el 22 oct de 2026.

En números

~1,240

líneas en el motor, con 0 dependencias de producción

960

finales con semilla, verificados para que siempre haya una jugada legal y la partida termine

8

anchos de pantalla, de 320 a 1280 px, sin fichas montadas después de ninguna jugada

Fig. 1 El camino de una jugada
  • Cliente web, iPhone o iMessage
  • API ruta de Next.js
  • Postgres Supabase
  • Realtime Supabase
  1. Cliente a API 01 POST /api/games/[id]/move La jugada y el último state_version leído por el cliente.
  2. API a Postgres 02 Lee la mesa
  3. API a Cliente 03 409 si state_version cambió El cliente solicita el estado más reciente.
  4. API 04 applyMove Usa el mismo motor de reglas que la web y la app de iPhone: unas 1,240 líneas y 0 dependencias en tiempo de ejecución.
  5. API a Postgres 05 Escribe si state_version no cambió Solo la API puede escribir. Las políticas de acceso no permiten inserciones ni actualizaciones anónimas.
  6. Postgres a Realtime 06 postgres_changes La fila actualizada.
  7. Realtime a Cliente 07 Envía el nuevo estado a cada jugador
  8. Cliente 08 Resincronización Si no llegan actualizaciones, el cliente solicita el estado a los 10 segundos y repite cada 10 segundos durante un máximo de 30 minutos.

La idea

Capi lleva el dominó dominicano a internet. Elige un nombre, comparte un código o enlace y juega 1 contra 1 o 2 contra 2 con tus panas. Sin crear una cuenta.

Desde la versión 1.1 también puedes empezar una partida en iMessage, con invitaciones y turnos que llegan como burbujas en el chat.

Lo que construí

Escribí las reglas en un solo paquete de TypeScript: unas 1,240 líneas sin dependencias de producción. La web, la API del servidor y la app de iPhone lo importan, para que la capicúa, el tranque, la puntuación y la posición de las fichas funcionen igual. En producción, el reparto usa crypto.getRandomValues. Las pruebas pueden repetir cualquier partida a partir de una semilla.

La web usa Next.js y Supabase. Postgres guarda el estado de la partida y solo la API puede escribirlo, después de que una migración eliminara todas las políticas anónimas de inserción y actualización. Supabase Realtime entrega las jugadas; las mesas sin actividad también actualizan su estado cada 10 segundos. La app de iPhone usa Expo y React Native con la misma API. Una extensión de iMessage en Swift crea las burbujas y muestra la partida web dentro de Mensajes.

Retos técnicos

  1. 01 Validar cada jugada

    La API valida cada jugada con el motor y la guarda solo si state_version coincide con lo que leyó el cliente. Una jugada basada en un estado anterior recibe HTTP 409, para que dos jugadas simultáneas no dañen la mesa.

    Dónde está en el código: apps/web/src/app/api/games/[id]/move/route.ts (se abre en otra pestaña)

  2. 02 Recuperar una mesa trabada

    En septiembre, un jugador reportó una partida trabada por una actualización en tiempo real que no llegó. Agregué una consulta del estado tras 10 segundos sin actividad, que se repite cada 10 segundos por hasta 30 minutos. El cliente comparte el estado recibido por el canal de chat de la mesa, para que los jugadores de la versión 1.0 también se pongan al día. Un nuevo conjunto de pruebas juega 960 partidas con semilla y verifica que cada mesa tenga una acción legal y termine.

    Dónde está en el código: apps/web/src/hooks/useRealtimeGame.ts (se abre en otra pestaña)

  3. 03 La misma mesa en cualquier pantalla

    La fila de fichas dobla en los bordes de la mesa y tiene que caber en pantallas de 320 a 1280 px. Una prueba de propiedades juega 200 partidas con el reducer real. Después de cada jugada, revisa que no haya fichas montadas en 8 anchos y 2 niveles de diseño.

    Dónde está en el código: packages/engine/__tests__/boardLayout.overlap.test.ts (se abre en otra pestaña)

  4. 04 La misma mesa dentro de Mensajes

    La extensión nativa en Swift se encarga de las burbujas, la identidad del jugador y los asientos. Muestra playcapi.com en una vista web y usa el mismo motor y la misma API que la app y la web, sin duplicar las reglas. Un manejador de mensajes comunica las jugadas y los finales de ronda y de partida a la extensión, que los convierte en burbujas. Cada partida usa una sola sesión de Mensajes, para que cada burbuja nueva reemplace la anterior.

    Dónde está en el código: apps/mobile/targets/messages/MessagesViewController.swift (se abre en otra pestaña)

Antes y después

AntesDespuésFuente
Recuperación cuando se pierde una actualización en tiempo realUna actualización perdida podía dejar una mesa donde nadie podía jugar.Resincronización cada 10 segundos, retransmisión por el chat de la mesa y 960 finales con semilla en las pruebas.Commit f5cadf0 y los commits del arreglo del 30 de septiembre de 2026

Lo próximo

Próxima versión: mostrar tu diseño de fichas a los demás jugadores y verlo completo antes de comprar. En iMessage, elegir mesa y partidas de 100 o 200 puntos, además de dos correcciones al unirse a una partida.

Una partida 1 contra 1 de Capi en Mensajes para iPhone, con los botones New game y Open in Capi encima de la mesa.
iMessage
Una partida 2 contra 2 de Capi en la mesa del Colmado.
App de iPhone
La sala de espera de Capi en iPhone, con el código de invitación y el botón para copiar el enlace.
App de iPhone

Pruébalo

Enfoques compartidos