Gra w zapamiętywanie kolorów

Niniejszy artykuł został napisany w oparciu o ten tekst zamieszczony na blogu Newtona. Wszystkie ilustracje, filmik oraz kod programu pochodzą właśnie stamtąd.

Celem projektu było wykorzystanie jak największej ilości elementów zawartych w Starter Kicie. A czym ów projekt jest? Jak sam tytuł wskazuje, to gra polegająca na zapamiętaniu sekwencji kolorów wyświetlanych za pomocą diody LED RGB, a następnie odtworzeniu jej. Na początku sekwencja składa się z 3 kolorów, potem stopień trudności wzrasta – za każdym razem jest dodawany kolejny kolor do zapamiętania. Pewnym ułatwieniem jest fakt, że do każdego koloru jest przypisany dźwięk o innym tonie – podczas wyświetlania danego koloru odpowiadający mu dźwięk jest emitowany z dołączonego głośniczka, tak więc gra angażuje dwa zmysły: wzrok i słuch. Interfejs użytkownika stanowi wspomniana już dioda RGB, linijka diod LED informująca, który z kolei kolor ustawiamy oraz dwa przyciski typu microswitch. Wciskając oba naraz uruchamiamy grę, lewym wybieramy kolor, a prawym zatwierdzamy. Są jeszcze dwie diody LED informujące, czy udało nam się poprawnie odtworzyć sekwencję. Jeśli zaświeci się lewa – znaczy, że tak, w przeciwnym wypadku zaświeci się prawa dioda.

Zmontowany na płytce stykowej układ prezentuje się tak:

Zmontowany układ
Zmontowany układ

A jak działa, można zobaczyć (i usłyszeć) na tym filmiku. Poniżej natomiast jest schemat tego układu wykonany we Fritzingu:

Schemat układu
Schemat układu

W tym miejscu kilka słów komentarza – przy rysowaniu takiego schematu warto uruchomić program Fritzing podając w linii komend (bądź we właściwościach skrótu) parametr -graphicssystem raster – będzie działał szybciej. Kolejna rzecz, którą warto wiedzieć – jeśli podczas rysowania połączeń między elementami przytrzymamy klawisz Shift, uzyskamy ładne, prostokątne linie. Jak widać, autor powyższego obrazka nie wiedział o tym ;)
Co do kwestii technicznych, wszystkie rezystory umieszczone na płytce mają wartość 220Ω, zaś układ scalony w centralnej części płytki to rejestr przesuwający 74HC595, dzięki któremu możemy sterować ośmioma diodami, zużywając na to jedynie trzy cyfrowe wyprowadzenia Arduino. Dokładniej zostało to opisane w artykule „Przesuwane diody”.

Jeśli już mamy wszystko podłączone, teraz należałoby to jakoś oprogramować. Na początku trzeba zadeklarować, co gdzie podłączamy (do których wejść/wyjść). Odpowiada za to ten fragment kodu:

int latchPin = 8, clockPin = 12, dataPin = 3; //rejestr przesuwający
byte data = 0; //dane do wysłania na rejestr

int led8 = 7, led9 = 6; //dodatkowe LEDy
int ledR = 11, ledG = 10, ledB = 9; //LED RGB
int buttonLeft = 5, buttonRight = 4; //przyciski
int speakerPin = 13; //głośniczek

Teraz następuje inicjalizacja – ustawienie funkcji wyprowadzeń, włączenie rezystorów podciągających, itd.:

void setup()
{
  Serial.begin(57600);
  for(int i=3;i<=13;i++) // ustawienie wszystkich pinów
    pinMode(i, OUTPUT); // od 3 do 13 jako wyjścia
  pinMode(buttonLeft, INPUT); // ustawienie pinów przycisków
  pinMode(buttonRight, INPUT); // jako wejścia

  digitalWrite(buttonLeft, HIGH); // włączenie wewnętrznych
  digitalWrite(buttonRight, HIGH); // rezystorów podciągających
  randomSeed(analogRead(0)); // zainicjalizowanie generatora liczb pseudolosowych
  // wartością odczytaną z niepodłączonego analogowego pinu 0 - przy każdym 
  // uruchomieniu będzie ona inna
  clearLeds();
}

W tym miejscu chciałbym wtrącić pewne wyjaśnienie, otóż w paru miejscach w kodzie programu pojawia się taki dziwny zapis:

 a ? b : c

a jeśli ktoś na co dzień nie programuje w języku C, może być tym nieco zdezorientowany, tym bardziej, że w „Language Reference” na oficjalnej stronie Arduino nie wyjaśniono tego. Jest to tak zwany operator wyrażenia warunkowego. Jeśli warunek podstawiony w miejscu „a” jest spełniony (lub zmienna w miejscu „a” ma wartość TRUE), zostanie zwrócona liczba „b”. W przeciwnym wypadku – „c”. Ot i cała filozofia.

Wróćmy do właściwego programu. Główna pętla wygląda następująco:

void loop()
{
  effect();
  clearLeds();
  if(waitForButton() == 3)
    return;

  for(int j=3;j<=8;j++)
    if(!playLevel(7, j))
      return;
}

Kolejno: na początku, funkcją effect() uruchamiane są dźwiękowe i świetlne „fajerwerki”, a funkcja clearLeds gasi wszystkie diody. Następnie, o ile użytkownik nie wciśnie dwóch przycisków naraz, co spowoduje reset, funkcja playLevel rozpoczyna kolejne poziomy gry, za każdym obiegiem pętli for z drugim parametrem (ilość świateł do zapamiętania) zwiększonym o 1. Pierwszy parametr natomiast jest na sztywno ustawiony jako 7, czyli kolory do zapamiętania losowane są spośród ośmiu (od 0 do 7). Można oczywiście zmniejszyć tę ilość, ale wtedy gra stanie się zbyt prosta. Zdecydowanie lepiej pomanipulować funkcją setColor, dodając kolejne kolory (wystarczy zmienić parametry w setRGB). Mając kilka dodanych kolorów, możemy ich liczbę (mniejszą o 1, bo numeracja jest od 0!) wpisać jako pierwszy parametr funkcji playLevel. Wtedy rozgrywka stanie się trudniejsza, czyli ciekawsza ;).

Pełny kod programu opatrzony komentarzami można pobrać tutaj. Myślę, że jest on na tyle jasny, że nie ma potrzeby go szczegółowo wyjaśniać.