En este artículo te mostraré cómo programar Simon en JavaScript, HTML y un puñado de CSS. Este es un ejemplo sensacional para aprender conceptos elementales en la programación.
Hacer juegos es una buena técnica para aprender programación, porque tienes que usar creatividad y estructuras ingeniosas, además de mantenerte entretenido hasta que completes el reto. ¿Te parece interesante? Sigue leyendo.
Simón (juego)
Este es un juego que originalmente tiene cuatro colores que se encienden en secuencia, la cual aumenta conforme avanzamos en el luego. El objetivo es que el jugador presione los colores en el orden presentado por el juego, y cada vez que acierta se agrega un color más a la secuencia. Es un juego de memorizar secuencias.
Cómo programar Simon en JavaScript
Antes de entrar al código, es necesario que sentemos los bases de lo que vamos a programar. Si no entendemos lo que tenemos que hacer, es difícil que consigamos nuestro objetivo. Así que vamos a desarrollar este ejercicio de la siguiente manera:
Objetivos
- Al jugador se le presentarán 5 botones, cada uno con un color diferente.
- El juego debe mostrar al usuario la secuencia de colores activa, de tal manera que el jugador la pueda memorizar.
- El jugador debe presionar los botones en el orden que le fue mostrado.
- Si el jugador acierta la secuencia completa, el juego repite la secuencia activa y agrega un nivel más.
- Si el jugador falla, el juego termina.
- El jugador gana cuando repite la secuencia completa en todos los niveles.
- Cada vez que juguemos se debe generar una secuencia diferente.
Paso #1: Crear los botones
Como este ejemplo debe ser sencillo de programar, nos vamos a conformar con 5 botones, cada uno con el color que le corresponde. Vamos a colocarlos dentro de un contenedor para que podamos manipular su apariencia de una forma sencilla.
En un archivo HTML coloca lo siguiente:
<div class="contenedor"> <div class="botones"> <button id="verde" data-color="verde"> </button> <button id="amarillo" data-color="amarillo"> </button> <button id="azul" data-color="azul"> </button> <button id="rojo" data-color="rojo"> </button> <button id="blanco" data-color="blanco"> </button> </div> <button id="btnEmpezar" class="principal" onclick="iniciarJuego()">Empezar</button> </div>
Si tienes dudas sobre cómo crear el HTML, al final de este artículo te daré los archivos completos para que puedas practicar y hacer tus modificaciones.
Observa que en nuestro contenedor llamado “botones”, tenemos 5 botones cada uno con un identificador (“id”) y con un valor llamado color (“data-color”). En HTML podemos asignarle valores a nuestros controles si antecedemos la palabra “data-” al nombre del atributo.
Es muy importante que el atributo se escriba en minúsculas, ya que de otra manera no funcionará.
Para que sea sencillo identificar los botones, vamos a pintarlos grandes y al centro de la pantalla de la siguiente manera con un poco de CSS:
.contenedor { max-width: 60%; margin: 0 auto; padding-top: 30px } .botones { display: flex } .botones button { flex: 1; padding: 20px; border: 0; outline: none } button.principal { padding: 10px; background-color: aliceblue; border: 1px solid #ddd; margin-top: 20px; display: block; width: 100% } #verde { background-color: green } #azul { background-color: blue } #rojo { background-color: red } #amarillo { background-color: yellow } #blanco { background-color: white }
Estas reglas CSS significan lo siguiente:
- El elemento con la clase “contenedor” tendrá un ancho máximo del 60% de la pantalla, un espacio en la parte de arriba de 30 pixeles y un margen automático de CERO, lo que hace que el contenedor quede alineado al centro de la pantalla (como puedes ver en la imagen).
- El contenedor de botones usará el mecanismo de distribución FLEX, lo que hará que los botones se expandan y ocupen todo el espacio horizontal disponible.
- Cada botón tomará el mismo tamaño (flex: 1), tendrá un espacio interior de 20 pixeles, sin borde y sin efecto cuando reciba el foco.
- El botón principal ocupará todo el espacio disponible en la parte de abajo. Le dijimos que su display es “block”, lo que hace que empuje los elementos siguientes a la línea de abajo.
- Las siguientes líneas indican el color que debe tener cada botón. Observa que utilizamos el símbolo # para definir el estilo, lo que significa que esas características se deben aplicar a los elementos que llevan ese nombre. Por ejemplo, la clase “#verde” se aplicará al elemento con id=”verde”, y así sucesivamente.
Paso #2: Mostrar la secuencia de botones
Aquí tenemos que mostrar un mecanismo que muestre la secuencia activa. Esto es, en el nivel 1 encenderá uno de los botones, en el nivel 2 encenderá 2 botones, y así sucesivamente. Cada vez que lo haga debe mostrar la misma secuencia, que es la que el jugador debe memorizar.
Por ejemplo, imagina que el juego va en el nivel 4 y hasta ahora la secuencia es amarillo, azul, verde y rojo. El juego debe encender esos botones siguiendo esta secuencia.
Si el usuario aprieta los botones en esta secuencia, entonces el juego debe agregar un nivel más pero debe encender todos los botones y al final encender el nuevo color. Digamos que el usuario lo hizo y ahora el juego agrega un nivel más con el color blanco, entonces debe encender los botones amarillo, azul, verde, rojo y blanco, en esa misma secuencia pero agregando blanco al final.
Para lograrlo necesitamos crear las siguientes variables en el archivo JavaScript:
let secuencia = []; let nivel = -1; let subnivel = 0; const ultimo = 5; const espera = 750; const esperaMedia = 1500; const eliminarColor = 350; const numColores = 5;
Estas variables sirven para lo siguiente:
- E arreglo secuencia va a contener la secuencia de colores. En JavaScript se define un arreglo vacío con las llaves vacías (“[]”). Observa que usamos el modo let porque este arreglo va a ser diferente cada vez que corramos el juego.
- La variable nivel nos dirá en qué nivel está el juego, siendo 0 el primer nivel. Usamos -1 para indicarle al juego que este aún no ha comenzado. Más adelante veremos cómo.
- La variable subnivel nos dirá en qué nivel estamos evaluando al usuario. Este es un marcador que va del cero al número de nivel actual en el juego.
- La constante ultimo nos dice cuantos niveles hay en un juego. Es una constante porque no queremos que esto cambie mientras jugamos.
- Las constantes espera, esperaMedia y eliminaColor nos sirven para indicarle al juego cuanto tiempo tiene que esperar para hacer una determinada tarea. Ya lo veremos más adelante.
- La constante numColores nos dice cuantos colores consideramos en el juego.
Vamos a crear una rutina en JavaScript que nos ayude a iniciar el juego. Se encargará de hacer todas las tareas necesarias para que podamos correr el juego:
function iniciarJuego() { // creamos la secuencia para los botones secuencia = new Array(ultimo); secuencia = secuencia.fill(0); secuencia = secuencia.map(n => Math.floor(Math.random() * numColores)); nivel = 0; subnivel = 0; // preparamos la interfaz boton.classList.add('hide'); // mostramos la primer secuencia del luego iluminarSecuencia(); }
A continuación una explicación de lo que significan estas líneas:
- En la línea 4, creamos un arreglo con el número de niveles del juego.
- En la línea 5, tomamos ese arreglo y lo llenamos con ceros. Observa que esa rutina “fill” nos devuelve un nuevo arreglo, por eso lo volvemos a recibir en la variable secuencia.
- En la línea 6, tomamos cada elemento del arreglo y generamos un número aleatorio entre 0 y el número de colores. Esta línea es la más interesante, porque aquí es donde creamos la secuencia que el usuario tendrá que repetir.
- En las líneas 7 y 8 iniciamos las variables del juego.
- En la línea 11, ocultamos el botón de iniciar el juego.
- En la línea 14, mostramos la secuencia de colores activa.
Ahora que tenemos el código que crea la secuencia, vamos a trabajar sobre la rutina que muestra la secuencia en pantalla.
// muestra en pantalla la secuencia actual function iluminarSecuencia() { for (let i = 0; i <= nivel; i++) { const color = transformarNumeroAColor(secuencia[i]); setTimeout(() => iluminarColor(color), espera * i); } } // ilumina el color especificado function iluminarColor(color) { colores[color].classList.add('light'); setTimeout(() => apagarColor(color), eliminarColor); } // apaga el botón especificado function apagarColor(color) { colores[color].classList.remove('light'); } // recupera el color utilizando la posición indicada function transformarNumeroAColor(numero) { switch (numero) { case 0: return 'rojo'; case 1: return 'verde'; case 2: return 'amarillo'; case 3: return 'azul'; case 4: return 'blanco'; } }
La rutina iluminarSecuencia ejecuta un ciclo desde el nivel 0 hasta el nivel actual en el juego. Cuando este comienza estamos en el nivel cero, así que ilumina un solo color en la secuencia, pero conforme avanzamos en el juego la variable nivel se incrementa, así que cada vez que llamemos a esta rutina encenderá más colores de la secuencia.
Observa que en la línea 6 tomamos ese valor aleatorio que está en la secuencia y se lo pasamos a una rutina que se llama transformarNumeroAColor, que se encarga de decirnos dado un valor, qué color le corresponde. Este valor lo guardamos en una constante porque si no lo hiciéramos así, cuando pasemos al siguiente nivel este valor cambiaría, así que JavaScript encendería siempre el último color evaluado. Puedes hacer la prueba, cambia el “const” por un “var” y observa el resultado.
En la línea 7 creamos un evento que hará una tarea pasados unos segundos determinados por “espera * i“, lo que significa que cada nivel mostrará el color y el siguiente esperará un poco más. Es decir, el primer nivel mostrará el color inmediatamente porque 1000 x cero es cero, pero el siguiente nivel esperará 1000 milisegundos, el siguiente esperará 2000 milisegundos y así sucesivamente.
La rutina iluminarColor se encarga de encender el botón según el color que le indiquemos. Esto lo hace en la línea 14 agregando al botón la clase CSS llamada “light”. Luego en la línea 15 crea un evento que correrá unos milisegundos después para eliminar esa clase llamando a la rutina apagarColor.
Paso #3: Iluminar los botones
Mi objetivo aquí es crear código sencillo de seguir, así que utilizaremos una técnica bastante simple para lograr este efecto. Lo que haremos es cambiar el color del botón y, tras unos segundos, lo regresamos al color original.
Esto lo haremos utilizando una clase CSS que manipule el color de fondo del botón, que llamaremos “light“.
En el paso anterior creamos una rutina que se llama iluminarColor que hace precisamente esto, la pondré de nuevo para dar claridad al tema:
function iluminarColor(color) { colores[color].classList.add('light'); setTimeout(() => apagarColor(color), eliminarColor); }
En este código, colores es un arreglo con los botones que tenemos en la interfaz. Aquí tomamos el botón del color indicado y le agregamos la clase “light“. La siguiente línea crea un timer para que al mismo botón se le quite esa clase.
function apagarColor(color) { colores[color].classList.remove('light'); }
Observa que en ambos casos usamos el objeto classList del botón para agregar o eliminar una clase CSS. Esta clase es muy sencilla, dice lo siguiente:
.light { background-color: aqua !important }
Para que esto funcione tenemos que crear ese arreglo de botones, así que pondremos el siguiente código al inicio de nuestro archivo JavaScript:
const boton = document.getElementById('btnEmpezar'); const verde = document.getElementById('verde'); const amarillo = document.getElementById('amarillo'); const azul = document.getElementById('azul'); const rojo = document.getElementById('rojo'); const blanco = document.getElementById('blanco'); const colores = { verde, amarillo, azul, rojo, blanco }
Paso #4: Interacción con el usuario
Ya tenemos los botones, la secuencia y los “efectos especiales”; lo que sigue es crear la rutina que se encargará de responder a los clics que haga el usuario y determinar si lo está haciendo bien o ha pedido el juego.
Para lograrlo, tenemos que darle una función a los botones en nuestra interfaz, así que debemos agregar a nuestro código en JavaScript las siguientes líneas, justo debajo del código del paso anterior:
colores.verde.addEventListener('click', elegirColor); colores.amarillo.addEventListener('click', elegirColor); colores.rojo.addEventListener('click', elegirColor); colores.azul.addEventListener('click', elegirColor); colores.blanco.addEventListener('click', elegirColor);
Este código dice que cada vez que se presione uno de estos botones, se debe ejecutar la rutina llamada elegirColor, que es la siguiente:
// al hacer clic en un botón, evalúa si la secuencia es correcta function elegirColor(ev) { // si el juego no está activo, salimos if (nivel === -1) return; // recupera el nombre del color activo const nombreColor = ev.target.dataset.color; // averiguamos su posición const numeroColor = transformarColorANumero(nombreColor); // mostramos el color en pantalla para confirmar la lectura iluminarColor(nombreColor); // si el botón presionado es el mismo en la secuencia if (numeroColor === secuencia[subnivel]) { // pasamos a la siguiente secuencia subnivel++; // si nos hemos quedado sin secuencia, pasamos al siguiente nivel if (subnivel > nivel) { // siguiente nivel nivel++; // si ya hemos llegado al final del juego if (nivel === ultimo) { alert("Ganaste :D"); terminarJuego(); } else { // reseteamos para que la próxima vez valide desde // el principio de la secuencia subnivel = 0; // mostramos la siguiente secuencia setTimeout(iluminarSecuencia, esperaMedia); } } } else { alert("Perdiste :'("); terminarJuego(); } } // recupera la posición a través del color indicado function transformarColorANumero(color) { switch (color) { case 'rojo': return 0; case 'verde': return 1; case 'amarillo': return 2; case 'azul': return 3; case 'blanco': return 4; } }
Lee esta rutina con detenimiento para entender lo que hace. Aquí te pondré unas observaciones interesantes:
- En la línea 5 analizamos el nivel, si este es -1 quiere decir que el juego no ha iniciado y salimos sin realizar ninguna tarea.
- En la línea 8 recuperamos el color del botón que definimos en el paso 1, el que dice en “data-color“. Este es un truco muy sencillo para identificar cada uno de los botones, y claro que pudimos utilizar unos valores más simples que requirieran menos validaciones, pero usé estos para que sea fácil de entender el código.
- En la línea 10 transformamos el color en el valor que le corresponde, que utilizamos luego en la línea 15 para conocer si el usuario hizo clic en el botón correcto.
- Si el usuario presionó el botón correcto, en la línea 18 hacemos que el próximo clic evalúe el siguiente valor en la secuencia. Para lograrlo incrementamos el valor en subnivel. Observa que esta variable nos sirve para controlar la posición de la secuencia actual que debemos evaluar.
- Si el valor de subnivel ya es mayor a nivel, significa que ya hemos presionado todos los botones en el orden correcto para el nivel actual del juego, así que incrementamos el valor de nivel para agregar un color más a la secuencia.
- En la línea 25 evaluamos si hemos alcanzado el nivel máximo del juego. Si esto es así, significa que el juego ha terminado y el jugador ha ganado la partida. De otra manera regresamos subnivel a cero, lo que significa que comenzaremos a evaluar la secuencia desde la primera posición, luego iluminamos de nuevo todos los botones en la secuencia.
- Si en la línea 15 el valor del botón no es el que corresponde en la secuencia, el usuario ha perdido y terminamos el juego.
La rutina de fin del juego es muy simple, solo debemos resetear las variables de la siguiente manera:
function terminarJuego() { boton.classList.remove('hide'); nivel = -1; }
En esta rutina volvemos a mostrar el botón principal y colocamos el nivel en -1, lo que deshabilita el juego.
Conclusión
Este ha sido un ejercicio muy interesante y divertido. Traté de utilizar diferentes técnicas y recursos para que aprendas un poco de cada cosa.
Para que todo quede más claro, colocaré el código completo a continuación:
Deja un comentario