6
Juegos

Wordle

Una recreación del popular juego de adivinanza de palabras Wordle, donde los jugadores intentan adivinar una palabra oculta de 5 letras en español en seis intentos.

Wordle

Descripción

Este proyecto es una implementación del popular juego de adivinanza de palabras Wordle, adaptado al idioma español. Los jugadores tienen seis intentos para adivinar una palabra oculta de cinco letras. Después de cada intento, el juego proporciona retroalimentación visual: las letras correctas en la posición correcta se marcan en verde, las letras correctas pero en la posición incorrecta se marcan en amarillo, y las letras que no están en la palabra se marcan en gris. El juego utiliza un diccionario en español para validar las palabras ingresadas, lo que asegura una experiencia auténtica y desafiante.

Características clave

  • Tablero de Juego Interactivo:

    Un grid visual donde los jugadores ingresan sus intentos de palabra.

  • Teclado Virtual Personalizado:

    Un teclado QWERTY en pantalla que permite la entrada de letras y acciones como 'BACKSPACE' y 'ENTER'. Las teclas cambian de color (verde, amarillo, gris) según la retroalimentación de las letras.

  • Validación de Palabras en Español:

    El juego verifica cada intento contra un extenso diccionario en español para asegurar que solo se ingresen palabras válidas. Esto incluye la normalización de acentos para una mejor experiencia.

  • Retroalimentación Visual Clara:

    Las celdas del tablero y las teclas del teclado se actualizan con colores (letter-correct, letter-present, letter-wrong) para indicar si una letra es correcta, está presente en la palabra pero en la posición incorrecta, o no está en la palabra.

  • Mensajes al Usuario:

    Un área de mensajes informa al jugador sobre palabras no válidas o el progreso del juego.

  • Manejo de 'Game Over' y Victoria:

    El juego detecta si el jugador adivina la palabra o agota sus intentos, mostrando un cuadro de diálogo (dialog) con el resultado y opciones para reiniciar.

  • Palabra Aleatoria Diaria (Simulada):

    Selecciona una palabra al azar de un conjunto de palabras válidas de 5 letras para cada nueva partida, garantizando rejugabilidad.

Aspectos destacados del código

  • Manipulación Dinámica del DOM:

    Creación y actualización programática del tablero del juego (createBoard) y el teclado virtual (createKeyboard), permitiendo una interfaz de usuario totalmente interactiva y responsiva.

  • const createBoard = () => {
        const board = document.querySelector<HTMLDivElement>(".board")!;
        if (!board) return;
        let html = "";
        for (let i = 0; i < 6; i++) {
            html += `
            <div class="row row-${i}">
                <div class="cell cell-0"></div>
                <div class="cell cell-1"></div>
                <div class="cell cell-2"></div>
                <div class="cell cell-3"></div>
                <div class="cell cell-4"></div>
            </div>`;
        }
        board.innerHTML = html;
    };
  • Carga y Filtrado de Diccionario Remoto:

    Utiliza fetch para cargar un diccionario en español desde un repositorio externo, y luego lo procesa (split, map, filter, deleteAccent) para obtener un conjunto de palabras válidas de 5 letras, crucial para la lógica del juego.

  • const loadValidWords = async () => {
        const response = await fetch(
            `https://raw.githubusercontent.com/titoBouzout/Dictionaries/refs/heads/master/Spanish.dic`
        );
        const data = await response.text();
        const words = data.split("\n");
        const onlyLetters = words.map((l) => l.replace(/\/.*/g, ""));
        const normalizeWords = onlyLetters.map((l) => deleteAccent(l));
        correctWords = [...normalizeWords];
        resetGame(true);
    };
  • Lógica de Verificación de Letras (Wordle Core):

    La función checkLetter implementa el algoritmo central de Wordle. Realiza dos pasadas para marcar las letras: primero, las correctas (verde), y luego las presentes pero mal posicionadas (amarillo), manejando duplicados de letras con un contador (letterCounts) para evitar sobremarcar.

  • const checkLetter = () => {
        const letterCounts: Record<string, number> = {};
        for (const letter of word.join("").toLowerCase()) {
            letterCounts[letter] = (letterCounts[letter] || 0) + 1;
        }
    
        for (let i = 0; i < currentWord.length; i++) {
            const guessedLetter = currentWord[i].letter.toLowerCase();
            if (guessedLetter === word[i].toLowerCase()) {
                currentWord[i].isCorrect = true;
                markLetterSelected(currentWord[i].letter, "correct");
                document.querySelector<HTMLDivElement>(`.row-${currentRow} .cell-${i}`)?.classList.add("letter-correct");
                letterCounts[guessedLetter]--;
            }
        }
    
        for (let i = 0; i < currentWord.length; i++) {
            if (currentWord[i].isCorrect) continue;
            const guessedLetter = currentWord[i].letter.toLowerCase();
            if (word.includes(guessedLetter) && letterCounts[guessedLetter] > 0) {
                currentWord[i].isPresent = true;
                markLetterSelected(currentWord[i].letter, "present");
                document.querySelector<HTMLDivElement>(`.row-${currentRow} .cell-${i}`)?.classList.add("letter-present");
                letterCounts[guessedLetter]--;
            } else {
                markLetterSelected(currentWord[i].letter, "wrong");
                document.querySelector<HTMLDivElement>(`.row-${currentRow} .cell-${i}`)?.classList.add("letter-wrong");
            }
        }
    };
  • Manejo de Eventos del Teclado Virtual:

    Un único forEach con un addEventListener maneja todos los clics en las teclas del teclado virtual. Discrimina entre letras, 'BACKSPACE' (para borrar) y 'ENTER' (para enviar la palabra), actualizando el estado del juego y la UI en consecuencia.

  • keys!.forEach((key) => {
        key.addEventListener("click", () => {
            const letter = key.id.split("-")[1];
            if (!letter) return;
            if (letter === "backspace" && currentColumn > 0) {
                // ... lógica para borrar letra ...
            } else if (letter === "enter") {
                if (currentColumn !== 5) return;
                if (!isWordValid()) {
                    // ... lógica para palabra inválida ...
                } else {
                    checkWord();
                    checkLetter();
                }
                // ... lógica para avanzar de fila ...
            } else {
                if (letter === "backspace") return; // Evita el doble procesamiento de backspace
                currentWord[currentColumn].letter = letter;
                const cell = document.querySelector<HTMLDivElement>(`.row-${currentRow} .cell-${currentColumn}`);
                if (!cell) return;
                cell.classList.add("new-letter");
                cell.textContent = letter.toUpperCase();
                currentColumn++;
            }
        });
    });
  • Normalización de Acentos:

    La función deleteAccent es crucial para que el juego funcione correctamente con palabras en español, eliminando los acentos de las letras para comparar de manera uniforme tanto la palabra objetivo como las entradas del usuario con el diccionario.

  • function deleteAccent(word: string): string {
        return word.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
    }
  • Manejo de Diálogos con DaisyUI (simulado):

    La función showDialog crea y gestiona un cuadro de diálogo modal que aparece al ganar o perder el juego, usando clases para controlar su visibilidad y comportamiento. Aunque el código no importa directamente DaisyUI, las clases como dialog-show y la estructura modular de dialog son consistentes con cómo DaisyUI maneja los modales.

  • const showDialog = (message: string[], type: "win" | "lose") => {
        createDialog(); // Función que crea el elemento <dialog>
        const dialog = document.querySelector<HTMLDialogElement>(".dialog")!;
        if (!dialog) return;
        dialog.classList.add("dialog-show"); // Simula la apertura con una clase
        dialog.open = true;
        // ... lógica para mostrar mensajes y botones ...
        button.addEventListener("mousedown", () => {
            dialog.close();
            dialog.open = false;
            dialog.classList.remove("dialog-show");
            resetGame(type === "win");
            dialog.remove(); // Elimina el elemento del DOM al cerrar
        });
    };

Desafíos técnicos

  • Gestión de Estado Complejo:

    Mantener el estado sincronizado entre la palabra actual, la fila y columna activas, y la retroalimentación de las letras (correctas, presentes, incorrectas) a través de múltiples interacciones del usuario y validaciones.

  • Implementación Robusta de la Lógica Wordle:

    Desarrollar el algoritmo checkLetter para manejar correctamente las letras duplicadas en la palabra objetivo y en la palabra adivinada, asegurando que la retroalimentación visual sea precisa.

  • Normalización de Palabras con Acentos:

    La necesidad de limpiar y normalizar las palabras del diccionario y las entradas del usuario (ej. eliminar acentos) para realizar comparaciones precisas y evitar errores de validación.

  • Optimización del Diccionario:

    Cargar y filtrar eficientemente un diccionario grande para solo usar palabras de 5 letras y sin caracteres especiales, lo cual impacta el rendimiento inicial de la aplicación.

  • Diseño de la Interfaz de Usuario Dinámica:

    Construir un tablero y un teclado que se actualizan visualmente de forma reactiva a las acciones del usuario y a la lógica del juego, incluyendo animaciones y cambios de color de las teclas.

Aprendizajes obtenidos

  • Arquitectura de Juegos Basados en el DOM:

    Comprendí cómo estructurar un juego interactivo utilizando la manipulación directa del DOM, gestionando el estado del juego y la UI de manera eficiente.

  • Algoritmos de Procesamiento de Cadenas (Strings):

    Adquirí experiencia en el manejo y la normalización de cadenas de texto (palabras con acentos), un aspecto crucial para aplicaciones multilingües.

  • Consumo de APIs/Recursos Externos:

    Aprendí a integrar datos de una fuente externa (diccionario) en una aplicación frontend, procesando la información para adaptarla a los requisitos del juego.

  • Patrones de Retroalimentación Visual:

    Reforcé mi conocimiento sobre cómo proporcionar retroalimentación visual clara e instantánea al usuario (colores en el tablero y teclado) para mejorar la experiencia de juego.

  • Gestión de Eventos Complejos:

    Practiqué la delegación y el manejo de eventos a nivel global para un teclado virtual, diferenciando entre diferentes tipos de teclas y sus acciones asociadas.

Posibles mejoras / Ideas a implementar

  • Sistema de Puntuación/Estadísticas:

    Añadir un seguimiento de partidas ganadas/perdidas, rachas de victorias y almacenamiento de estadísticas en localStorage.

  • Compartir Resultados:

    Implementar una función para compartir los resultados de la partida en redes sociales (como Wordle original).

  • Modo Oscuro/Temas:

    Permitir a los usuarios cambiar entre diferentes temas visuales utilizando las clases de temas de DaisyUI.

  • Palabra Diaria Única:

    Configurar un sistema para que la palabra objetivo sea la misma para todos los jugadores durante un día, actualizándose cada 24 horas.

  • Animaciones Mejoradas:

    Añadir más animaciones sutiles para el ingreso de letras, el volteo de las celdas o la aparición de mensajes.

  • Soporte Multilenguaje:

    Extender la funcionalidad para admitir diccionarios y palabras en otros idiomas.

Información

Juegos

2025-07-29

TypeScript

Etiquetas

gameword gamewordleguessing gamedom manipulationevent handlinglogicUI/UXdictionary