/*******************************************************************************************************************************/ //Hra 'Simon' pro stavebnici Saimon. Tvůrci Ralph H. Baer, Howard J. Morrison, software Lenny Cope //https://en.wikipedia.org/wiki/Simon_(game) // //Hráč uslyší melodii a musí ji zopakovat stiskem kláves - spínače S0,S1,S2,S3. Po posloechu tonu stiskne tlacitko. //pokud hrac udela v sekvenci chybu, udela to hluboky a smutny ton a ztratí život. //Pokud trefi tón dle vzoru, vesele to zahraje a prida dalsi ton k melodii. //Hráč pokračuje, dokud melodii trefuje a má nenulový počet životů /*******************************************************************************************************************************/ //Pro snadnější drátování využíváme možnost multiplexovat přenos digitálních výstupů přes propojky. //Vstupy ABCD obvodu 74175 jsou přivedeny i na displej 1602 //pomocí CLOCK udáváme, zda se zapisuje do 74175, do displeje 1602, nebo do 74164. CLOCK obvodu 74175 je nazván ZAPSAT. // Zapojeni: Saimon 1 LOG0-98,98-96,96-94,94-92,LOG0-6,5-X1,109-log0, X6-87, 86-99, 81-97, 74-95, 79-93, Napájení 74175-log1 // Zapojeni: Saimon 3 - Saimon 4 (spínače) 343-A07,344-A06,345-A03,346-A02, (eeprom) 318(VCC)-log1, 317(WP])-log0, 316(SCL)-A05, 315(SDA)-A04 // Zapojeni: Saimon 3 DISPLEJ 1602:VCC-log1,CLOCK-X5,V0-X1,RS-X2,D7-84,D6-83,D5-76,D4-77 // Zapojeni: Saimon 3 Posuvný registr: 355(vstup)-84, 356(clock)-X3, 357(vcc) - Log1, 347-T8, 348-T9, 349-TA, 350-TB, 351-TC, 352-TD, 353-TE, 354-TF // Zapojeni: Saimon 4 - D02-77,D03-76,D04-83,D05-84,D06-108,D07-X2,D08-X5,D09-X6,D10-X3,LOG1-napajeni Arduino, /* * Poznámka - v programu pro verzi ATmega168 šetřím pamětí, kde to jen jde. Co nemusí být int, to je byte. Pro verzi procesoru ATmega328 to není potřeba * Kdo má procesor ATmega328, může odkomentovat některé komentáře níže */ #include #include byte led[] = {2,3,4,5}; //čísla digitálních výstupů pro ledky byte spinac[] = {7,6,3,2}; //čísla analogových vstupů pro spínače Zdířky 4 a 5 jsou vyhrazeny pro i2c komunikaci s eeprom byte reproduktor = 6; //číslo digitálního výstupu reproduktoru int tony[] = {659,880,330,554}; //frekvence výšky tónů hry byte tonyKonecHry[] = {131,147,165,175}; //frekvence výšky tónů hry int tonyStartHry[] = {262,277,294,311,330,349,370,392}; byte kolo = 0; //číslo levelu hry const byte delkaPoleMelodie = 40; byte vygenerovanaMelodie[delkaPoleMelodie]; //pole pro generování sekvence tónů hry byte melodieZadanaHracem[delkaPoleMelodie]; //pole pro načítání melodie zadávané hráčem byte zadavanyTon = 0; //aktuální ukazatel do pole zadávaných tónů boolean zacinaKolo = false; char jmenoHrace[6] = "Lukas"; //jmeno hráče int scoreHrace = 0; const int maxZaznamuTabulkyScore = 4, delkaJmena = 6; /* * Tabulka score - velikost pro procesor ATmega168 */ byte tabulkaScore[maxZaznamuTabulkyScore]={35,25,15,5}; char tabulkaJmenHrace[maxZaznamuTabulkyScore][delkaJmena] = {"Lukas","Liza ", "Josef", "Pepik"}; /* * Tabulka score - velikost pro procesor ATmega328 byte tabulkaScore[maxZaznamuTabulkyScore]={50,45,35,25,30,40,20,15,10,5}; char tabulkaJmenHrace[maxZaznamuTabulkyScore][delkaJmena] = {"Lukas","Pavla", "Vlada", "Simon", "Petra", "Pavel", "Jana ","Liza ", "Josef", "Pepik"}; */ char text[2][17]; //pole pro scrolovani textu const byte rs = 7, clk = 8, d4 = 2, d5 = 3, d6 = 4, d7 = 5; //čísla digitálních výstupů pro ovládání displeje const byte zapsat_74175 = 9, zapsat_74164 = 10; int pocetZivotu; LiquidCrystal lcd(rs, clk, d4, d5, d6, d7); void i2c_eeprom_write_byte( int eeaddress, byte data ) { // inicalizace komunikace s EEPROM // protože jsou A0 až A2 (1 - 3 na EEPROM) // připojeny k zemi je adresa // paměti 0x50 int rdata = data; Wire.beginTransmission(0x50); Wire.write(eeaddress); // odešle vyšší byte Wire.write(rdata); delay (5);//tahle delay tu musí být. Jinak to zapisuje kraviny. Druhý zápis je zkomolený Wire.endTransmission(); } byte i2c_eeprom_read_byte( int eeaddress ) { // inicalizace komunikace s EEPROM // protože jsou A0 až A2 (1 - 3 na EEPROM) // připojeny k zemi je adresa // paměti 0x50 byte rdata = 0xFF; Wire.beginTransmission(0x50); Wire.write(eeaddress); // odešle vyšší byte Wire.endTransmission(); Wire.requestFrom(0x50,1); if (Wire.available()) rdata = Wire.read(); return rdata; } void setup() { Wire.begin(); //počáteční nastavení digitálních výstupů for (byte i=0;i<4;i++) { pinMode(led[i], OUTPUT); } pinMode(reproduktor, OUTPUT); pinMode(zapsat_74175, OUTPUT); pinMode(zapsat_74164, OUTPUT); //nastavení displeje 1602 na 16 znaků, 2 řádky lcd.begin(16, 2); lcd.clear(); zobrazUvodniText (); } void generujSekvenciCisel ( byte* ukazatelSekvence ) { //funkce pro generování náhodné sekvence čísel //jako generátor náhodných čísel používáme //čtení z analogového vstupu 0 - nezapojovat randomSeed(analogRead(0)); //generování náhodné sekvence tónů for (byte i=0;i=0;i--) { tone(reproduktor, tonyKonecHry[i], 500); digitalWrite(led[i], HIGH); generujNabeznouHranu (zapsat_74175); //pro rozsvícení ledky musíme provést zápis do 74175 delay(250); digitalWrite(led[i], LOW); generujNabeznouHranu (zapsat_74175); //pro zhasnutí ledky musíme také provést zápis do 74175 delay(250); } } delay (2000); vymazDisplejScroll (); if (scoreHrace > tabulkaScore[maxZaznamuTabulkyScore-1]) { tabulkaScore[maxZaznamuTabulkyScore-1]=scoreHrace; strcpy (tabulkaJmenHrace[maxZaznamuTabulkyScore-1],jmenoHrace); seradTabulkuScore(); } } void bargrafPocetLedek ( byte pocet ) { //funkce pro zobrazeni bar grafu. 'pocet' je pocet sviticich ledek. //posilame seriova data na výstup 5. Začínáme od nejvyššího bitu 7 for (int i=7;i>=0;i--) { if (pocet <= i ) digitalWrite ( 5, LOW); else digitalWrite ( 5, HIGH); generujNabeznouHranu (zapsat_74164); } digitalWrite ( 5, LOW); //vyčistit sbernici } void zobrazPlnyBargraf ( void ) { //funkce pro zobrazeni bar grafu, všechny ledky svítí. Voláme na začátku kola //posilame seriova data na výstup 5. Začínáme od nejvyššího bitu 7 digitalWrite ( 5, HIGH); for (byte i=0;i<8;i++) { generujNabeznouHranu (zapsat_74164); } digitalWrite ( 5, LOW); //vyčistit sbernici } void zobrazPrazdnyBargraf ( void ) { //funkce pro zobrazeni bar grafu, všechny ledky svítí. Voláme na začátku kola //posilame seriova data na výstup 5. Začínáme od nejvyššího bitu 7 digitalWrite ( 5, LOW); for (byte i=0;i<8;i++) { generujNabeznouHranu (zapsat_74164); } digitalWrite ( 5, LOW); //vyčistit sbernici } void bargrafUbihajiciCas ( void ) { //funkce pro ubíhání času, zhasínající ledky vyjadřují ubíhající čas //posilame seriova data na výstup 5. Začínáme od nejvyššího bitu 7 digitalWrite ( 5, LOW); generujNabeznouHranu (zapsat_74164); } void chyba() { //funkce zahraje nízký tón při chybě hráče zobrazPrazdnyBargraf (); pocetZivotu--; //bargrafPocetLedek (pocetZivotu); lcd.clear(); lcd.setCursor (0,0); lcd.print ("Chyba !!!"); lcd.setCursor (0,1); lcd.print ("Pocet zivotu: "); lcd.print (pocetZivotu); smazatSbernici(); tone(reproduktor, 131, 500); delay(2000); kolo=0; zadavanyTon = 0; zacinaKolo = false; } void vyprselCasKola() { //funkce zahraje nízký tón při vypršení času kola, ubírá život pocetZivotu--; //bargrafPocetLedek (pocetZivotu); lcd.clear(); lcd.setCursor (0,0); lcd.print ("Vyprsel cas"); lcd.setCursor (0,1); lcd.print ("Pocet zivotu: "); lcd.print (pocetZivotu); smazatSbernici(); tone(reproduktor, 131, 500); delay(2000); kolo=0; zadavanyTon = 0; zacinaKolo = false; } void prehrajMelodii(int delka) { for (int i=0;i=0;i--) { //Scrolování textu na displej lcd.setCursor (i,0); lcd.print (text[0]); lcd.setCursor (i,1); lcd.print (text[1]); delay (50); } for (byte i=0;i<8;i++) {//čekací smyčka, hraje melodie bargrafPocetLedek (i); tone (reproduktor, tonyStartHry[i], 80); delay(100); } for (int i=7;i>=0;i--) {//čekací smyčka, hraje melodie bargrafPocetLedek (i); tone (reproduktor, tonyStartHry[i], 80); delay(100); } vymazDisplejScroll (); } int stisknutaKlavesa ( byte* spinac ) { //funkce vrací číslo stisknutého spínače //0=spínač S3, 1=spínač S2 atd. for (byte i=0;i<4;i++) { if (analogRead(spinac[i])>500) { return (3-i); } } return -1; } int cekaciSmycka ( void ) { int klavesa=-1; for (byte i=0;i<40;i++) {//čekací smyčka 2 sekundy na stisk kláves klavesa = stisknutaKlavesa (spinac); if ( klavesa > -1 ) { //při stisku klávesy udělá zvuk, čeká 200ms a vrátí hodnotu klávesy tone (reproduktor, 100, 10); lcd.clear(); smazatSbernici(); delay (200); return klavesa; } delay (50); } return klavesa; } int vypisInstrukce ( void ) { //funkce pro vypis instrukci ke hre, po stisku S1 v menu tone (reproduktor, 100, 10); lcd.clear(); lcd.setCursor (0,0); lcd.print ("Opakujte melodii"); lcd.setCursor (0,1); lcd.print ("stiskem klaves"); delay (2000); //zde místo čekací smyčky, která čeká na stisk klávesy, čekáme delay. //je to proto, aby klávesa, která volá instrukce, je hned zase nepřerušila vymazDisplejScroll (); /* * Zbytek je pro verzí procesoru Atmega328, zabírá to hodně paměti lcd.setCursor (0,0); lcd.print ("stiskem tlacitek"); lcd.setCursor (0,1); lcd.print ("S0, S1, S2, S3. "); if (cekaciSmycka() > -1) return 0;//čekací smyčka 2 sekundy, až zde je možné opustit volbu instrukce vymazDisplejScroll (); lcd.setCursor (0,0); lcd.print ("Posloupnost tonu"); lcd.setCursor (0,1); lcd.print ("zobrazuji ledky."); if (cekaciSmycka() > -1) return 0;//čekací smyčka 2 sekundy, stisk klávesy přeruší funkci vymazDisplejScroll (); lcd.setCursor (0,0); lcd.print ("Hrac ma 4 zivoty"); lcd.setCursor (0,1); lcd.print ("na zacatku hry. "); if (cekaciSmycka() > -1) return 0;//čekací smyčka 2 sekundy, stisk klávesy přeruší funkci vymazDisplejScroll (); lcd.setCursor (0,0); lcd.print ("Kazdou chybou "); lcd.setCursor (0,1); lcd.print ("ztraci zivot. "); if (cekaciSmycka() > -1) return 0;//čekací smyčka 2 sekundy, stisk klávesy přeruší funkci vymazDisplejScroll (); lcd.setCursor (0,0); lcd.print ("Original hry "); lcd.setCursor (0,1); lcd.print ("Ralph. H. Baer "); if (cekaciSmycka() > -1) return 0;//čekací smyčka 2 sekundy, stisk klávesy přeruší funkci vymazDisplejScroll (); lcd.setCursor (0,0); lcd.print ("Howard Morrison"); lcd.setCursor (0,1); lcd.print ("a Lenny Cope. "); if (cekaciSmycka() > -1) return 0;//čekací smyčka 2 sekundy, stisk klávesy přeruší funkci vymazDisplejScroll (); */ } void vycistiPoleText (void) { // tohle kopírování řetězce je zde proto, abychom naplnili mezerami pole pro vkládání textu //------------------------------------------ char mezera[] = " "; strcpy (text[0],mezera); strcpy (text[1],mezera); for ( byte i = 1; i<16 ; i++ ) { strcat (text[0],mezera); strcat (text[1],mezera); } } int zobrazScore ( void ) { int klavesa=-1, j=0; char cislo[]=" "; //pameť pro převod čísla na řetězec vycistiPoleText (); tone (reproduktor, 100, 10); while (j=0;i--) { //Scrolování textu na displej lcd.setCursor (i,0); lcd.print (text[0]); lcd.setCursor (i,1); lcd.print (text[1]); delay (20); } klavesa = cekaciSmycka (); //čekací smyčka 2 sekundy, reaguje na stisk klávesy switch ( klavesa ) { //stiskem S3 ulozime tabulku do pameti, S2 ji nacteme case 3: zapisTabulkyEprom();break; case 2: cteniTabulkyEprom();break; case 1: return 0; case 0: return 0; } vymazDisplejScroll();//Scroll displeje } } int menuHry (void) { //menu, stiskem spínačů se volí možnosti //S3 - Odstartuje hru, S2 - Zadávání jmena hráče //S1 - Vytiskne instrukce, S0 - Nejvyssi score int klavesa=-1, polozkyMenu=0; for ( byte cyklusMenu = 0; cyklusMenu < 4 ; cyklusMenu++) { lcd.clear(); if (polozkyMenu == 0 ) { vycistiPoleText (); strcpy (text[0],"S3-Start hry "); strcpy (text[1],"S2-Instrukce "); polozkyMenu=1; } else { vycistiPoleText (); strcpy (text[0],"S1-Zadat jmeno "); strcpy (text[1],"S0-High score "); polozkyMenu=0; } for (int i=16;i>=0;i--) { //Scrolování textu na displej lcd.setCursor (i,0); lcd.print (text[0]); lcd.setCursor (i,1); lcd.print (text[1]); delay (20); } for (byte i=0;i<80;i++) {//čekací smyčka 4 sekundy na stisk kláves klavesa = stisknutaKlavesa (spinac); if (klavesa > -1 ) return klavesa; delay (50); } }//smyčka cyklusMenu, opakování 2x return -1; } void hraj ( void ) { byte casKola; byte delayKola = 50; do { if (kolo==0) { trefa(); kolo =kolo + 1; casKola=80; zobrazPlnyBargraf(); } if (zadavanyTon==0 && zacinaKolo==false) { // přehrajeme melodii do dozaženého kola prehrajMelodii(kolo); zacinaKolo = true; } else if (zadavanyTon==kolo) { // user has entered their pattern scoreHrace++; trefa(); zacinaKolo = false; kolo = kolo + 1; zadavanyTon = 0; casKola=80; zobrazPlnyBargraf(); } else { // user still entering their pattern for (byte j=0;j<10;j++) { for (byte i=0;i<4;i++) { if (analogRead(spinac[i])>500) { melodieZadanaHracem[zadavanyTon] = i; if (melodieZadanaHracem[zadavanyTon]!=vygenerovanaMelodie[zadavanyTon]) { chyba(); casKola=80; } else { zadavanyTon = zadavanyTon + 1; digitalWrite(led[i], HIGH); generujNabeznouHranu (zapsat_74175); //pro rozsvícení ledky musíme provést zápis do 74175 tone(reproduktor, tony[i], 500); delay(500); digitalWrite(led[i], LOW); generujNabeznouHranu (zapsat_74175); //pro zhasnutí ledky musíme také provést zápis do 74175 } //else }// if } delay(delayKola); casKola--; }//Opakování 10 čtení kláves, pak ubyde jednotka času na bargrafu bargrafUbihajiciCas(); if ( casKola == 0) { vyprselCasKola(); casKola=80; zobrazPlnyBargraf(); } }//else } while (pocetZivotu>0); konecHry(); } void seradTabulkuScore ( void ) { //provede bubble sort s tabulkou score char pomocneJmeno[]=" "; byte pomocneScore=0; for ( byte i=0;i S0ok"); delay (2000); lcd.clear(); while (poziceZnaku<1) {//Smyčka pro zadávání prvního písmene jména, velké písmeno lcd.setCursor (0,0); lcd.print ("Zadej jmeno:"); lcd.setCursor (0,1); if (blikani) zadaneJmeno[0]=prvniZadavanyZnak; else zadaneJmeno[0]='_'; //Zadávané písmeno bude blikat - střídání mezery a písmena lcd.print (zadaneJmeno); int klavesa = stisknutaKlavesa (spinac); switch (klavesa) { case -1: break; case 3: prvniZadavanyZnak--; tone(reproduktor,vyskaTonu,delkaTonu);break; case 2: prvniZadavanyZnak++; tone(reproduktor,vyskaTonu,delkaTonu);break; case 1: zadaneJmeno[poziceZnaku]=prvniZadavanyZnak; tone(reproduktor,vyskaTonu,delkaTonu); poziceZnaku=1; break; case 0: zadaneJmeno[poziceZnaku]=prvniZadavanyZnak; tone(reproduktor,vyskaTonu,delkaTonu); poziceZnaku=5; break; } delay (200); if (prvniZadavanyZnak<'A') prvniZadavanyZnak='Z'; if (prvniZadavanyZnak>'Z') prvniZadavanyZnak='A'; if (citacBlikani++>2) { citacBlikani = 0; blikani = !blikani; } } while (poziceZnaku<5) {//Smyčka pro zadávání druhého a dalšího písmene jména, malé písmeno lcd.setCursor (0,0); lcd.print ("Zadej jmeno:"); lcd.setCursor (0,1); if (blikani) zadaneJmeno[poziceZnaku]=dalsiZadavanyZnak; else zadaneJmeno[poziceZnaku]='_'; //Zadávané písmeno bude blikat - střídání mezery a písmena lcd.print (zadaneJmeno); int klavesa = stisknutaKlavesa (spinac); switch (klavesa) { case -1: break; case 3: dalsiZadavanyZnak--; tone(reproduktor,vyskaTonu,delkaTonu);break; case 2: dalsiZadavanyZnak++; tone(reproduktor,vyskaTonu,delkaTonu);break; case 1: zadaneJmeno[poziceZnaku++]=dalsiZadavanyZnak; tone(reproduktor,vyskaTonu,delkaTonu); dalsiZadavanyZnak='a'; break; case 0: zadaneJmeno[poziceZnaku]=' '; tone(reproduktor,vyskaTonu,delkaTonu); poziceZnaku=5; break; } delay (200); if (citacBlikani++>2) { citacBlikani = 0; blikani = !blikani; } if (dalsiZadavanyZnak<'a') dalsiZadavanyZnak='z'; if (dalsiZadavanyZnak>'z') dalsiZadavanyZnak='a'; } strcpy (jmenoHrace,zadaneJmeno); } void zapisTabulkyEprom (void ) { int ukazatelPameti=0; byte kontrolniScore = 50; char kontrolniJmeno[] = "Simon"; lcd.clear(); lcd.setCursor (0,0); lcd.print ("Ukladam tabulku"); lcd.setCursor (0,1); for (int j=0;j<5;j++) {//Tohle jmeno zapisujeme pro kontrolu při čtení - první jmeno musí být "Simon", jinak je chyba cteni z EEPROM i2c_eeprom_write_byte( ukazatelPameti++, kontrolniJmeno[j]); } i2c_eeprom_write_byte( ukazatelPameti++, kontrolniScore); lcd.print ("."); delay (50); for (byte i=0;i