Indice

Progetto finale: il gioco "Simon"

il gioco elettronico originale

Vogliamo realizzare un clone di un famoso gioco elettronico degli anni '80: Simon. Il gioco consisteva nel ricordare e riprodurre una sequenza sonora e luminosa. Ad ogni turno i tasti colorati venivano illuminati in maniera casuale (ogni bottone emetteva anche un suono) poi il giocatore doveva ripetere la sequenza premendo i tasti nel giusto ordine. Ad ogni turno aumentava il numero di tasti da premere.

Per realizzare questo progetto dovremo generare una sequenza casuale con cui pilotare quattro LED colorati e verificare che il giocatore prema quattro pulsanti nel giusto ordine.

Il risultato finale è questo:

Progetto: realizzare un clone del gioco Simon

Scopo realizzare un gioco di memoria interattivo
Componenti 4 LED (giallo, blu, verde, rosso), 4 resistori da 220Ω, 4 pulsanti, 4 resistori da 10kΩ

Approfondimento sul linguaggio di programmazione: le funzioni

Nello sketch, oltre alle funzioni setup() e loop(), compaiono due funzioni definite nel programma:

In uno sketch come questo, lungo un centinaio di righe, è utile usare le funzioni per raggruppare le istruzioni che si occupano di un compito ben preciso; in questo modo il programma è più leggibile ed è più facile modificarlo senza commettere errori.

E' importante sapere che le variabili definite all'interno di una funzione sono accessibili solo dalle istruzioni della funzione stessa mentre quelle definite all'esterno sono accessibili ovunque nel programma. Ad esempio è possibile accedere alla variabile turno dentro la funzione inputGiocatore() ma non è possibile accedere alla variabile pulsantePremuto dalle funzioni visualizzaSequenza() o loop(). Questo è un bene perché permette di tenere separate le variabili che servono nell'intero programma da quelle utili solo dentro una certa funzione.

L'istruzione return serve a restituire un risultato nelle funzioni che lo prevedono; nel nostro caso invece viene usata per uscire dalla funzione inputGiocatore() in caso di errore e ridare il controllo alla funzione principale loop() che l'aveva chiamata.

Schema elettrico

progetto finale: simon

sketch

/*
 * Progetto Finale: un clone del gioco elettronico Simon.
 * Il giocatore deve memorizzare una sequenza luminosa casuale 
 * e ripeterla accendendo i LED nell'ordine giusto. 
 * Ad ogni turno il numero di LED accesi aumenta.
 * 
 * si gioca una sola partita con dieci turni (per ricominciare resettare)
 *
 * Nome Studente:
 * Creato il: data
 */
 
// pin collegati ai quattro LED
int led[] = { 2, 3, 4, 5};
// pin collegati ai quattro pulsanti
int pulsante[] = { 8, 9, 10, 11};
// turno del gioco
int turno = 1;
// sequenza casuale per l'accensione dei LED
int sequenza[10];
 
void setup(){
  // pin collegati ai quattro LED 
  for(int x = 0; x < 4; x++){
    pinMode(led[x], OUTPUT);
  }
  // pin collegati ai quattro pulsanti
  for(int x = 0; x < 4; x++){
    pinMode(pulsante[x], INPUT);
  }
 
  // comunicazione seriale per il debugging
  Serial.begin(9600);
 
  // GENERAZIONE DELLA SEQUENZA CASUALE
  // inizializzo il generatore di numeri casuali con un valore 
  // casuale (l'input su un pin analogico non collegato)
  randomSeed(analogRead(0));
  // genero 10 numeri tra 0 e 3
  for(int x = 0; x < 10; x++){
    sequenza[x] = random(0, 4);
  }
}
 
void loop(){
  // giochiamo dieci turni poi loop continua senza fare niente
  while(turno <= 10){
    Serial.print("turno ");
    Serial.println(turno);
    // chiamo la funzione che visualizza la sequenza
    visualizzaSequenza();
    // chiamo la funzione che memorizza l'input del giocatore
    // e controlla se sono stati premuti i tasti giusti
    inputGiocatore();
    // al prossimo turno un LED in più
    turno = turno + 1;
    // pausa tra un turno e l'altro
    delay(2000);
  }
}
 
void visualizzaSequenza(){
  // SEQUENZA DI LED DA RICORDARE PER IL TURNO CORRENTE
  // primo turno: un solo LED, secondo turno due e così via
  for(int x = 0; x < turno; x++){
    // accendo e spengo il LED corrispondente al numero casuale
    // contenuto nell'array sequenza
    digitalWrite(led[sequenza[x]], HIGH);
    delay(500);
    digitalWrite(led[sequenza[x]], LOW);
    delay(500);
    // stampo la sequenza
    Serial.print(sequenza[x]);
  }
  Serial.println();
}
 
void inputGiocatore(){
  // INPUT DEL GIOCATORE CON I PULSANTI CORRISPONDENTI AI LED
  // il giocatore preme un numero di pulsanti pari al turno
  for(int x = 0; x < turno; x++){
    Serial.println("attesa input");
    // attende la pressione di un pulsante memorizzata nella 
    // variabile pulsantePremuto (0 = non premuto, 1 = premuto)
    int pulsantePremuto = 0;
    while(pulsantePremuto == 0){
      // controlla lo stato dei quattro pulsanti
      for(int y = 0; y < 4; y++){
        // se il pulsante è premuto
        if(digitalRead(pulsante[y]) == HIGH){
          // stampo il numero del pulsante
          Serial.print("pulsante ");
          Serial.print(y);
          Serial.println(" premuto");
          // accendo il LED corrispondente
          digitalWrite(led[y], HIGH);
          delay(200);
          digitalWrite(led[y], LOW);
          delay(200);
          // se è il pulsante sbagliato
          if(y != sequenza[x]){
            Serial.println("errore!");
            // segnalo l'errore facendo lampeggiare i LED
            for(int i = 0; i < 4; i++){
              digitalWrite(led[i], HIGH);
            }
            delay(500);
            for(int i = 0; i < 4; i++){
              digitalWrite(led[i], LOW);
            }
            delay(500);           
            // azzero la variabile turno e comincio una nuova 
            // partita con la stessa sequenza
            turno = 0;
            // esco dalla funzione inputGiocatore
            return;
          }
          // registro la pressione del pulsante
          pulsantePremuto = 1;
          // esco dal ciclo for (non controllo altri pulsanti)
          // e torno al ciclo while che termina
          break;
        }
      }
    }    
    // pausa (debounce) tra la pressione di un tasto e l'altra
    delay(100);
  }
}

Osservazioni

Variante: aggiungere il suono

Il gioco originale produceva un suono diverso per ogni LED. Per aggiungere questa funzionalità colleghiamo un cicalino piezoelettrico al pin 12. Poi modifichiamo il programma. Definiamo l'array suono[] con le frequenza corrispondenti alla tonalità da emettere per ogni LED:

int suono[] = {262, 330, 392, 523};

Aggiungiamo la funzione tone() quando viene acceso il LED nella funzione visualizzaSequenza():

tone(12, suono[sequenza[x]], 500);

Facciamo lo stesso nella fuzione inputGiocatore():

tone(12, suono[y], 500);

Volendo si potrebbe suonare una piccola melodia quando si commette un errore o quando si indovinano tutti e 10 i LED.

Torna all'indice.