Assembly: Endgame es una implementación técnica del juego del ahorcado desarrollada con React 19 y Vite, donde el jugador dispone de 8 oportunidades para adivinar una palabra secreta antes de que "Assembly" sea la última opción restante. La aplicación integra gestión de estado reactiva mediante hooks, renderizado condicional basado en valores derivados, y accesibilidad ARIA para navegación por teclado. El proyecto resuelve la implementación de lógica de juego compleja en React, enfatizando patrones como componentes funcionales, manejo de eventos, y optimización de re-renders.
- 🎮 Gestión de Estado Reactiva: useState con Inicialización lazy y valores derivados para lógica de victoria/derrota
- 🎉 Efectos Visuales Dinámicos: Animación de confetti condicional y overlays con pseudo-elementos CSS
- 💀 Sistema de Feedback Progresivo: Mensajes de despedida temáticos por cada error cometido
- 🔄 Reinicio de Estado Puro: Función sin side-effects para resetear partida
- ♿ Accesibilidad Técnica: ARIA live regions y atributos para lectores de pantalla
- 📱 Diseño Responsive: Flexbox y media queries para adaptabilidad móvil
- 🎨 Estilos Modulares: Clases condicionales con clsx para optimización CSS
Vista previa mostrando la interfaz del juego con chips de lenguajes, palabra oculta, teclado virtual y estados de juego.
Para una experiencia interactiva, puedes ejecutar el proyecto localmente o visitar una demo la demo en vivo.
- Node.js (versión 16 o superior) - Runtime de JavaScript
- npm o yarn - Gestores de paquetes
# Clona el repositorio
git https://github.com/Seb-RM/React-Vite-Hangman-Game.git
cd React-Vite-Hangman-Game
# Instala dependencias (resuelve árbol de dependencias con npm)
npm install
# Inicia servidor de desarrollo (Vite con HMR)
npm run dev
# Construye para producción (minificación y optimización)
npm run build
# Vista previa de build de producción
npm run preview- Inicialización: El componente monta con
useStatelazy que selecciona palabra aleatoria - Interacción: Clics en botones del teclado actualizan array de letras adivinadas
- Lógica Derivada: Cálculos síncronos determinan estado sin re-renders innecesarios
- Renderizado Condicional: JSX basado en flags booleanos (isGameWon, isGameLost)
- Accesibilidad: Screen readers anuncian cambios vía ARIA live regions
- Propósito: Punto de entrada HTML que monta la aplicación React
- Conceptos Clave: Meta tags para responsividad, module scripts para ES6 imports
- Fragmentos Destacados:
<!doctype html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hangman Game</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>- Flujo de Datos: Browser carga HTML → ejecuta main.jsx → React hydrates #root
- API/DOM:
getElementByIdpara mounting; no manipulación directa post-hydration
- Propósito: Bootstrap de React con StrictMode para desarrollo
- Conceptos Clave: createRoot (React 18+), StrictMode para warnings de desarrollo
- Fragmentos Destacados:
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
)- Flujo de Datos: Import chain: main.jsx → App.jsx → assets → render tree
- API/DOM:
document.getElementByIdpara root element; ReactDOM maneja reconciliation
- Propósito: Componente funcional principal con lógica de juego compleja
- Conceptos Clave: useState con lazy init, derived state, event handlers, conditional rendering, ARIA accessibility
- Fragmentos Destacados:
// State management
const [currentWord, setCurrentWord] = useState(() => getRandomWord());
const [guessedLetters, setGuessedLetters] = useState([]);
// Derived computations (no side-effects)
const wrongGuessCount = guessedLetters.filter(letter => !currentWord.includes(letter)).length;
const isGameWon = currentWord.split("").every(letter => guessedLetters.includes(letter));
const isGameLost = wrongGuessCount >= languages.length - 1;
// Event handler (pure function)
function addGuessedLetter(letter) {
setGuessedLetters(prev => prev.includes(letter) ? prev : [...prev, letter]);
}
// Conditional rendering
const gameStatusClass = clsx("game-status", {
won: isGameWon,
lost: isGameLost,
farewell: !isGameOver && isLastGuessIncorrect,
});- Flujo de Datos: Input usuario (click) → addGuessedLetter → setState → re-render → recalculo valores derivados → update UI
- API/DOM: onClick handlers en botones; ARIA attributes para screen readers; clsx para className dinámico; Confetti mounts condicionalmente
- Propósito: Configuración visual como estructura de datos separada
- Conceptos Clave: Array de objetos para theming, principio DRY
- Fragmentos Destacados:
export const languages = [
{
name: "HTML",
backgroundColor: "#E2680F",
color: "#F9F4DA",
},
// ... 8 lenguajes total
];- Flujo de Datos: Importado en App.jsx; mapeado a elementos JSX con styling condicional
- API/DOM: Inline styles via style prop; backgroundColor/color para feedback visual
- Propósito: Utilidades puras para lógica de negocio
- Conceptos Clave: Funciones puras, Math.random para pseudo-aleatoriedad, manipulación de arrays
- Fragmentos Destacados:
export function getRandomWord() {
const randomIndex = Math.floor(Math.random() * words.length);
return words[randomIndex];
}
export function getFarewellText(language) {
const options = [
`Farewell, ${language}`,
`Adios, ${language}`,
`R.I.P., ${language}`,
// ... 12 opciones
];
const randomIndex = Math.floor(Math.random() * options.length);
return options[randomIndex];
}- Flujo de Datos: getRandomWord llamado en mount y reset; getFarewellText en errores
- API/DOM: Sin interacción DOM; outputs strings para interpolación JSX
- Propósito: Dataset estático simulando API externa
- Conceptos Clave: Array grande para variedad, imports estáticos
- Fragmentos Destacados:
export const words = [
"about", "account", "across", // ... ~400 palabras
];- Flujo de Datos: Importado por utils.js; indexado aleatoriamente
- API/DOM: Data consumida por lógica de juego; sin DOM directo
- Propósito: Estilos globales con CSS moderno y accesibilidad
- Conceptos Clave: Flexbox para layouts, pseudo-elementos para overlays, media queries para responsive
- Fragmentos Destacados:
/* Conditional styling via className */
section.keyboard>button.correct {
background-color: #10A95B;
}
section.keyboard>button.wrong {
background-color: #EC5D49;
}
/* Accessibility: screen reader only */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
/* ... clip properties */
}- Flujo de Datos: Aplicado globalmente; React aplica clases dinámicamente basado en estado
- API/DOM: CSSOM manipulado indirectamente via cambios className; pseudo-elements (::before) para skulls
React-Vite-Hangman-Game/
├── index.html # Entry point estático
├── src/
│ ├── main.jsx # Bootstrap React
│ ├── App.jsx # Componente stateful (lógica principal)
│ ├── index.css # Estilos globales
│ └── assets/ # Data y utils estáticos
│ ├── languages.js # Config visual
│ ├── utils.js # Funciones puras
│ └── words.js # Dataset palabras
└── package.json # Árbol dependencias
Patrones Implementados:
- Componente Funcional: Sin class components, todo hooks
- Estado Derivado: Valores computados desde estado raw para evitar redundancia
- Updates Event-Driven: Cambios imperativos de estado trigger re-renders declarativos
- Separación de Concerns: Data, lógica, estilos en archivos separados
- Accesibilidad First: ARIA y HTML semántico integrado
