Inicio >> Proyectos >> El juego de Simón con Arduino >> Parte 3

En la parte 2 del proyecto vimos cómo generar las secuencias de luces aleatorias que el jugador debe repetir.

Ahora es el momento de controlar los turnos del jugador. Después de cada secuencia de luces el jugador debe intentar repetirla, para ello presionará los pulsadores en el mismo orden en el que se han encendido los LEDs. Tendremos que leer los pines digitales para comprobar qué pulsadores presiona y comprobar si son correctos.

Si el jugador repite la secuencia bien pasa a la siguiente ronda, sino pierde y se termina el juego. Si completa todas las rondas finaliza el juego y gana. Este comportamiento también tenemos que controlarlo.

Recuerda que necesitas haber completados la parte 1 y la parte 2 del proyecto para continuar, puesto que vamos a seguir trabajando sobre el sketch y el circuito que hicimos anteriormente.

Declaraciones

A parte de todo lo que ya teníamos, voy a declarar una variable nueva para controlar cuándo termina el juego y si el usuario gana o pierde:

bool ok = true;

Es una simple variable bool que nos ayudará a controlar el flujo del juego. La usaremos más tarde.

Funciones

Para saber qué pulsador se ha presionado en cada momento podría usar interrupciones. Sería la solución más rápida, pero como estamos usando un Arduino UNO no tenemos suficientes interrupciones disponibles, así que no nos queda más remedio que leer constantemente el estado de los pines digitales en los que están conectados los pulsadores.

Por eso vamos a utilizar esta función que comprueba constantemente el estado de los pulsadores:

bool readButtons(){
  for (int j=0; j<n && ok; j++){
    int input[]={0,0,0,0};
    while(!input[GREEN] 
          && !input[RED] 
          && !input[BLUE] 
          && !input[YELLOW]){
      for(int i=0; i<SIZE; i++){
        input[i] = digitalRead(BUTTONS[i]);
        if(input[i]){
          printLed(LEDS[i]);
        } 
      }
    }
    if(!input[sequence[j]]){
      ok=false;
    }
  }
  return ok;
}

Por cada ronda tenemos que leer n pulsaciones, eso lo controla el primer bucle for:

for (int j=0; j<n && ok; j++)

Fíjate que en la condición compruebo que j sea mejor que n y que la variable ok siga siendo true. Si esa variable cambia su valor a false el for se detiene.

En la línea 3 he declarado un array de enteros (input) para guardar el estado de los 4 pulsadores. El orden en el que los vamos a almacenar es verde, rojo, azul y amarillo. Inicialmente están a 0 porque no hay ningún pulsado.

Mientras todos los elementos del array tengan valor 0 leo los pines, eso lo controlo con el while:

while(!input[GREEN] 
      && !input[RED] 
      && !input[BLUE] 
      && !input[YELLOW])

Cuando alguno cambie su estado a 1 el while se detiene. Si un pin cambia de 0 a 1 significa que el pulsador conectado a dicho pin ha sido presionado.

El segundo for (línea 8) sirve para leer los 4 pines de los pulsadores y guardar el estado en el array input: 

for(int i=0; i<SIZE; i++){
  input[i] = digitalRead(BUTTONS[i]);
  if(input[i]){
    printLed(LEDS[i]);
  }
}

La instrucción if comprueba si el estado del pin tiene valor 1. Eso significa que el pulsador conectado a ese pin ha sido pulsado, por lo que tenemos que encender el LED correspondiente. Para eso llamamos a la función printLed.

Cuando el bucle while finaliza significa que se ha presionado un botón. Ya hemos iluminado el LED asociado, ahora tenemos que comprobar si es el botón correcto. Eso lo hacemos con la siguiente instrucción if (línea 15):

if(!input[sequence[j]]){
  ok=false;
}

Debe coincidir con el color de la secuencia que toque. Recuerda que dentro del array sequence están los indices de los LEDS y pulsadores, por tanto, el valor de la posición j debe coincidir con el indice del botón pulsado, y debe haber un 1 en dicha posición del array input. De lo contrario se ha presionado un pulsador incorrecto y ok tomará valor false.

Función loop

Tenemos que completar la función loop para que realice todo el flujo del juego. Quedaría así:

void loop(){
  Serial.println("**********");
  ok = true;
  randomSequence();
  while(ok && n<=ROUNDS){
    Serial.print("ronda ");
    Serial.println(n);
    printSecuence();
    if(readButtons()){
      Serial.println("btn ok");
      n++;
    }else{
      ok=false;
    }
  }
  if(ok){
    Serial.println("WIN!!!!!");
  }else{
    Serial.println("FAIL!!!!!");
  }
  delay(2000);
}

El bucle while sirve para controlar el fin de la partida. Mientras ok tenga valor true y n sea inferior a ROUND la partida no habrá terminado. Quedan rondas y el usuario aun no ha perdido.

Imprimimos la secuencia con printSequence (línea 8) y esperamos a que el usuario la repita con los pulsadores. Para eso llamando a la función readButtons. Si devuelve true es que lo ha hecho bien, lo comprobamos con if(readButtons()) e incrementamos el valor de n para pasar a la siguiente ronda (n++). En caso contrario le damos valor false a la variable ok para que termine la partida.

Cuando la partida termina (finaliza el while) sabremos si el jugador ha ganado o perdido comprobando el valor de la variable ok:

if(ok){
  Serial.println("WIN!!!!!");
}else{
  Serial.println("FAIL!!!!!");
}

Si es true ha ganado y simplemente mostramos un mensaje en consola que lo indique con Serial.println. Si es false ha perdido y hacemos lo mismo con otro mensaje.

En la parte 4 (que ya es la última) vamos a cambiar estos mensajes en la consola por efectos luminosos para que el jugador sepa que ha terminado el juego y el resultado final.

Te dejo por aquí el sketch completo con todo el código junto para que puedas probarlo sin problemas:

/*----------------------*
*     DECLARACIONES     *
*-----------------------*/
 
//LEDs:
const int LED_GREEN = 8;
const int LED_RED = 6;
const int LED_BLUE = 4;
const int LED_YELLOW = 2;
 
//pulsadores:
const int BTN_GREEN = 9;
const int BTN_RED = 7;
const int BTN_BLUE = 5;
const int BTN_YELLOW = 3;
 
//arrays:
const int LEDS[] = {LED_GREEN, LED_RED, LED_BLUE, LED_YELLOW};
const int BUTTONS[] = {BTN_GREEN, BTN_RED, BTN_BLUE, BTN_YELLOW};
const int SIZE = 4;
 
//indices:
const int GREEN = 0;
const int RED = 1;
const int BLUE = 2;
const int YELLOW = 3;

//rondas y secuencias:
const int ROUNDS = 20;
int sequence[ROUNDS];
int n = 0; 

//ganador
bool ok = true;
 
/*----------------------*
*     CONFIGURACIÓN     *
*-----------------------*/
void setup(){
  Serial.begin(9600);
 
  for(int i=0; i<SIZE; i++){
    pinMode(LEDS[i], OUTPUT);
    pinMode(BUTTONS[i], INPUT);
  } 

  randomSeed(analogRead(0));
}

/*----------------------*
*       FUNCIONES       *
*-----------------------*/
void printLed(int led){
  digitalWrite(led, HIGH);
  delay(1000);
  digitalWrite(led, LOW);
}

void randomSequence(){
  for (int i=0; i<ROUNDS; i++){
    sequence[i] = random(0,SIZE);
  }
  n = 1;  
}

void printSequence(){
  delay(1000);
  for(int i=0; i<n; i++){
    printLed(LEDS[sequence[i]]);
    Serial.println(sequence[i]);
    delay(1000);
  }
}

bool readButtons(){
  for (int j=0; j<n && ok; j++){
    int input[]={0,0,0,0};
    while(!input[GREEN] 
          && !input[RED] 
          && !input[BLUE] 
          && !input[YELLOW]){
      for(int i=0; i<SIZE; i++){
        input[i] = digitalRead(BUTTONS[i]);
        if(input[i]){
          printLed(LEDS[i]);
        } 
      }
    }
    if(!input[sequence[j]]){
      ok=false;
    }
  }
  return ok;
}

/*----------------------*
*          LOOP         *
*-----------------------*/
void loop(){
  Serial.println("**********");
  ok = true;
  randomSequence();
  while(ok && n<=ROUNDS){
    Serial.print("ronda ");
    Serial.println(n);
    printSecuence();
    if(readButtons()){
      Serial.println("btn ok");
      n++;
    }else{
      ok=false;
    }
  }
  if(ok){
    Serial.println("WIN!!!!!");
  }else{
    Serial.println("FAIL!!!!!");
  }
  delay(2000);
}