Strumenti Utente

Strumenti Sito


arduino_4

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:

  • visualizzaSequenza(), che mostra la sequenza dei LED da ricordare
  • inputGiocatore(), che attende che il giocatore prema un numero di bottoni corrispondenti al turno (un bottone al primo turno, due al secondo e così via) e interrompe il gioco in caso di errore

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

  • per generare dei numeri interi casuali usiamo la funzione random(minimo incluso, massimo escluso)
  • il generatore di numeri casuali di Arduino produce una serie molto lunga ma prevedibile di numeri; per non avere ripetizioni si usa la funzione randomSeed(numero casuale) che fa partire la sequenza da una posizione a caso della serie; il numero casuale è ottenuto leggendo l'input da un pin analogico scollegato, il cui valore di tensione è ignoto e fluttuante
  • la sequenza casuale di numeri da 0 a 3 corrispondenti ai LED da accendere è contenuta nell'array sequenza[]; ad ogni turno accendere i LED da sequenza[0] a sequenza[turno-1] (ricordiamo che si conta a partire da zero)
  • nella funzione inputGiocatore()
    • il ciclo for chiede al giocatore di premere un numero di tasti pari al turno
    • il ciclo while attende la pressione di uno dei quattro pulsanti e verifica che il pulsante premuto sia corretto
    • l'istruzione break viene usata per uscire dal ciclo for quando viene rilevata la pressione di un pulsante
    • l'istruzione return viene usata per uscire dalla funzione quando viene rilevato un'errore; in questo modo il giocatore non immette l'intera sequenza ma il gioco si interrompe appena si commette un errore
    • quando si sbaglia la variabile turno viene azzerata e il gioco riprende dall'inizio ma con la stessa sequenza casuale
  • per ricominciare il gioco con una nuova sequenza casuale bisogna resettare

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.

Questo sito Web utilizza i cookie. Utilizzando il sito Web, l'utente accetta la memorizzazione dei cookie sul proprio computer. Inoltre riconosci di aver letto e compreso la nostra Informativa sulla privacy. Se non sei d'accordo, lascia il sito.Maggiori informazioni sui cookie
arduino_4.txt · Ultima modifica: 2020/07/03 15:56 da 127.0.0.1