Creamos Cards4.net para que las personas puedan abrir un juego de cartas y empezar a jugar sin necesidad de cuenta o anuncios. Una vez que el juego está en marcha, deberían poder deshacer un movimiento o volver a él más tarde sin preocuparse por si el tablero ha cambiado. Una partida guardada debe preservar sus decisiones malas tan fielmente como las buenas.

Cumplir con esa expectativa ha definido cómo usamos Phoenix y Elixir en el backend, React en el navegador y PostgreSQL para la persistencia. Las reglas, controles y partidas guardadas necesitan estar de acuerdo sobre lo que ha hecho el jugador.

Mantener el estado del juego en el servidor

En Klondike, mover una carta puede revelar y voltear la carta que está debajo. Si el jugador presiona 'Deshacer', ambos cambios deben ser revertidos. Si luego abandona el juego, la posición guardada debe reflejar el tablero al que volvió. Mantenemos el estado del juego en el servidor para que la manipulación de movimientos, el deshacer y la persistencia funcionen desde la misma representación de esa posición.

Cuando un jugador suelta una carta, el navegador envía el movimiento solicitado a nuestro backend Phoenix, donde las reglas lo verifican contra el estado actual. Si el movimiento es legal, el servidor lo aplica, registra la posición anterior para el 'Deshacer' y devuelve el tablero actualizado. El navegador puede entonces mostrar el resultado, incluyendo cualquier carta que haya sido revelada. Debido a que el servidor maneja esos cambios juntos, la interfaz no necesita reconstruir las posiciones anteriores a partir de lo que se veía en pantalla.

Para juegos multijugador, ese arreglo también nos permite coordinar una mesa sin depender de que el navegador del jugador decida qué sucedió. Los canales Phoenix transmiten acciones al servidor y entregan las actualizaciones resultantes a los jugadores. Dado que el servidor mantiene el estado completo del juego, puede enviar a cada asiento la información que el jugador puede ver mientras mantiene las otras manos fuera de la respuesta.

Conservar una partida al terminar la sesión

Durante el juego, cada sesión de solitario mantiene su posición en un GenServer, un proceso de Elixir que maneja llamadas entrantes en secuencia. La sesión delega las comprobaciones de reglas al módulo de juego apropiado, permitiendo a Klondike, FreeCell y Spider compartir el código para manejar sesiones y guardados.

Ese proceso solo mantiene la posición en memoria, por lo que también escribimos instantáneas en PostgreSQL. Cuando una solicitud llega a una sesión que ha terminado, la aplicación puede cargar su estado guardado en una nueva sesión. La recuperación ocurre a través de ese código; el supervisor no reinicia automáticamente estos procesos temporales después de un fallo.

Programamos las escrituras por separado del manejo de movimientos para evitar esperar a la base de datos en cada movimiento. Como resultado, la posición en memoria puede estar por delante de la última guardada con éxito. La prueba de recuperación implica comprobar qué se escribió realmente y qué se puede restaurar, incluyendo el historial de Deshacer.

Controles que responden al jugador

Colocar las reglas en el servidor significa que cada movimiento necesita una petición de red antes de ser aceptado. Esto puede retrasar el resultado en una conexión lenta e impide jugar sin conexión. Un juego de solitario que funcione completamente en el navegador evitaría esta dependencia. Hemos aceptado esto para que la validación de reglas, el deshacer y el progreso guardado puedan usar la misma implementación del backend.

La interfaz aún necesita responder mientras un jugador arrastra una carta. Nuestros tableros usan React y TypeScript, con Vite para compilar el frontend. React maneja el tablero y el estado de la interacción, mientras que un bucle requestAnimationFrame mueve la vista previa del arrastre directamente. Esto mantiene la vista previa siguiendo el puntero sin necesidad de una actualización del estado de React para cada fotograma, aunque aceptar el movimiento al soltar la carta aún requiere una respuesta del servidor.

En un teléfono, los propios gestos necesita atención. Las cartas son más pequeñas, las columnas pueden ser largas y un toque puede significar seleccionar una carta o iniciar un arrastre. Probamos estas interacciones por separado de las reglas porque saber que una pila es legal para mover nos dice poco sobre cuán fácilmente alguien puede tomarla. Los gestos cancelados y los intentos de soltar cartas en destinos no válidos también necesitan dejar el tablero en un estado que el jugador entienda.

Reproducir lo que salió mal

Cuando un jugador reporta que el tablero se comportó de manera inesperada, necesitamos recrear la posición que estaba jugando. Los juegos de solitario soportados se generan a partir de semillas, por lo que el mismo juego, variante y semilla producen la misma disposición inicial bajo la misma implementación.

Para un problema que ocurre más tarde, también necesitamos los movimientos que lo preceden. Una captura de pantalla puede mostrar qué se ve mal, mientras que la semilla y la secuencia de movimientos nos permiten seguir el juego hasta ese punto. Mantener esos datos de entrada con una prueba de regresión nos da una forma de verificar el comportamiento nuevamente después de una corrección. También significa que los cambios en el barajado o en el código de reparto necesitan comprobaciones de compatibilidad si las semillas antiguas deben seguir produciendo las mismas disposiciones.

Usamos informes de errores y mediciones de rendimiento para investigar fallos y páginas lentas también. Estos tienen una finalidad distinta de las cifras de tráfico en Plausible, que cargamos solo después del consentimiento de análisis. Las vistas de página pueden decirnos que alguien visitó; no pueden establecer que un juego se guardó o restauró correctamente. La política de privacidad describe los datos recopilados por estos sistemas separados.

Lo que haríamos antes

En otro proyecto de juego de cartas, completaríamos primero, para un solo juego, todo el ciclo de jugar, deshacer, guardar y reanudar antes de expandir el catálogo. Lo mantendríamos como una prueba usando un reparto fijo, así los cambios posteriores podrían verificarse contra el mismo punto de partida. Esto nos daría una forma repetible de detectar un cambio que haga que un movimiento funcione en pantalla pero rompa la posición restaurada después de una recarga. En un sitio al que se vuelve para jugar entre otras actividades, conviene dedicar tiempo desde el principio a que reanudar una partida funcione bien.

Preguntas frecuentes

¿Qué tecnologías utiliza Cards4.net?

El backend utiliza Phoenix y Elixir con PostgreSQL. El frontend utiliza React, TypeScript y Vite. Las acciones de solitario utilizan REST, mientras que las mesas multijugador utilizan Phoenix Channels. Los tableros de juego no utilizan LiveView.

¿Puedo jugar sin cuenta?

Sí. El juego casual está disponible para invitados. Si quieres una cuenta, el registro utiliza un enlace de correo electrónico único. El sitio no te pide que crees una contraseña.

¿Pueden los juegos funcionar sin conexión?

No. Los movimientos se validan en el servidor, por lo que el juego requiere conexión a internet.

¿Qué herramientas de análisis utiliza el sitio?

Plausible se carga después del consentimiento de análisis. La aplicación también tiene informes de errores y monitoreo de rendimiento separados. Consulta la política de privacidad para más detalles.

Lecturas relacionadas