Problema
Recrear la estructura básica de una partida de Balatro en el navegador: selección de baraja, niveles con blinds, juego de manos de póker, tienda, jokers y consumibles, con reglas del dominio bien separadas de la interfaz.
Solución
- React 19 + TypeScript + Vite para construir la aplicación web, con React Router para la navegación entre landing, menú, juego y rutas.
- CSS Modules para estilar los componentes de forma aislada.
- Hook
useGameStatepara centralizar el estado de la partida y las acciones que la modifican. - Módulos de lógica (
logic/) para aislar las reglas del dominio: barajas, detección de manos, jokers y cálculo de score. - React Context para compartir las preferencias de tema entre los componentes.
Funcionalidades
- Menú principal y selección visual de baraja con bonuses (Red, Blue y Yellow).
- Niveles con Small, Big y Boss Blind, y tabla de objetivos de cada uno.
- Juego y descarte de cartas con detección de manos de póker.
- Cálculo de chips y multiplicador, aplicando efectos de los jokers.
- Tienda con ofertas aleatorias y compra de jokers.
- Consumibles con tarots, planetas y Spectral Packs con selección de objetivos.
- Efectos de consumibles sobre cartas, jokers, niveles de manos y la mano completa.
- Bosses aleatorios sin repetición dentro de cada ciclo.
- Bosses con efectos propios y filtrado según el nivel de Ante.
- Guardado y carga automática de la partida en localStorage.
- Temas visuales persistidos en localStorage.
Decisiones Técnicas
El estado de la partida se centraliza en el hook useGameState, que devuelve los datos que la interfaz muestra y callbacks para cada acción del jugador. Los componentes no modifican el estado directamente: reciben una función como onPlayHand y el hook se encarga de actualizar el estado, lo que mantiene la lógica de negocio desacoplada de las vistas.
Las reglas del dominio se separan en módulos de logic/ — creación y barajado del mazo, detección de manos, blinds, catálogo de jokers, consumibles y cálculo de score — de modo que son independientes de la interfaz y pueden evolucionar por separado.
El guardado en localStorage persiste únicamente las fases reales de partida (blindSelect, playing o shop), de forma que el estado serializable permite continuar una partida tras recargar la página.





