Posts etiquetados ‘arduino games’

Arduino es una plataforma prácticamente infinita para todo tipo de aplicaciones como lo hemos visto en este humilde blog de entre millones que existen en la web. En este espacio comenzamos con aplicaciones de Hacking, luego incursionamos en la fascinante disciplina de la Robótica y últimamente avanzamos sobre el increíble universo de los Videojuegos.

Anteriormente publicamos juegos (basados en la librería VGAx desarrollada por Sandro Maffiodo) como, por ejemplo:

También construimos juegos más sencillos basados simplemente en LEDs y pulsadores como

Y no hace mucho tiempo atrás comenzamos con la construcción de videojuegos utilizando matrices de LEDs

En esta ocasión continuaremos con la construcción de juegos utilizando matrices de LEDs controladas por el circuito integrado MAX7219.

Tetris es un videojuego de puzzle originalmente diseñado y programado por Alekséi Pázhitnov en la Unión Soviética. Fue lanzado el 6 de junio de 1984, mientras trabajaba para el Centro de Computación Dorodnitsyn de la Academia de Ciencias de la Unión Soviética en Moscú, RSFS de Rusia. Su nombre deriva del prefijo numérico griego tetra- (todas las piezas del juego, conocidas como Tetrominós que contienen cuatro segmentos) y del tenis, el deporte favorito de Pázhitnov.

En el Tetris se juega con los tetrominós, el caso especial de cuatro elementos de poliominós. Los poliominós se han utilizado en los rompecabezas populares por lo menos desde 1907, y el nombre fue dado por el matemático Solomon W. Golomb en 1953. Sin embargo, incluso la enumeración de los pentominós data de la antigüedad.

El juego (o una de sus muchas variantes) está disponible para casi cada consola de videojuegos y sistemas operativos de PC, así como en dispositivos tales como las calculadoras gráficas, teléfonos móviles, reproductores de multimedia portátiles, PDAs, reproductores de música en red e incluso como huevo de pascua en productos no mediáticos como los osciloscopios. También ha inspirado servicios de mesa y ha sido jugado en los costados de varios edificios, manteniendo el récord de ser el juego completamente funcional más grande del mundo gracias al esfuerzo de estudiantes holandeses en 1995 que iluminaron 15 pisos del Departamento de Ingeniería Eléctrica en la Universidad Técnica de Delft.

Mientras que las versiones de Tetris se vendieron para una amplia gama de plataformas de ordenadores domésticos de los años 1980, fue la muy exitosa versión portátil para la Game Boy lanzada en 1989 que estableció al juego como uno de los más populares de todos los tiempos. La edición número 100 del Electronic Gaming Monthly tuvo al Tetris en el primer lugar como el “mejor juego de todos los tiempos”. En 2007, Tetris ocupó el segundo lugar en los “100 mejores videojuegos de todos los tiempos” para IGN. Ha vendido más de 70 millones de copias. En enero de 2010, se anunció que el Tetris ha vendido más de 100 millones de unidades para teléfonos celulares (móviles) sólo desde el año 2005.

Efectos mentales de Tetris

De acuerdo con el Dr. Richard Haier, jugar al Tetris de forma prolongada puede llevar a una actividad cerebral más eficiente durante el juego.

La primera vez que se juega al Tetris, aumentan la función y actividad cerebral, incrementándose también el consumo de energía y glucosa por parte de este. A medida que el jugador de Tetris se vuelve más hábil, el cerebro reduce su consumo de energía y glucosa, indicando una actividad cerebral más eficiente para el juego. Jugar al Tetris de forma moderada (media hora al día por un período de tres meses) incrementa las funciones cognoscitivas tales como “pensamiento crítico, razonamiento, procesamiento del lenguaje”, elevándose también el espesor de la corteza cerebral.

El juego puede provocar que se imaginen combinaciones de Tetris de forma involuntaria aun cuando no se esté jugando (el llamado Efecto Tetris), aunque esto puede ocurrir con cualquier videojuego o situación real que proyecte las imágenes o escenarios de forma repetida, tales como rompecabezas.

La Matriz de LEDs

Una matriz LEDs es un display formado por múltiples LEDs en distribución rectangular. Existen distintos tamaños, siendo el más habitual los cuadrados de 8×8 LEDs.
Podemos combinar varios módulos para formar un display mucho mayor. En estos display podemos mostrar textos, dibujos o animaciones, como desplazar un texto (scroll).
Por lo demás, son diodos LEDs totalmente normales, organizados en forma de matriz, que tendremos que multiplexar para poder iluminar uno u otro punto.
Si los diodos se unen por el positivo, se dice que son matrices de Ánodo común, y si se une por el negativo decimos que son de Cátodo común. Dependiendo del fabricante podemos encontrar de ambos tipos.

Encender una matriz de LEDs directamente con Arduino requiere emplear una gran cantidad de pines, lo cual supondría un gran desperdicio de recursos. Por este motivo, lo normal es siempre utilizar un controlador específicamente diseñado para esta función. Un controlador habitualmente empleado por ser barato y sencillo es el circuito integrado MAX7219.

Circuito Integrado MAX7219

Encender una matriz de 8×8 LED requeriría 16 señales digitales y un trabajo constante del procesador para refrescar la imagen. Eso es una cantidad enorme de recursos para cualquier autómata, que estaríamos desperdiciando para simplemente encender un display.
Por este motivo, utilizamos el circuito integrado MAX7219 que está especialmente diseñado para encender displays de 7 segmentos y matrices de LEDs, liberando al procesador para hacer tareas mucho más valiosas y productivas.
La comunicación con el circuito integrado MAX7219 se realiza mediante el bus SPI por lo que sólo se requieren 3 pines de Arduino (SS, MOSI y SCK). Además, ni siquiera “ocupamos” en su totalidad estos pines, ya que con el mismo bus podemos controlar múltiples dispositivos.
Por último, las placas MAX7219 generalmente incorporan un puerto de entrada y salida, de forma que podemos combinar múltiples controladores sin ninguna dificultad.
MAX7219 Datasheet

El diagrama esquemático de esta variante de Arduino Tetris se observa en la siguiente imagen:

(click para ampliar)

Lista de Componentes
1 Resistencia 10KΩ 0.25W
5 Pulsadores N.A. o Push-Buttons N.A.
1 Transistor NPN 2SC1815
2 Módulos con Matrices de LEDs 8×8 con Circuito Integrado MAX7219 c/u. Cátodo Común [Color Rojo en caso de aplicar Red Mod]
1 Buzzer Pasivo 5V
1 Placa Arduino Nanino
Librería LedControl desarrollada por wayoda

#include <LedControl.h>

LedControl lc = LedControl(12, 11, 10, 2); // (dataPin, clockPin, csPin, totalDevices)

int lc0[] = {0, 0, 0, 0, 0, 0, 0, 0};
int lc1[] = {0, 0, 0, 0, 0, 0, 0, 0};
long active[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
long screen[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int tmpCol = 0;


int figura = 0;
int figuraNext = 0;
int fromLeft = 0;
int fromRight = 0;
int angle = 0;
int colCheck = 0;
int moveCheck = 0;
int score = 0;
int started = 0;
int lcRows = 16;
int lcCols = 8;
int allLines = 0;
int currLines = 0;
int brickDelay = 0;
int defDelay = 500;
int level = 0;

boolean sound = true;
//Pinos

int rotate_button = 2;
int left_button   = 3;
int down_button   = 4;
int right_button  = 5;
int start_button  = 6;
int speaker_pin   = 7;
int sound_button  = 8;

byte X[8] =
{
  0b00000,
  0b10001,
  0b01010,
  0b00100,
  0b01010,
  0b10001,
  0b00000,
  0b00000
};

byte O[8] =
{
  0b00000,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b11111,
  0b00000,
  0b00000
};

byte L[8] =
{
  0b11000,
  0b11000,
  0b11000,
  0b11000,
  0b11000,
  0b11111,
  0b11111,
  0b00000
};

byte J[8] =
{
  0b00011,
  0b00011,
  0b00011,
  0b00011,
  0b00011,
  0b11111,
  0b11111,
  0b00000
};

byte T[8] =
{
  0b00000,
  0b00000,
  0b11111,
  0b11111,
  0b01110,
  0b01110,
  0b00000,
  0b00000
};

byte I[8] =
{
  0b01100,
  0b01100,
  0b01100,
  0b01100,
  0b01100,
  0b01100,
  0b01100,
  0b00000
};

byte Z[8] =
{
  0b00000,
  0b00000,
  0b11110,
  0b11110,
  0b01111,
  0b01111,
  0b00000,
  0b00000
};

byte S[8] =
{
  0b00000,
  0b00000,
  0b01111,
  0b01111,
  0b11110,
  0b11110,
  0b00000,
  0b00000
};

//Nuty
int length = 99;
char notes[] = "EbCDCbaaCEDCbbCDECaa DFAGFEECEDCbbCDECaa EbCDCbaaCEDCbbCDECaa DFAGFEECEDCbbCDECaa ECDbCab ECDbCEAJ ";
int beats[] =      // Som
{
  2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 2,
  2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 1,
  2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 2,
  2, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 2, 2, 2, 4, 1,
  5, 5, 5, 5, 5, 5, 7, 2, 5, 5, 5, 5, 2, 2, 5, 5, 3
};
int tempo = 128;        // Tempo


void playTone(int tone, int duration) {
  for (long i = 0; i < duration * 1000L; i += tone * 2) {
    digitalWrite(speaker_pin, HIGH);
    delayMicroseconds(tone);
    digitalWrite(speaker_pin, LOW);
    delayMicroseconds(tone);
  }
}


void playNote(char note, int duration) {
  char names[] = { 'c', 'd', 'e', 'f', 'g', 'a', 'b', 'C' , 'D', 'E', 'F', 'G', 'J', 'A', 'B'};
  int tones[] = { 1915, 1700, 1519, 1432, 1275, 1136, 1014, 956, 850, 760, 716, 637, 603, 568, 507 };

  for (int i = 0; i < 14; i++) {
    if (names[i] == note) {
      playTone(tones[i], duration);
    }
  }
}


void updateColumn(int colnum)
{
    lc0[colnum] = active[colnum] >> (lcRows / 2);
    lc1[colnum] = active[colnum];
    lc.setColumn(0,colnum,(screen[colnum] >> (lcRows / 2)) | lc0[colnum]);
    lc.setColumn(1,colnum,screen[colnum] | lc1[colnum]);
}



void buttonDelay(int bdelay)
{
    if(brickDelay > bdelay)
    {
        brickDelay -= bdelay;
    }
    delay(bdelay);
}


void splashScreen()
{
     int up[] =
     {
         B11101110,   //   o o o   o o o
         B01001000,   //     o     o
         B01001100,   //     o     o o
         B01001000,   //     o     o
         B01001110,   //     o     o o o
         B00000000,   //
         B11101110,   //   o o o   o o o
         B01001010    //     o     o   o
     };

     int down[] =
     {
         B01001100,   //     o     o o
         B01001010,   //     o     o   o
         B01001001,   //     o     o     o
         B00000000,   //
         B01000111,   //     o       o o o
         B01000100,   //     o       o
         B01000010,   //     o         o
         B01001110    //     o     o o o
     };

     for(int rownum = 0; rownum < 8; rownum++)
     {
         lc.setRow(0,rownum,up[rownum]);
         lc.setRow(1,rownum,down[rownum]);
     }
}


void setup() {
  pinMode(speaker_pin,  OUTPUT);
  pinMode(rotate_button,INPUT_PULLUP);
  pinMode(down_button,  INPUT_PULLUP);
  pinMode(right_button, INPUT_PULLUP);
  pinMode(left_button,  INPUT_PULLUP);
  pinMode(start_button, INPUT_PULLUP);
  pinMode(sound_button, INPUT_PULLUP);

  lc.shutdown(0,false);
  lc.shutdown(1,false);

  lc.setIntensity(0,5);
  lc.setIntensity(1,5);

  lc.clearDisplay(0);
  lc.clearDisplay(1);
  Serial.begin(9600);

  randomSeed(analogRead(0));
}


//LOOP
void loop()
{



 if(started == 0)
 {
     splashScreen();

     for (int i = 0; i < length; i++)
     {

 if(digitalRead(sound_button) == LOW)
 {
  sound =! sound;
  delay(300);
  }

         if(digitalRead(start_button) == LOW)
         {
             started = 1;
             break;
         }
         if (notes[i] == ' ')
         {
             delay(beats[i] * tempo);   //Pausa
         }
         else
         {
          if (sound == 1){
             playNote(notes[i], beats[i] * tempo);
          }
          else
          {
             digitalWrite(speaker_pin,LOW);
            }
         }


        delay(tempo / 2);
     }
 }



 else
 {

     lc.clearDisplay(0);
     lc.clearDisplay(1);
     memset(lc0, 0, sizeof(lc0));
     memset(lc1, 0, sizeof(lc1));
     memset(active, 0, sizeof(active));
     memset(screen, 0, sizeof(screen));
     tmpCol = 0;

     while(started == 1)
     {

         if(allLines < 100)
         {
             level = 0;              //Level 0
         }
         else if(allLines < 200)
         {
             level = 1;              //Level 1
         }
         else if(allLines < 300)
         {
             level = 2;              //Level 2
         }
         else if(allLines < 400)
         {
             level = 3;              //Level 3
         }
         else if(allLines < 500)
         {
             level = 4;              //Level 4
         }
         else
         {
             level = 5;              //Level 5
         }


         defDelay = (5 - level) * 100;
         brickDelay = defDelay;


         if(figura == 0)
         {
              figura = random(1,8);
         }
         else
         {
             figura = figuraNext;
         }
         figuraNext = random(1,8);
         angle = 0;



         switch(figura)
         {
             case 1:
             //"O"
                 active[3] = 131072 + 65536;
                 active[4] = 131072 + 65536;
                 fromLeft = 3;
                 fromRight = 3;
                 break;

             case 2:
             //"L"
                 active[3] = 262144 + 131072 + 65536;
                 active[4] = 65536;
                 fromLeft = 3;
                 fromRight = 3;
                 break;

             case 3:
             //"J"
                 active[3] = 65536;
                 active[4] = 262144 + 131072 + 65536;
                 fromLeft = 3;
                 fromRight = 3;
                 break;

             case 4:
             //"T"
                 active[2] = 131072;
                 active[3] = 131072 + 65536;
                 active[4] = 131072;
                 fromLeft = 2;
                 fromRight = 3;
                 break;

             case 5:
             //"I"
                 active[3] = 524288 + 262144 + 131072 + 65536;
                 fromLeft = 3;
                 fromRight = 4;
                 break;

             case 6:
             //"Z"
                 active[2] = 131072;
                 active[3] = 131072 + 65536;
                 active[4] = 65536;
                 fromLeft = 2;
                 fromRight = 3;
                 break;

             case 7:
             //"S"
                 active[2] = 65536;
                 active[3] = 131072 + 65536;
                 active[4] = 131072;
                 fromLeft = 2;
                 fromRight = 3;
                 break;
         }



         for(int krok = 0; krok < lcRows + 1; krok++)
         {
             colCheck = 0;


             for(int i = 0; i < (lcCols / 2); i++)
             {
                 if((digitalRead(left_button) == LOW) && (fromLeft > 0))
                 {
                     moveCheck = 0;
                     for(int colnum = fromLeft; colnum < (lcCols - fromRight); colnum++)
                     {
                         if((active[colnum] & screen[colnum - 1]) == 0)
                         {
                             moveCheck++;
                         }
                     }

                     if(moveCheck == (lcCols - fromLeft - fromRight))
                     {
                         for(int colnum = (fromLeft - 1); colnum < (lcCols - fromRight); colnum++)
                         {
                             if(colnum < (lcCols - 1))
                             {
                                 active[colnum] = active[colnum+1];
                             }
                             else
                             {
                                 active[colnum] = 0;
                             }
                             updateColumn(colnum);
                         }
                         fromLeft--;
                         fromRight++;
                         playNote('E', 10);
                         buttonDelay(200);
                     }
                 }
             }





             for(int i = 0; i < (lcCols / 2); i++)
             {
                 if((digitalRead(right_button) == LOW) && (fromRight > 0))
                 {
                     moveCheck = 0;
                     for(int colnum = fromLeft; colnum < (lcCols - fromRight); colnum++)
                     {
                         if((active[colnum] & screen[colnum + 1]) == 0)
                         {
                             moveCheck++;
                         }
                     }

                     if(moveCheck == (lcCols - fromLeft - fromRight))
                     {
                         for(int colnum = (lcCols - fromRight); colnum > (fromLeft - 1); colnum--)
                         {
                             if(colnum > 0)
                             {
                                 active[colnum] = active[colnum-1];
                             }
                             else
                             {
                                 active[colnum] = 0;
                             }
                             updateColumn(colnum);
                         }
                         fromLeft++;
                         fromRight--;
                         playNote('E', 10);
                         buttonDelay(200);
                     }
                 }
             }



             if(digitalRead(down_button) == LOW)
             {
                 brickDelay = 0;
                 playNote('b', 10);
             }
             else
             {
                 brickDelay = defDelay;
             }



             for(int i = 0; i < (lcCols / 2); i++)
             {
                 if(digitalRead(rotate_button) == LOW)
                 {

                     switch(figura)
                     {
                         case 1:
                         //"O"
                             break;

                         case 2:
                         //"L"
                             switch(angle)
                             {
                                 case 0:
                                 // . o .        . . .
                                 // . o .  --->  o o o
                                 // . o o        o . .
                                     if((fromLeft > 0)
                                     && (((active[fromLeft + 1] | (active[fromLeft + 1] << 1)) & screen[fromLeft - 1]) == 0))
                                     {
                                         active[fromLeft - 1] = (active[fromLeft + 1] | (active[fromLeft + 1] << 1));
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         fromLeft--;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // . . .        o o .
                                 // o o o  --->  . o .
                                 // o . .        . o .
                                     if((((active[fromLeft + 2] << 1) & screen[fromLeft]) == 0)
                                     && ((((active[fromLeft + 1] << 1) | (active[fromLeft + 1] >> 1)) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft] = (active[fromLeft + 2] << 1);
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = active[fromLeft + 1] | (active[fromLeft + 1] << 1) | (active[fromLeft + 1] >> 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         fromRight++;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // o o .        . . o
                                 // . o .  --->  o o o
                                 // . o .        . . .
                                     if((fromRight > 0)
                                     && (((active[fromLeft] >> 1) & screen[fromLeft]) == 0)
                                     && ((((active[fromLeft + 1] << 1) & active[fromLeft + 1]) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft] = (active[fromLeft] >> 1);
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = active[fromLeft];
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = ((active[fromLeft + 1] << 1) | active[fromLeft + 1]);
                                         updateColumn(fromLeft + 2);
                                         fromRight--;
                                         krok--;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                 // . . o        . o .
                                 // o o o  --->  . o .
                                 // . . .        . o o
                                     if(((((active[fromLeft] << 1) | (active[fromLeft] >> 1)) & screen[fromLeft + 1]) == 0)
                                     && (((active[fromLeft] >> 1) & screen[fromLeft + 2]) == 0)
                                     && (krok < lcRows))
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 2] | (active[fromLeft + 2] >> 1));
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = ((active[fromLeft + 2] >> 1) & (active[fromLeft + 2] >> 2));
                                         updateColumn(fromLeft + 2);
                                         fromLeft++;
                                         krok++;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;

                         case 3:
                         //"J"
                             switch(angle)
                             {
                                 case 0:
                                 // . o .        o . .
                                 // . o .  --->  o o o
                                 // o o .        . . .
                                     if((fromRight > 0)
                                     && ((((active[fromLeft] << 2) | (active[fromLeft] << 1)) & screen[fromLeft]) == 0)
                                     && (((active[fromLeft] << 1) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = ((active[fromLeft] << 2) | (active[fromLeft] << 1));
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = ((active[fromLeft + 1] << 1) & (active[fromLeft + 1] >> 1));
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = active[fromLeft + 1];
                                         updateColumn(fromLeft + 2);
                                         fromRight--;
                                         krok--;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // o . .        . o o
                                 // o o o  --->  . o .
                                 // . . .        . o .
                                     if((krok < lcRows)
                                     && ((((active[fromLeft + 1] << 1) | (active[fromLeft + 1] >> 1)) & screen[fromLeft + 1]) == 0)
                                     && (((active[fromLeft + 2] << 1) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 1] | (active[fromLeft + 1] << 1) | (active[fromLeft + 1] >> 1));
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = (active[fromLeft + 2] << 1);
                                         updateColumn(fromLeft + 2);
                                         fromLeft++;
                                         krok++;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // . o o        . . .
                                 // . o .  --->  o o o
                                 // . o .        . . o
                                     if((fromLeft > 0)
                                     && (((active[fromLeft + 1] >> 1) & screen[fromLeft - 1]) == 0)
                                     && ((((active[fromLeft + 1] >> 1) | (active[fromLeft + 1] >> 2)) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft - 1] = (active[fromLeft + 1] >> 1);
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = active[fromLeft - 1];
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft] | (active[fromLeft + 1] >> 2));
                                         updateColumn(fromLeft + 1);
                                         fromLeft--;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                 // . . .        . o .
                                 // o o o  --->  . o .
                                 // . . o        o o .
                                     if((((active[fromLeft] >> 1) & screen[fromLeft]) == 0)
                                     && ((((active[fromLeft] << 1) | (active[fromLeft >> 1])) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft] = (active[fromLeft] >> 1);
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = ((active[fromLeft + 1] << 1) | active[fromLeft + 2]);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         fromRight++;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;

                         case 4:
                         //"T"
                             switch(angle)
                             {
                                 case 0:
                                 // . . .        . o .
                                 // o o o  --->  o o .
                                 // . o .        . o .
                                     if(((active[fromLeft + 1] << 1) & screen[fromLeft + 1]) == 0)
                                     {
                                         //active[fromLeft]
                                         active[fromLeft + 1] = active[fromLeft + 1] | (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         fromRight++;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // . o .        . o .
                                 // o o .  --->  o o o
                                 // . o .        . . .
                                     if((fromRight > 0)
                                     && ((active[fromLeft] & screen[fromLeft + 2])== 0))
                                     {
                                         //active[fromLeft]
                                         active[fromLeft + 1] = active[fromLeft + 1] & (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = active[fromLeft];
                                         updateColumn(fromLeft + 2);
                                         fromRight--;
                                         krok--;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // . o .        . o .
                                 // o o o  --->  . o o
                                 // . . .        . o .
                                     if((((active[fromLeft + 1] >> 1) & screen[fromLeft + 1]) == 0)
                                     && (krok < lcRows))
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = active[fromLeft + 1] | (active[fromLeft + 1] >> 1);
                                         updateColumn(fromLeft + 1);
                                         //active[fromLeft + 2]
                                         fromLeft++;
                                         krok++;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                     if((fromLeft > 0)
                                     && ((active[fromLeft + 1] & screen[fromLeft - 1]) == 0))
                                     {
                                         active[fromLeft - 1] = active[fromLeft + 1];
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = active[fromLeft] & (active[fromLeft] >> 1);
                                         updateColumn(fromLeft);
                                         fromLeft--;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;

                         case 5:
                         //"I"
                             switch(angle)
                             {
                                 case 0:
                                 // . o . .        . . . .
                                 // . o . .  --->  o o o o
                                 // . o . .        . . . .
                                 // . o . .        . . . .
                                     if((fromLeft > 0)
                                     && (fromRight > 1)

                                     && ((((((active[fromLeft] >> 1) & (active[fromLeft] << 2)) & screen[fromLeft - 1]) & screen[fromLeft + 1]) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft - 1] = ((active[fromLeft] >> 1) & (active[fromLeft] << 2));
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = active[fromLeft - 1];
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = active[fromLeft];
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = active[fromLeft];
                                         updateColumn(fromLeft + 2);
                                         fromLeft--;
                                         fromRight -= 2;
                                         krok -= 2;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // . . . .        . . o .
                                 // o o o o  --->  . . o .
                                 // . . . .        . . o .
                                 // . . . .        . . o .
                                     if((krok < (lcRows - 1))
                                     && (((active[fromLeft] << 1) | (active[fromLeft] >> 1) | (active[fromLeft] >> 2)) & screen[fromLeft + 2]) == 0)
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = 0;
                                         updateColumn(fromLeft + 1);

                                         active[fromLeft + 2] = (active[fromLeft + 2] | (active[fromLeft + 2] << 1) | (active[fromLeft + 2] >> 1) | (active[fromLeft + 2] >> 2));
                                         updateColumn(fromLeft + 2);
                                         active[fromLeft + 3] = 0;
                                         updateColumn(fromLeft + 3);
                                         fromLeft += 2;
                                         fromRight++;
                                         krok += 2;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // . . o .        . . . .
                                 // . . o .  --->  . . . .
                                 // . . o .        o o o o
                                 // . . o .        . . . .
                                     if((fromLeft > 1)
                                     && (fromRight > 0)

                                     && ((((((active[fromLeft] << 1) & (active[fromLeft] >> 2)) & screen[fromLeft - 2]) & screen[fromLeft - 1]) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft - 2] = ((active[fromLeft] << 1) & (active[fromLeft] >> 2));
                                         updateColumn(fromLeft - 2);
                                         active[fromLeft - 1] = active[fromLeft - 2];
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = active[fromLeft - 1];
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = active[fromLeft];
                                         updateColumn(fromLeft + 1);
                                         fromLeft -= 2;
                                         fromRight--;
                                         krok--;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                 // . . . .        . o . .
                                 // . . . .  --->  . o . .
                                 // o o o o        . o . .
                                 // . . . .        . o . .
                                     if((krok < (lcRows))
                                     && (((active[fromLeft] >> 1) | (active[fromLeft] << 1) | (active[fromLeft] << 2)) & screen[fromLeft + 1]) == 0)
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);

                                         active[fromLeft + 1] = (active[fromLeft + 1] | (active[fromLeft + 1] >> 1) | (active[fromLeft + 1] << 1) | (active[fromLeft + 1] << 2));
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         active[fromLeft + 3] = 0;
                                         updateColumn(fromLeft + 3);
                                         fromLeft++;
                                         fromRight += 2;
                                         krok++;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;

                         case 6:
                         //"Z"
                             switch(angle)
                             {
                                 case 0:
                                 // . . .        . o .
                                 // o o .  --->  o o .
                                 // . o o        o . .
                                     if(((active[fromLeft + 1] & screen[fromLeft]) == 0)
                                     && (((active[fromLeft + 1] << 1) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft] = active[fromLeft + 1];
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         fromRight++;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // . o .        o o .
                                 // o o .  --->  . o o
                                 // o . .        . . .
                                     if((fromRight > 0)
                                     && ((((active[fromLeft] << 2) & (active[fromLeft] << 1)) & screen[fromLeft]) == 0)
                                     && (((active[fromLeft] & active[fromLeft + 1]) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = ((active[fromLeft] << 2) & (active[fromLeft] << 1));
                                         updateColumn(fromLeft);
                                         //active[fromLeft + 1]
                                         active[fromLeft + 2] = (active[fromLeft] >> 1);
                                         updateColumn(fromLeft + 2);
                                         fromRight--;
                                         krok--;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // o o .        . . o
                                 // . o o  --->  . o o
                                 // . . .        . o .
                                     if((krok < lcRows)
                                     && (((active[fromLeft + 1] >> 1) & screen[fromLeft + 1]) == 0)
                                     && (((active[fromLeft + 2] << 1) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 1] >> 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = (active[fromLeft + 2] | (active[fromLeft + 2] << 1));
                                         updateColumn(fromLeft + 2);
                                         fromLeft++;
                                         krok++;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                 // . . o        . . .
                                 // . o o  --->  o o .
                                 // . o .        . o o
                                     if((fromLeft > 0)
                                     && (((active[fromLeft] & active[fromLeft + 1]) & screen[fromLeft - 1]) == 0)
                                     && (((active[fromLeft + 1] >> 1) & screen[fromLeft + 1]) == 0))
                                     {
                                         active[fromLeft - 1] = (active[fromLeft] & active[fromLeft + 1]);
                                         updateColumn(fromLeft - 1);
                                         //active[fromLeft]
                                         active[fromLeft + 1] = (active[fromLeft - 1] >> 1);
                                         updateColumn(fromLeft + 1);
                                         fromLeft--;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;

                         case 7:
                         //"S"
                             switch(angle)
                             {
                                 case 0:
                                 // . . .        o . .
                                 // . o o  --->  o o .
                                 // o o .        . o .
                                     if(((active[fromLeft + 1] << 1) & screen[fromLeft]) == 0)
                                     {
                                         active[fromLeft] = (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft);
                                         //active[fromLeft + 1]
                                         active[fromLeft + 2] = 0;
                                         updateColumn(fromLeft + 2);
                                         fromRight++;
                                         angle = 1;
                                     }
                                     break;

                                 case 1:
                                 // o . .        . o o
                                 // o o .  --->  o o .
                                 // . o .        . . .
                                     if((fromRight > 0)
                                     && (((active[fromLeft + 1] << 1) & screen[fromLeft + 1]) == 0)
                                     && (((active[fromLeft] & (active[fromLeft] << 1)) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = (active[fromLeft] & active[fromLeft + 1]);
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft + 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         active[fromLeft + 2] = (active[fromLeft] << 1);
                                         updateColumn(fromLeft + 2);
                                         fromRight--;
                                         krok--;
                                         angle = 2;
                                     }
                                     break;

                                 case 2:
                                 // . o o        . o .
                                 // o o .  --->  . o o
                                 // . . .        . . o
                                     if((krok < lcRows)
                                     && (((active[fromLeft + 1] >> 1) & screen[fromLeft + 2]) == 0))
                                     {
                                         active[fromLeft] = 0;
                                         updateColumn(fromLeft);
                                         //active[fromLeft + 1]
                                         active[fromLeft + 2] = (active[fromLeft + 1] >> 1);
                                         updateColumn(fromLeft + 2);
                                         fromLeft++;
                                         krok++;
                                         angle = 3;
                                     }
                                     break;

                                 case 3:
                                 // . o .        . . .
                                 // . o o  --->  . o o
                                 // . . o        o o .
                                     if((fromLeft > 0)
                                     && ((active[fromLeft + 1] & ((active[fromLeft + 1] >> 1)) & screen[fromLeft - 1]) == 0)
                                     && ((active[fromLeft + 1] & screen[fromLeft]) == 0))
                                     {
                                         active[fromLeft - 1] = (active[fromLeft + 1] & (active[fromLeft + 1] >> 1));
                                         updateColumn(fromLeft - 1);
                                         active[fromLeft] = active[fromLeft + 1];
                                         updateColumn(fromLeft);
                                         active[fromLeft + 1] = (active[fromLeft - 1] << 1);
                                         updateColumn(fromLeft + 1);
                                         fromLeft--;
                                         angle = 0;
                                     }
                                     break;
                             }
                             break;
                     }
                     playNote('E', 10);
                     buttonDelay(200);
                 }
             }


             //Restart
             if(digitalRead(start_button) == LOW)
             {
                 memset(lc0, 0, sizeof(lc0));
                 memset(lc1, 0, sizeof(lc1));
                 memset(active, 0, sizeof(active));
                 memset(screen, 0, sizeof(screen));
                 score = 0;
                 allLines = 0;
                 figura = 0;
                 break;
             }

             for(int colnum = 0; colnum < lcCols; colnum++)
             {

                 if((screen[colnum] & (active[colnum] >> 1)) == 0)
                 {
                     colCheck++;
                 }

                 else
                 {
                     colCheck = 0;
                     if(krok == 0)
                     {
                         started = 0;
                     }
                 }
             }

             if((colCheck == lcCols) && (krok < lcRows))
             {
                 for(int colnum = 0; colnum < lcCols; colnum++)
                 {
                     active[colnum] = active[colnum] >> 1;
                     updateColumn(colnum);
                 }
             }
             else
             {
                 break;
             }
             delay(brickDelay);
         }

         for(int colnum = 0; colnum < lcCols; colnum++)
         {
             screen[colnum] = screen[colnum] | (lc0[colnum] << (lcRows / 2));
             screen[colnum] = screen[colnum] | lc1[colnum];
             lc0[colnum] = 0;
             lc1[colnum] = 0;
             active[colnum] = 0;
         }



         currLines = 0;
         for(int rownum = 0; rownum < lcRows; rownum++)
         {
             colCheck = 0;
             for(int colnum = 0; colnum < lcCols; colnum++)
             {
                 if(((screen[colnum] >> rownum) & 1) == 1)
                 {
                     colCheck++;
                 }
             }
             if(colCheck == lcCols)
             {
                 //Animacja kasowania
                 for(int colnum = 0; colnum < lcCols; colnum++)
                 {
                     tmpCol = ~((int) round(pow(2, rownum)));
                     screen[colnum] = screen[colnum] & tmpCol;
                     updateColumn(colnum);

                     switch(currLines)
                     {
                         case 0:
                             playNote('b', 20);
                             break;
                         case 1:
                             playNote('D', 20);
                             break;
                         case 2:
                             playNote('F', 20);
                             break;
                         case 3:
                             playNote('A', 20);
                             break;
                     }
                     delay(30);

                     tmpCol = (int) (round(pow(2, rownum)) - 1);
                     tmpCol = screen[colnum] & tmpCol;
                     screen[colnum] = (screen[colnum] >> (rownum + 1));
                     screen[colnum] = (screen[colnum] << rownum);
                     screen[colnum] = screen[colnum] | tmpCol;

                 }


                 for(int colnum = 0; colnum < lcCols; colnum++)
                 {
                     updateColumn(colnum);
                 }
                 rownum--;
                 currLines++;
                 allLines++;
             }
         }

         if(currLines > 0)
         {
             score += (int) round(pow(4, currLines-1));
         }
    }

gameOver();
// == Game Over ==

 }
}

void gameOver()
{
  playNote('F', 80);
  playNote('A', 60);
  playNote('F', 80);
  playNote('A', 60);

     int cima[] =
     {
         B11111111,   //     o     o o o
         B11111111,   //   o o o    o
         B11111111,   //   o   o     o o
         B11111111,   //     o     o
         B11111111,   //     o     o o o
         B11111111,   //
         B11111111,   //   o o o   o o o
         B11111111    //     o     o   o
     };

     int baixo[] =
     {
         B11111111,   //     o     o o
         B11111111,   //     o     o   o
         B11111111,   //     o     o     o
         B11111111,   //
         B11111111,   //     o       o o o
         B11111111,   //     o       o
         B11111111,   //     o         o
         B11111111    //     o     o o o
     };

 for(int rownum = 8; rownum >= 0; rownum--)
     {
         lc.setRow(1,rownum,baixo[rownum]);
         delay(100);
     }

     for(int rownum = 8; rownum >= 0; rownum--)
     {
         lc.setRow(0,rownum,cima[rownum]);
         delay(100);
     }

     delay(1800);

}

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

Red Mod
La cinta Kapton es resistente al calor y eléctricamente aislante que se usa mucho en electrónica e impresión 3D. Si has estado en esta afición por un tiempo hay una buena probabilidad de que tengas un rollo a mano.

Una o dos capas de cinta Kapton aplicada a las matrices las hacen menos propensas a lavarse bajo luz brillante. El color de la cinta es lo suficientemente similar a los LEDs rojos que brillan a través con poca dificultad, mientras que la mayoría de la luz ambiente está bloqueada.

La película de enmascarar “Rubylith” (empleada en diseño gráfico) probablemente funcionaría igual de bien, o quizás mejor. Este film puede conseguirse en tiendas de arte decentes de la vieja escuela.

Arduino es una plataforma prácticamente infinita para todo tipo de aplicaciones como lo hemos visto en este humilde blog de entre millones que existen en la web. En este espacio comenzamos con aplicaciones de Hacking, luego incursionamos en la fascinante disciplina de la Robótica y últimamente avanzamos sobre increíble universo de los Videojuegos.

Anteriormente publicamos juegos (basados en la librería VGAx desarrollada por Sandro Maffiodo) como, por ejemplo:

Y juegos más sencillos basados simplemente en LEDs y pulsadores como

En esta ocasión continuaremos con la construcción de juegos sencillos, pero algo más elaborados utilizando matrices de LEDs controladas por el circuito integrado MAX7219.
Pong (o Tele-Pong) fue un videojuego de la primera generación de videoconsolas publicado por Atari, creado por Nolan Bushnell y lanzado el 29 de noviembre de 1972. Pong está basado en el deporte de tenis de mesa (o ping pong). La palabra Pong es una marca registrada por Atari Interactive, mientras que la palabra genérica «pong» es usada para describir el género de videojuegos «bate y bola». La popularidad de Pong dio lugar a una demanda de infracción de patentes y ganada por parte de los fabricantes de Magnavox Odyssey, que poseía un juego similar.
Pong es un juego de deportes en dos dimensiones que simula un tenis de mesa. El jugador controla en el juego una paleta moviéndola verticalmente en la parte izquierda de la pantalla, y puede competir tanto contra un oponente controlado por computadora, como con otro jugador humano que controla una segunda paleta en la parte opuesta. Los jugadores pueden usar las paletas para pegarle a la pelota hacia un lado u otro. El objetivo consiste en que uno de los jugadores consiga más puntos que el oponente al finalizar el juego. Estos puntos se obtienen cuando el jugador adversario falla al devolver la pelota.

Este videojuego es un verdadero clásico y es sumamente adictivo para jugar durante breves intervalos de tiempo. Ya sea esperando un micro o taxi, alguna hora o minutos libres en la Universidad o Colegio, o simplemente de noche hasta que logramos conciliar el sueño.
Esta variante contiene tres niveles de dificultad aumentado la velocidad de desplazamiento de la pelota a medida que avanzamos de nivel. En el último nivel la pelota se desplaza muy rápidamente, no apto para cardíacos, y si logramos superar este nivel ocurre una secuencia que podrán observar en el gameplay que se encuentra al final de la publicación y solo apto para verdaderos fanáticos.

La Matriz de LEDs

Una matriz LEDs es un display formado por múltiples LEDs en distribución rectangular. Existen distintos tamaños, siendo el más habitual los cuadrados de 8×8 LEDs.
Podemos combinar varios módulos para formar un display mucho mayor. En estos display podemos mostrar textos, dibujos o animaciones, como desplazar un texto (scroll).
Por lo demás, son diodos LEDs totalmente normales, organizados en forma de matriz, que tendremos que multiplexar para poder iluminar uno u otro punto.
Si los diodos se unen por el positivo, se dice que son matrices de Ánodo común, y si se une por el negativo decimos que son de Cátodo común. Dependiendo del fabricante podemos encontrar de ambos tipos.

Encender una matriz de LEDs directamente con Arduino requiere emplear una gran cantidad de pines, lo cual supondría un gran desperdicio de recursos. Por este motivo, lo normal es siempre utilizar un controlador específicamente diseñado para esta función. Un controlador habitualmente empleado por ser barato y sencillo es el circuito integrado MAX7219.

Circuito Integrado MAX7219

Encender una matriz de 8×8 LED requeriría 16 señales digitales y un trabajo constante del procesador para refrescar la imagen. Eso es una cantidad enorme de recursos para cualquier autómata, que estaríamos desperdiciando para simplemente encender un display.
Por este motivo, utilizamos el circuito integrado MAX7219 que está especialmente diseñado para encender displays de 7 segmentos y matrices de LEDs, liberando al procesador para hacer tareas mucho más valiosas y productivas.
La comunicación con el circuito integrado MAX7219 se realiza mediante el bus SPI por lo que sólo se requieren 3 pines de Arduino (SS, MOSI y SCK). Además, ni siquiera “ocupamos” en su totalidad estos pines, ya que con el mismo bus podemos controlar múltiples dispositivos.
Por último, las placas MAX7219 generalmente incorporan un puerto de entrada y salida, de forma que podemos combinar múltiples controladores sin ninguna dificultad.
MAX7219 Datasheet

El diagrama esquemático de esta variante de Arduino Pong se observa en la siguiente imagen:

(click para ampliar)

Lista de Componentes
1 Resistencia 10KΩ 0.25W
1 Potenciómetro Lineal 10KΩ
1 Transistor NPN 2SC1815
2 Módulos con Matrices de LEDs 8×8 con Circuito Integrado MAX7219 c/u. Cátodo Común [Color Rojo en caso de aplicar Red Mod]
1 Buzzer Pasivo 5V
1 Placa Arduino Nanino
Librería LedControl desarrollada por wayoda

#include <LedControl.h>

#define DATA_PIN    12
#define CLOCK_PIN   11
#define CS_PIN      10
#define NUM_DEVICES 2
#define KNOB_PIN    A0
#define RANDOM_PIN  A1
#define SPEAKER_PIN 7

#define N_a   440
#define N_f   349
#define N_cH  523
#define N_eH  659
#define N_fH  698
#define N_gS  415

int step_col, step_row, subidas, OLD_X, sensor, row, col, sc, sr, dly;
int row_bar = 0;
int X = 4;
int cnt = 0;
int desarma = 0;

boolean showBars_enabled = false;

LedControl LC = LedControl(DATA_PIN, CLOCK_PIN, CS_PIN, NUM_DEVICES);

byte MSG[8 * 20];
byte SPACE[8] = {
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000
};
byte W[8] = {
    B00000000,
    B01000010,
    B01000010,
    B01000010,
    B01000010,
    B01011010,
    B01100110,
    B00000000
};
byte I[8] = {
    B00000000,
    B00111100,
    B00011000,
    B00011000,
    B00011000,
    B00011000,
    B00111100,
    B00000000
};
byte N[8] = {
    B00000000,
    B01000010,
    B01100010,
    B01010010,
    B01001010,
    B01000110,
    B01000010,
    B00000000
};
byte E[8] = {
    B00000000,
    B01111110,
    B01000000,
    B01111100,
    B01000000,
    B01000000,
    B01111110,
    B00000000
};
byte R[8] = {
    B00000000,
    B01111100,
    B01000010,
    B01111100,
    B01001000,
    B01000100,
    B01000010,
    B00000000
};

void loose()
{
    delay(80);

    for(row = 0; (row < 16); row++) {
        setROW(row, 0xFF);
        tone(SPEAKER_PIN, (row * 500), 10);
        delay(20);
    }

    delay(130);

    for(row = 15; (row >= 0); row--) {
        setROW(row, 0x00);
        tone(SPEAKER_PIN, (row * 500), 10);
        delay(20);
    }
}

void showBars(boolean show)
{
    showBars_enabled = show;

    if(show) {
        row_bar = (2 + random(5));
    }

    setROW(row_bar, (show == true ? 0xFF : 0x00));
}

void setLED(int l, int c, boolean state)
{
    int disp = 0;

    if(l > 7) {
        l = (l - 8);
        disp = 1;
    }

    LC.setLed(disp, l, c, state);
}

void setROW(int r, char ch)
{
    int disp = 0;

    if(r > 7) {
        r = (r - 8);
        disp = 1;
    }

    LC.setRow(disp, r, ch);
}

void copyLetter(byte *ch,int pos)
{
    int i;

    for(i = 0; (i < 8); i++) {
        *(MSG + i + (pos * 8)) = *( ch + i);
    }
}

void showMSG(int qtde, int tempo)
{
    int idx;

    LC.clearDisplay(0);
    LC.clearDisplay(1);

    for(idx = 0; (idx < (8 * qtde)); idx++) {
        for(row = 0; (row < 16); row++) {
            setROW(row, *(MSG + row + idx));
        }
		
        delay(tempo);
    }

    LC.clearDisplay(0);
    LC.clearDisplay(1);
}

void beep(int frequencyInHertz, long timeInMilliseconds)
{
    int x;
    long delayAmount = (long)(1000000 / frequencyInHertz);
    long loopTime = (long)((timeInMilliseconds * 1000) / (delayAmount * 2));

    for(row = 0; (row < 16); row++) {
        setROW(row,0xFF);
    }

    for (x = 0; (x < loopTime); x++) {
        digitalWrite(SPEAKER_PIN, HIGH);
        delayMicroseconds(delayAmount);
        digitalWrite(SPEAKER_PIN, LOW);
        delayMicroseconds(delayAmount);
    }

    LC.clearDisplay(0);
    LC.clearDisplay(1);

    delay(20);
}

void march()
{
    beep(N_a, 500);
    beep(N_a, 500);
    beep(N_a, 500);
    beep(N_f, 350);
    beep(N_cH, 150);

    beep(N_a, 500);
    beep(N_f, 350);
    beep(N_cH, 150);
    beep(N_a, 1000);

    beep(N_eH, 500);
    beep(N_eH, 500);
    beep(N_eH, 500);
    beep(N_fH, 350);
    beep(N_cH, 150);

    beep(N_gS, 500);
    beep(N_f, 350);
    beep(N_cH, 150);
    beep(N_a, 1000);
}

void winner()
{
    copyLetter(SPACE, 0);
    copyLetter(SPACE, 1);

    copyLetter(W, 2);
    copyLetter(I, 3);
    copyLetter(N, 4);
    copyLetter(N, 5);
    copyLetter(E, 6);
    copyLetter(R, 7);

    copyLetter(SPACE, 8);
    copyLetter(SPACE, 9);

    showMSG(9, 80);

    march();

    setup();
}

void setup() {
    pinMode(KNOB_PIN, INPUT);      // Potentiometer 10K Linear (GND, KNOB_PIN, +5v)
	pinMode(RANDOM_PIN, INPUT);    // Random Seed
    pinMode(SPEAKER_PIN, OUTPUT);  // Passive Buzzer

    LC.shutdown(0, false);
    LC.setIntensity(0, 8);
    LC.clearDisplay(0);

    LC.shutdown(1, false);
    LC.setIntensity(1, 8);
    LC.clearDisplay(1);

    randomSeed(analogRead(RANDOM_PIN) * millis());

    loose();

    dly = 500;

    setLED(15, X, true);
    setLED(15, (X + 1), true);
    setLED(15, (X + 2) ,true);
    OLD_X = (-1);
    subidas = 0;

    step_col = 1;
    step_row = 1;

    sc = step_col;
    sr = step_row;

    row = ((-1) + random(3));
    col = random(8);

    showBars(false);
}

void loop()
{
    sensor = analogRead(KNOB_PIN);

    X = map(sensor, 0, 980, 1, 6);

    if(X != OLD_X) {
        OLD_X = X;
        setROW(15, 0x00);
        setLED(15, (X - 1),true);
        setLED(15, X, true);
        setLED(15, (X + 1), true);
    }

    if(cnt == 0) {
        setLED(row, col, false);

        if((subidas == 1 || random(2) == 1) && showBars_enabled == false && row == 0) {
            showBars(true);
            subidas = 0;
            desarma = (2 + random(5));
        }

        if((subidas == desarma) && (showBars_enabled == true)) {
            showBars(false);
            subidas = 0;
        }

        if(col == 7) {
            sc = -step_col;
            tone(SPEAKER_PIN, 1000, 20);
        }

        if(col == 0) {
            sc = step_col;
            tone(SPEAKER_PIN, 1000, 20);
        }

        if(row == 0) {
            sr = step_row;
            subidas++;
            dly -= 5;
            tone(SPEAKER_PIN, 1000, 20);
        }

        if(dly <= 190) {
            winner();
        }

        if(showBars_enabled == true && sr > 0 && row < row_bar) {
            if(row == (row_bar - 1)) {
                sr = -step_row;
                tone(SPEAKER_PIN, 1000, 20);
            }
        }

        if(row == 14)
        {
            if(col >= (X - 1) && col <= (X + 1)) {
                sr = -step_row;
				tone(SPEAKER_PIN, 1500, 20);
			}
            else if(col == (X - 2) && (sc > 0)) {
                sr = -step_row;
                sc = -step_col;
                tone(SPEAKER_PIN, 1500, 20);
            }
            else if(col == (X + 2) && (sc < 0)) {
                sr = -step_row;
                sc = step_col;
                tone(SPEAKER_PIN, 1500, 20);
            }
        }

        row += sr;
        col += sc;

        if(col == 8) {
            col = 7;
        }

        if(col == (-1)) {
            col = 0;
        }

        setLED(row, col, true);

        if(row == 15) {
            setup();
        }
    }

    cnt++;

    if(cnt == dly) {
        cnt = 0;
    }
}

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

(click para ampliar)

Red Mod
La cinta Kapton es resistente al calor y eléctricamente aislante que se usa mucho en electrónica e impresión 3D. Si has estado en esta afición por un tiempo hay una buena probabilidad de que tengas un rollo a mano.

Una o dos capas de cinta Kapton aplicada a las matrices las hacen menos propensas a lavarse bajo luz brillante. El color de la cinta es lo suficientemente similar a los LEDs rojos que brillan a través con poca dificultad, mientras que la mayoría de la luz ambiente está bloqueada.

La película de enmascarar “Rubylith” (empleada en diseño gráfico) probablemente funcionaría igual de bien, o quizás mejor. Este film puede conseguirse en tiendas de arte decentes de la vieja escuela.

La memoria, en sentido general, es una función del cerebro y a la misma vez un proceso psíquico que nos permite codificar, almacenar y posteriormente recuperar la información o las vivencias. La memoria surge como resultado de las conexiones sinápticas entre neuronas que, a lo largo del tiempo, crean una serie de redes neuronales de forma que los recuerdos se mantienen relativamente estables en el tiempo.

La memoria es el único paraíso del que no podemos ser expulsados.
(Jean Paul)

En relación con el alcance temporal de la memoria ésta se clasifica en: memoria a corto plazo, a mediano plazo y a largo plazo.

La memoria a corto plazo tiene una capacidad muy limitada, tanto en volumen de almacenamiento como en tiempo de permanencia. La información se puede mantener en la memoria a corto plazo mediante la repetición.

Por ejemplo, cuando nos dicen un número de teléfono y lo vamos repitiendo hasta encontrar un papel donde apuntarlo.

Si uno presta atención a esta información y la elabora (analiza, comprende, relaciona con otras ideas) puede pasar de la memoria a corto plazo a la memoria a largo plazo.

La memoria a largo plazo es prácticamente ilimitada tanto en capacidad como en duración.

La memoria a corto plazo, también denominada “memoria operativa”, es un sistema a partir del cual la persona maneja la información que se obtiene de una interacción directa con el ambiente que le rodea. Generalmente esta información se encuentra limitada a 7 elementos, con una variación de más o menos 2 ítems y puede mantenerse durante una media que oscila entre los 15 y los 30 segundos. No obstante, la memoria a corto plazo se verá más o menos limitada en relación con las capacidades de cada persona y el entrenamiento que ha realizado a lo largo de la vida.

Generalmente la memoria a corto plazo funciona a partir de tres principios muy sencillos: el efecto de primacía, el efecto de recencia y la significatividad.

El efecto de primacía hace referencia al hecho de que las personas recuerdan mejor las cosas que suceden inicialmente (ya sea los primeros ítems de una lista o las primeras palabras de una conversación). El efecto de recencia, al contrario, se refiere a nuestra excelente memoria para los hechos o datos que se presentan al final de una lista o una situación. Así, la tendencia de la memoria a corto plazo será la de transferir a la memoria a largo plazo los datos primeros o últimos; obviando buena parte de los hechos o datos intermedios. No obstante, si las informaciones intermedias tienen un gran significado emocional para las personas, entonces éstas adquirirán la primacía absoluta.

La memoria a corto plazo cumple varias funciones:

  • Retención de la información por un periodo corto de tiempo.
  • Apoyo al aprendizaje del nuevo conocimiento.
  • Comprensión del ambiente.
  • Facilitación del proceso de solución de problemas.

En esta ocasión crearemos un juego con Arduino para entrenar y mejorar el rendimiento de nuestra memoria a corto plazo.

El circuito esquemático de este juego de entrenamiento de la memoria se observa en la siguiente imagen:

Lista de Componentes
5 Resistencias 10KΩ 0.25W
4 Resistencias 390Ω 0.25W
1 LED 5mm Azul [Alto brillo]
1 LED 5mm Amarillo [Alto brillo]
1 LED 5mm Verde [Alto brillo]
1 LED 5mm Rojo [Alto brillo]
4 Pulsadores N.A. o Push-Button N.A.
1 Transistor NPN 2SC1815
1 Buzzer Pasivo 5V
1 Placa Arduino Nanino

/*
  Memory Game with Arduino
  Based on a project by Jeremy Wilson
  Modified by Rui Santos
  Visit: http://randomnerdtutorials.com
*/

#define BLUE_BUTTON     2
#define YELLOW_BUTTON   3
#define GREEN_BUTTON    4
#define RED_BUTTON      5
#define BLUE_LED        7
#define YELLOW_LED      8
#define GREEN_LED       9
#define RED_LED         10
#define BUZZER          12

// Constants
const int tones[] = {1915, 1700, 1519, 1432, 2700}; // tones when you press the LED's - the last one is when you fail.

// Variables
int buttonState[] = {0, 0, 0, 0};         // current state of the button
int lastButtonState[] = {0, 0, 0, 0};     // previous state of the button
int buttonPushCounter[] = {0, 0, 0, 0};
int game_on = 0;
int wait = 0;
int currentlevel = 1; // This is the level (also the number of button presses to pass to next level)
int rando = 0; //initialize random integer for loopgame_on. Will be from 1-4 later.
int butwait = 500; //amount of time to wait for next button input (ghetto de-bounce)
int ledtime = 500; //amount of time each LED flashes for when button is pressed
int n_levels = 10; //number of levels until the game is won
int pinandtone = 0; //This integer is used when the sequence is displayed
int right = 0; //This variable must be 1 in order to go to the next level
int speedfactor = 5; //This is the final speed of the lights and sounds for the last level. This increases as more games are won
int leddelay = 200; //Initializing time for LED. This will decrease as the level increases

long rand_num = 0; //initialize long variable for random number from 0-100.

void playTone(int tone, int duration) {
    for(long i = 0; i < duration * 1000L; i += tone * 2) {
        digitalWrite(BUZZER, HIGH);
        delayMicroseconds(tone);
        digitalWrite(BUZZER, LOW);
        delayMicroseconds(tone);
    }
}

void setup() {
    // initialize inputs:
    randomSeed(analogRead(0));
    pinMode(BLUE_BUTTON, INPUT);
    pinMode(YELLOW_BUTTON, INPUT);
    pinMode(GREEN_BUTTON, INPUT);
    pinMode(RED_BUTTON, INPUT);

    // initialize outputs:
    pinMode(BLUE_LED, OUTPUT);
    pinMode(YELLOW_LED, OUTPUT);
    pinMode(GREEN_LED, OUTPUT);
    pinMode(RED_LED, OUTPUT);
    pinMode(BUZZER, OUTPUT);
}

void loop() {

    int n_array[n_levels];
    int u_array[n_levels];
    int i;

    //clears arrays both "n_array" and "u_array" and starts a new game
    if(game_on == 0) {
        for(i = 0; i < n_levels; i = i + 1) {
            n_array[i] = 0;
            u_array[i] = 0;
            rand_num = random(1, 200);

            if(rand_num <= 50)
                rando = 0;
            else if(rand_num > 50 && rand_num <= 100)
                rando = 1;
            else if(rand_num > 100 && rand_num <= 150)
                rando = 2;
            else if(rand_num <= 200)
                rando = 3;
            //saves a random number in our n_array
            n_array[i] = rando;
        }
        game_on = 1;
    }

    //shows the user the current sequence
    if(wait == 0) {
        delay(200);
        i = 0;

        for(i = 0; i < currentlevel; i = i + 1) {
            leddelay = ledtime/(1+(speedfactor/n_levels)*(currentlevel - 1));
            pinandtone = n_array[i];
            digitalWrite(pinandtone+7, HIGH);
            playTone(tones[pinandtone], leddelay);
            digitalWrite(pinandtone+7, LOW);
            delay(100/speedfactor);
        }
        wait = 1;
    }

    i = 0;
    int buttonchange = 0;
    int j = 0; // This is the current position in the sequence

    while(j < currentlevel) {
        while(buttonchange == 0) {
            for(i = 0; i < 4; i = i + 1) {
            buttonState[i] = digitalRead(i + 2);
            buttonchange = buttonchange + buttonState[i];
            }
        }
        for(i = 0; i < 4; i = i + 1) {
            if(buttonState[i] == HIGH) {
                digitalWrite(i+7, HIGH);
                playTone(tones[i], ledtime);
                digitalWrite(i+7, LOW);
                wait = 0;
                u_array[j] = i;
                buttonState[i] = LOW;
                buttonchange = 0;
            }
        }
        if(u_array[j] == n_array[j]) {
            j++;
            right = 1;
        }
        else {
            right = 0;
            i = 4;
            j = currentlevel;
            wait = 0;
        }
    }

    if(right == 0) {
        delay(300);
        i = 0;
        game_on = 0;
        currentlevel = 1;
        for(i = 0; i < 4; i = i + 1) {
            digitalWrite(i + 7, HIGH);
        }
        playTone(tones[4], ledtime);
        for(i = 0; i < 4; i = i + 1) {
            digitalWrite(i + 7, LOW);
        }
        delay(200);
        for(i = 0; i < 4; i = i + 1) {
            digitalWrite(i+7, HIGH);
        }
        playTone(tones[4], ledtime);
        for(i = 0; i < 4; i = i + 1) {
            digitalWrite(i + 7, LOW);
        }

        delay(500);
        game_on = 0;
    }

    //if you insert the right sequence it levels up
    if(right == 1) {
        currentlevel++;
        wait = 0;
    }

    //if you finish the game
    if(currentlevel == n_levels) {
        delay(500);
        // The following is the victory sound:

        int notes[] = {2, 2, 2, 2, 0, 1, 2, 1, 2};
        int note = 0;
        int tempo[] = {200, 200, 200, 400, 400, 400, 200, 200, 600};
        int breaks[] = {100, 100, 100, 200, 200, 200, 300, 100, 200};

        for(i = 0; i < 9; i = i + 1) {
            note = notes[i];
            digitalWrite(note + 7, HIGH);
            playTone(tones[note], tempo[i]);
            digitalWrite(note + 7, LOW);
            delay(breaks[i]);
        }

        //sets game_on to 0, so it restarts a new game
        game_on = 0;
        currentlevel = 1;
        n_levels = n_levels + 2;
        speedfactor = speedfactor + 1;
    }
}

(click para ampliar)

(click para ampliar)

  • Agradecimientos: A mi prometida por haber grabado el siguiente video demo, por su tolerancia y comprensión. Sin ella nada de esto sería posible. TE AMO POR Y PARA SIEMPRE M.L.V!!! ❤ ❤ ❤ ❤
  • El circuito esquemático de Arduino Snake se observa en la siguiente imagen:

    (click para ampliar)

    Lista de Componentes
    2 Resistencias 470Ω 0.25W
    2 Resistencias 68Ω 0.25W
    4 Resistencias 10KΩ 0.25W
    4 Pulsadores N.A o Push-Button N.A
    1 Conector VGA Hembra para chasis
    1 Placa Arduino Nanino
    Librería VGAx desarrollada por Sandro Maffiodo

    /*
      Arduino Snake for VGA monitor - by Roberto Melzi.
      Version 1.0 - October the 10th 2015
    */
    
    //A1: Right (Derecha)
    //A2: Up (Arriba)
    //A3: Left (Izquierda)
    //A4: Down (Abajo)
    
    #include <VGAX.h>
    #include <VGAXUtils.h>
    #include <math.h>
    
    #define FNT_NANOFONT_HEIGHT        6
    #define FNT_NANOFONT_SYMBOLS_COUNT 95
    
    #define BUTTON_1 A1 //digital
    #define BUTTON_2 A2 //digital
    #define BUTTON_3 A3 //digital
    #define BUTTON_4 A4 //digital
    // NB: pin A0 is used for the audio!
    
    VGAX vga;
    VGAXUtils vgaU;
    
    //data size=570 bytes
    const unsigned char fnt_nanofont_data[FNT_NANOFONT_SYMBOLS_COUNT][1+FNT_NANOFONT_HEIGHT] PROGMEM={
    { 1, 128, 128, 128, 0, 128, 0, }, //glyph '!' code=0
    { 3, 160, 160, 0, 0, 0, 0, }, //glyph '"' code=1
    //{ 5, 80, 248, 80, 248, 80, 0, },  //glyph '#' code=2-
    { 5, 248, 248, 248, 248, 248, 0, },  //glyph '#' code=2 - full rectangle
    { 5, 120, 160, 112, 40, 240, 0, },  //glyph '$' code=3
    { 5, 136, 16, 32, 64, 136, 0, },  //glyph '%' code=4
    { 5, 96, 144, 104, 144, 104, 0, },  //glyph '&' code=5
    { 2, 128, 64, 0, 0, 0, 0, },  //glyph ''' code=6
    { 2, 64, 128, 128, 128, 64, 0, }, //glyph '(' code=7
    { 2, 128, 64, 64, 64, 128, 0, },  //glyph ')' code=8
    { 3, 0, 160, 64, 160, 0, 0, },  //glyph '*' code=9
    { 3, 0, 64, 224, 64, 0, 0, }, //glyph '+' code=10
    { 2, 0, 0, 0, 0, 128, 64, },  //glyph ',' code=11
    { 3, 0, 0, 224, 0, 0, 0, }, //glyph '-' code=12
    { 1, 0, 0, 0, 0, 128, 0, }, //glyph '.' code=13
    { 5, 8, 16, 32, 64, 128, 0, },  //glyph '/' code=14
    { 4, 96, 144, 144, 144, 96, 0, }, //glyph '0' code=15
    { 3, 64, 192, 64, 64, 224, 0, },  //glyph '1' code=16
    { 4, 224, 16, 96, 128, 240, 0, }, //glyph '2' code=17
    { 4, 224, 16, 96, 16, 224, 0, },  //glyph '3' code=18
    { 4, 144, 144, 240, 16, 16, 0, }, //glyph '4' code=19
    { 4, 240, 128, 224, 16, 224, 0, },  //glyph '5' code=20
    { 4, 96, 128, 224, 144, 96, 0, }, //glyph '6' code=21
    { 4, 240, 16, 32, 64, 64, 0, }, //glyph '7' code=22
    { 4, 96, 144, 96, 144, 96, 0, },  //glyph '8' code=23
    { 4, 96, 144, 112, 16, 96, 0, },  //glyph '9' code=24
    { 1, 0, 128, 0, 128, 0, 0, }, //glyph ':' code=25
    { 2, 0, 128, 0, 0, 128, 64, },  //glyph ';' code=26
    { 3, 32, 64, 128, 64, 32, 0, }, //glyph '<' code=27
    { 3, 0, 224, 0, 224, 0, 0, }, //glyph '=' code=28
    { 3, 128, 64, 32, 64, 128, 0, },  //glyph '>' code=29
    { 4, 224, 16, 96, 0, 64, 0, },  //glyph '?' code=30
    { 4, 96, 144, 176, 128, 112, 0, },  //glyph '@' code=31
    { 4, 96, 144, 240, 144, 144, 0, },  //glyph 'A' code=32
    { 4, 224, 144, 224, 144, 224, 0, }, //glyph 'B' code=33
    { 4, 112, 128, 128, 128, 112, 0, }, //glyph 'C' code=34
    { 4, 224, 144, 144, 144, 224, 0, }, //glyph 'D' code=35
    { 4, 240, 128, 224, 128, 240, 0, }, //glyph 'E' code=36
    { 4, 240, 128, 224, 128, 128, 0, }, //glyph 'F' code=37
    { 4, 112, 128, 176, 144, 112, 0, }, //glyph 'G' code=38
    { 4, 144, 144, 240, 144, 144, 0, }, //glyph 'H' code=39
    { 3, 224, 64, 64, 64, 224, 0, },  //glyph 'I' code=40
    { 4, 240, 16, 16, 144, 96, 0, },  //glyph 'J' code=41
    { 4, 144, 160, 192, 160, 144, 0, }, //glyph 'K' code=42
    { 4, 128, 128, 128, 128, 240, 0, }, //glyph 'L' code=43
    { 5, 136, 216, 168, 136, 136, 0, }, //glyph 'M' code=44
    { 4, 144, 208, 176, 144, 144, 0, }, //glyph 'N' code=45
    { 4, 96, 144, 144, 144, 96, 0, }, //glyph 'O' code=46
    { 4, 224, 144, 224, 128, 128, 0, }, //glyph 'P' code=47
    { 4, 96, 144, 144, 144, 96, 16, },  //glyph 'Q' code=48
    { 4, 224, 144, 224, 160, 144, 0, }, //glyph 'R' code=49
    { 4, 112, 128, 96, 16, 224, 0, }, //glyph 'S' code=50
    { 3, 224, 64, 64, 64, 64, 0, }, //glyph 'T' code=51
    { 4, 144, 144, 144, 144, 96, 0, },  //glyph 'U' code=52
    { 3, 160, 160, 160, 160, 64, 0, },  //glyph 'V' code=53
    { 5, 136, 168, 168, 168, 80, 0, },  //glyph 'W' code=54
    { 4, 144, 144, 96, 144, 144, 0, },  //glyph 'X' code=55
    { 3, 160, 160, 64, 64, 64, 0, },  //glyph 'Y' code=56
    { 4, 240, 16, 96, 128, 240, 0, }, //glyph 'Z' code=57
    { 2, 192, 128, 128, 128, 192, 0, }, //glyph '[' code=58
    { 5, 128, 64, 32, 16, 8, 0, },  //glyph '\' code=59
    { 2, 192, 64, 64, 64, 192, 0, },  //glyph ']' code=60
    { 5, 32, 80, 136, 0, 0, 0, }, //glyph '^' code=61
    { 4, 0, 0, 0, 0, 240, 0, }, //glyph '_' code=62
    { 2, 128, 64, 0, 0, 0, 0, },  //glyph '`' code=63
    { 3, 0, 224, 32, 224, 224, 0, },  //glyph 'a' code=64
    { 3, 128, 224, 160, 160, 224, 0, }, //glyph 'b' code=65
    { 3, 0, 224, 128, 128, 224, 0, }, //glyph 'c' code=66
    { 3, 32, 224, 160, 160, 224, 0, },  //glyph 'd' code=67
    { 3, 0, 224, 224, 128, 224, 0, }, //glyph 'e' code=68
    { 2, 64, 128, 192, 128, 128, 0, },  //glyph 'f' code=69
    { 3, 0, 224, 160, 224, 32, 224, },  //glyph 'g' code=70
    { 3, 128, 224, 160, 160, 160, 0, }, //glyph 'h' code=71
    { 1, 128, 0, 128, 128, 128, 0, }, //glyph 'i' code=72
    { 2, 0, 192, 64, 64, 64, 128, },  //glyph 'j' code=73
    { 3, 128, 160, 192, 160, 160, 0, }, //glyph 'k' code=74
    { 1, 128, 128, 128, 128, 128, 0, }, //glyph 'l' code=75
    { 5, 0, 248, 168, 168, 168, 0, }, //glyph 'm' code=76
    { 3, 0, 224, 160, 160, 160, 0, }, //glyph 'n' code=77
    { 3, 0, 224, 160, 160, 224, 0, }, //glyph 'o' code=78
    { 3, 0, 224, 160, 160, 224, 128, }, //glyph 'p' code=79
    { 3, 0, 224, 160, 160, 224, 32, },  //glyph 'q' code=80
    { 3, 0, 224, 128, 128, 128, 0, }, //glyph 'r' code=81
    { 2, 0, 192, 128, 64, 192, 0, },  //glyph 's' code=82
    { 3, 64, 224, 64, 64, 64, 0, }, //glyph 't' code=83
    { 3, 0, 160, 160, 160, 224, 0, }, //glyph 'u' code=84
    { 3, 0, 160, 160, 160, 64, 0, },  //glyph 'v' code=85
    { 5, 0, 168, 168, 168, 80, 0, },  //glyph 'w' code=86
    { 3, 0, 160, 64, 160, 160, 0, },  //glyph 'x' code=87
    { 3, 0, 160, 160, 224, 32, 224, },  //glyph 'y' code=88
    { 2, 0, 192, 64, 128, 192, 0, },  //glyph 'z' code=89
    { 3, 96, 64, 192, 64, 96, 0, }, //glyph '{' code=90
    { 1, 128, 128, 128, 128, 128, 0, }, //glyph '|' code=91
    { 3, 192, 64, 96, 64, 192, 0, },  //glyph '}' code=92
    { 3, 96, 192, 0, 0, 0, 0, },  //glyph '~' code=93
    { 4, 48, 64, 224, 64, 240, 0, },  //glyph '£' code=94
    };
    
    static const char str0[] PROGMEM="0";
    static const char str1[] PROGMEM="1";
    static const char str2[] PROGMEM="2";
    static const char str3[] PROGMEM="3";
    static const char str4[] PROGMEM="4";
    static const char str5[] PROGMEM="5";
    static const char str6[] PROGMEM="6";
    static const char str7[] PROGMEM="7";
    static const char str8[] PROGMEM="8";
    static const char str9[] PROGMEM="9";
    static const char str10[] PROGMEM="#";
    static const char str20[] PROGMEM="Arduino VGA Snake";
    static const char str21[] PROGMEM="by Roberto Melzi";
    static const char str22[] PROGMEM="Game Over";
    static const char str23[] PROGMEM="Score";
    
    boolean button1 = 0;
    boolean button2 = 0;
    boolean button3 = 0;
    boolean button4 = 0;
    boolean button;
    int counterMenu = 0;
    int counterMenu2 = 0;
    int state = 1;
    int score = 0;
    int scoreMax = 9;
    int foodX = 60;
    int foodY = 30;
    int sx[31];
    int sy[31];
    int slength = 3;
    int wleft = 36;
    int i;
    int x;
    int y;
    int direct = 3;
    int speedDelay = 100;
    
    void foodIni() {
      foodX = random(VGAX_WIDTH - 4 - wleft) + 2 + wleft;
      foodY = random(VGAX_HEIGHT - 4) + 2;
      // ------------ choose the following for food up to the border -----------------------------------------
      // foodX = random(VGAX_WIDTH - 2 - wleft) + 1 + wleft;
      // foodY = random(VGAX_HEIGHT - 2) + 1;
    }
    
    void processInputs() {
      button1 = digitalRead(BUTTON_1);
      button2 = digitalRead(BUTTON_2);
      button3 = digitalRead(BUTTON_3);
      button4 = digitalRead(BUTTON_4);
      button = button1 | button2 | button3 | button4;
    }
    
    void drawMenu() {
      counterMenu2++;
      vga.delay(10);
      if (counterMenu2 > 50){
        counterMenu++;
        vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str20, 26, 16, (counterMenu%3) + 1);
        vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str21, 28, 28, (counterMenu%3) + 1);
        counterMenu2 = 0;
      }
    }
    
    void drawBorder() {
        vgaU.draw_line(wleft, 0, VGAX_WIDTH-1, 0, 3);
        vgaU.draw_line(wleft, VGAX_HEIGHT-1, VGAX_WIDTH-1, VGAX_HEIGHT-1, 3);
        vgaU.draw_line(wleft, 0, wleft, VGAX_HEIGHT-1, 3);
        vgaU.draw_line(VGAX_WIDTH-1, 0, VGAX_WIDTH-1, VGAX_HEIGHT, 3);
    }
    
    void drawScore() {
      vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str23, 10, 3, 2);
      vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str10, 20, 10, 0);
      if (score == 0) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str0, 20, 10, 2);}
      if (score == 1) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str1, 20, 10, 2);}
      if (score == 2) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str2, 20, 10, 2);}
      if (score == 3) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str3, 20, 10, 2);}
      if (score == 4) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str4, 20, 10, 2);}
      if (score == 5) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str5, 20, 10, 2);}
      if (score == 6) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str6, 20, 10, 2);}
      if (score == 7) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str7, 20, 10, 2);}
      if (score == 8) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str8, 20, 10, 2);}
      if (score == 9) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str9, 20, 10, 2);}
      if (score == 10) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str0, 20, 10, 2);}
      if (score == 11) {vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str1, 20, 10, 2);}
    }
    
    // this is for the beginning game window ---------------------------------------------------------------------------------------
    void drawStartScreen() {
       vga.clear(0);
       drawBorder();
       drawSnakeIni();
       drawScore();
       button = 0;
       vga.delay(200);
    }
    
    void drawSnakeIni() {
       for (int i = 0; i < slength ; i++) {
          sx[i] = 80 + i;
          sy[i] = 30;
          vga.putpixel(sx[i], sy[i], 2);
       }
       vga.putpixel(foodX, foodY, 1);
    }
    
    // re-inizialize new match -------------------------------------------------------------------------------------------------------
    void newMatch(){
      score = 0;
      slength = 3;
      i = slength - 1;
      vga.clear(0);
      drawBorder();
      drawScore();
      vga.putpixel(foodX, foodY, 1);
    }
    
    void setup() {
      pinMode(BUTTON_1, INPUT);
      pinMode(BUTTON_2, INPUT);
      pinMode(BUTTON_3, INPUT);
      pinMode(BUTTON_4, INPUT);
      pinMode(A5, INPUT);
    
      vga.begin();
      //randomSeed(1);
      randomSeed(analogRead(5));
    }
    
    // This is the main loop of the game ------------------------------------------------------------
    void loop() {
    
      processInputs();
    
      if(state == 1) {
         drawMenu();
         vga.delay(10);
         processInputs();
         if (button == 1){
            button = 0;
            vga.clear(0);
            drawStartScreen();
            state = 2;
         }
      }
    
     if(state == 2){
         if(score == scoreMax || score == 0){
            processInputs();
         }
         if (button == 1){
            score = 0;
            drawScore();
            button = 0;
            button1 = 0;
            button2 = 0;
            button3 = 0;
            button4 = 0;
            direct = 3;
            x = -1;
            y = 0;
            i = slength - 1;
            state = 3;
         }
      }
    
      if(state == 3) {
    
         processInputs();
    
    //-------------------- change direction --------------------------------------------
         if (direct == 1){
            if (button2 == 1){ x = 0; y = -1; direct = 2; button4 = 0;}
            if (button4 == 1){ x = 0; y = +1; direct = 4;}
         }
         else {
            if (direct == 2){
               if (button1 == 1){ x = +1; y = 0; direct = 1; button3 = 0;}
               if (button3 == 1){ x = -1; y = 0; direct = 3;}
            }
            else {
               if (direct == 3){
                  if (button2 == 1){ x = 0; y = -1; direct = 2; button4 = 0;}
                  if (button4 == 1){ x = 0; y = +1; direct = 4;}
               }
               else {
                  if (direct == 4){
                     if (button1 == 1){ x = +1; y = 0; direct = 1; button3 = 0;}
                     if (button3 == 1){ x = -1; y = 0; direct = 3;}
                  }
               }
            }
         }
    
    //----------------------- delete tail --------------------------------------
         vga.putpixel(sx[i], sy[i], 0);
         if ( i == slength - 1){
            sx[i] = sx[0] + x;
            sy[i] = sy[0] + y;
         }
         else {
            sx[i] = sx[i + 1] + x;
            sy[i] = sy[i + 1] + y;
         }
    
    //--------------------- out from border ------------------------------------
    /*
         if(sx[i] < wleft + 1) {sx[i] = VGAX_WIDTH - 2;}
         if(sx[i] > VGAX_WIDTH - 2) {sx[i] = wleft + 1;}
         if(sy[i] < 1) {sy[i] = VGAX_HEIGHT - 2;}
         if(sy[i] > VGAX_HEIGHT - 2) {sy[i] = 1;}
    */
    //--------------------- check eating food -----------------------------------
         if ( sx[i] == foodX && sy[i] == foodY){
         //if (vga.getpixel(sx[i], sy[i]) == 1 ){
    
            foodIni();
            vga.putpixel(foodX, foodY, 1);
            slength = slength + 3;
            score++;
            if (score > scoreMax) {
               speedDelay = speedDelay - 20;
               newMatch();
            }
            drawScore();
         }
    
    //----------------------- increase head and Game Over -------------------------------------
         if (vga.getpixel(sx[i], sy[i]) == 0 || vga.getpixel(sx[i], sy[i]) == 1 ){
            vga.putpixel(sx[i], sy[i], 2);
         }
         else //------------------ Game Over --------------------------------------------------
         {
            vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str22, 58, 24, 1);
            vga.delay(1000);
            button == 0;
            while(button == 0){processInputs();}
            speedDelay = 100;
            newMatch();
            drawSnakeIni();
         }
    
         i--;
         if ( i < 0){i = slength - 1;}
    
         vga.delay(speedDelay);
      }
    }
    

    (click para ampliar)

    (click para ampliar)

    (click para ampliar)

    (click para ampliar)

    El circuito esquemático de Arduino Bomber se observa en la siguiente imagen:

    (click para ampliar)

    Lista de Componentes
    2 Resistencias 470Ω 0.25W
    2 Resistencias 68Ω 0.25W
    2 Resistencias 10KΩ 0.25W
    1 Potenciómetro Lineal 10KΩ
    1 Pulsador N.A. Rojo [tamaño grande]
    1 Buzzer Pasivo 5V
    1 Transistor NPN 2SC1815
    1 Conector VGA Hembra para chasis
    1 Placa Arduino Nanino
    Librería VGAx desarrollada por Sandro Maffiodo

    // Arduino Bomber for VGA output - Version 2.0, using VGAUtils.h
    // written by Rob Cai - June the 14th, 2015
    
    //A0: 5V Passive Buzzer (Buzzer Pasivo 5V)
    //A1: 10K Lineal Potentiometer (Potenciometro Lineal 10K)
    //A2: Push-Button (Pulsador N.A.)
    
    #include <VGAX.h>
    #include <VGAXUtils.h>
    #include <math.h>
    
    #define FNT_NANOFONT_HEIGHT        6
    #define FNT_NANOFONT_SYMBOLS_COUNT 95
    //#define BUTTON_PIN A0 //NB: by default, this pin (A0) is used in the VGAx library to generate the audio.
                          //    It is better to connect the button to another pin, for instance pin 2 (A2)
                          //    and use the next line insted of this one.
    #define WHEEL_PIN  A1
    #define BUTTON_PIN A2
    
    VGAX vga;
    VGAXUtils vgaU;
    
    //data size=570 bytes
    const unsigned char fnt_nanofont_data[FNT_NANOFONT_SYMBOLS_COUNT][1+FNT_NANOFONT_HEIGHT] PROGMEM={
    { 1, 128, 128, 128, 0, 128, 0, }, //glyph '!' code=0
    { 3, 160, 160, 0, 0, 0, 0, }, //glyph '"' code=1
    { 5, 32, 160, 240, 120, 32, 32, },  //glyph '#' = 'planeR' code=2
    { 3, 80, 32, 112, 112, 32, 0, },  //glyph '$' = 'bomb' code=3
    { 5, 32, 40, 120, 240, 32, 32, },  //glyph '%' = 'planeL' code=4
    { 5, 96, 144, 104, 144, 104, 0, },  //glyph '&' code=5
    { 5, 248, 248, 248, 248, 248, 248, },  //glyph ''' = 'rectangle 5 x 6' code=6
    { 2, 64, 128, 128, 128, 64, 0, }, //glyph '(' code=7
    { 2, 128, 64, 64, 64, 128, 0, },  //glyph ')' code=8
    { 3, 0, 160, 64, 160, 0, 0, },  //glyph '*' code=9
    { 3, 0, 64, 224, 64, 0, 0, }, //glyph '+' code=10
    { 2, 0, 0, 0, 0, 128, 64, },  //glyph ',' code=11
    { 3, 0, 0, 224, 0, 0, 0, }, //glyph '-' code=12
    { 1, 0, 0, 0, 0, 128, 0, }, //glyph '.' code=13
    { 5, 8, 16, 32, 64, 128, 0, },  //glyph '/' code=14
    { 4, 96, 144, 144, 144, 96, 0, }, //glyph '0' code=15
    { 3, 64, 192, 64, 64, 224, 0, },  //glyph '1' code=16
    { 4, 224, 16, 96, 128, 240, 0, }, //glyph '2' code=17
    { 4, 224, 16, 96, 16, 224, 0, },  //glyph '3' code=18
    { 4, 144, 144, 240, 16, 16, 0, }, //glyph '4' code=19
    { 4, 240, 128, 224, 16, 224, 0, },  //glyph '5' code=20
    { 4, 96, 128, 224, 144, 96, 0, }, //glyph '6' code=21
    { 4, 240, 16, 32, 64, 64, 0, }, //glyph '7' code=22
    { 4, 96, 144, 96, 144, 96, 0, },  //glyph '8' code=23
    { 4, 96, 144, 112, 16, 96, 0, },  //glyph '9' code=24
    { 1, 0, 128, 0, 128, 0, 0, }, //glyph ':' code=25
    { 2, 0, 128, 0, 0, 128, 64, },  //glyph ';' code=26
    { 3, 32, 64, 128, 64, 32, 0, }, //glyph '<' code=27
    { 3, 0, 224, 0, 224, 0, 0, }, //glyph '=' code=28
    { 3, 128, 64, 32, 64, 128, 0, },  //glyph '>' code=29
    { 4, 224, 16, 96, 0, 64, 0, },  //glyph '?' code=30
    { 4, 96, 144, 176, 128, 112, 0, },  //glyph '@' code=31
    { 4, 96, 144, 240, 144, 144, 0, },  //glyph 'A' code=32
    { 4, 224, 144, 224, 144, 224, 0, }, //glyph 'B' code=33
    { 4, 112, 128, 128, 128, 112, 0, }, //glyph 'C' code=34
    { 4, 224, 144, 144, 144, 224, 0, }, //glyph 'D' code=35
    { 4, 240, 128, 224, 128, 240, 0, }, //glyph 'E' code=36
    { 4, 240, 128, 224, 128, 128, 0, }, //glyph 'F' code=37
    { 4, 112, 128, 176, 144, 112, 0, }, //glyph 'G' code=38
    { 4, 144, 144, 240, 144, 144, 0, }, //glyph 'H' code=39
    { 3, 224, 64, 64, 64, 224, 0, },  //glyph 'I' code=40
    { 4, 240, 16, 16, 144, 96, 0, },  //glyph 'J' code=41
    { 4, 144, 160, 192, 160, 144, 0, }, //glyph 'K' code=42
    { 4, 128, 128, 128, 128, 240, 0, }, //glyph 'L' code=43
    { 5, 136, 216, 168, 136, 136, 0, }, //glyph 'M' code=44
    { 4, 144, 208, 176, 144, 144, 0, }, //glyph 'N' code=45
    { 4, 96, 144, 144, 144, 96, 0, }, //glyph 'O' code=46
    { 4, 224, 144, 224, 128, 128, 0, }, //glyph 'P' code=47
    { 4, 96, 144, 144, 144, 96, 16, },  //glyph 'Q' code=48
    { 4, 224, 144, 224, 160, 144, 0, }, //glyph 'R' code=49
    { 4, 112, 128, 96, 16, 224, 0, }, //glyph 'S' code=50
    { 3, 224, 64, 64, 64, 64, 0, }, //glyph 'T' code=51
    { 4, 144, 144, 144, 144, 96, 0, },  //glyph 'U' code=52
    { 3, 160, 160, 160, 160, 64, 0, },  //glyph 'V' code=53
    { 5, 136, 168, 168, 168, 80, 0, },  //glyph 'W' code=54
    { 4, 144, 144, 96, 144, 144, 0, },  //glyph 'X' code=55
    { 3, 160, 160, 64, 64, 64, 0, },  //glyph 'Y' code=56
    { 4, 240, 16, 96, 128, 240, 0, }, //glyph 'Z' code=57
    { 2, 192, 128, 128, 128, 192, 0, }, //glyph '[' code=58
    { 5, 128, 64, 32, 16, 8, 0, },  //glyph '\' code=59
    { 2, 192, 64, 64, 64, 192, 0, },  //glyph ']' code=60
    { 5, 32, 80, 136, 0, 0, 0, }, //glyph '^' code=61
    { 4, 0, 0, 0, 0, 240, 0, }, //glyph '_' code=62
    { 2, 128, 64, 0, 0, 0, 0, },  //glyph '`' code=63
    { 3, 0, 224, 32, 224, 224, 0, },  //glyph 'a' code=64
    { 3, 128, 224, 160, 160, 224, 0, }, //glyph 'b' code=65
    { 3, 0, 224, 128, 128, 224, 0, }, //glyph 'c' code=66
    { 3, 32, 224, 160, 160, 224, 0, },  //glyph 'd' code=67
    { 3, 0, 224, 224, 128, 224, 0, }, //glyph 'e' code=68
    { 2, 64, 128, 192, 128, 128, 0, },  //glyph 'f' code=69
    { 3, 0, 224, 160, 224, 32, 224, },  //glyph 'g' code=70
    { 3, 128, 224, 160, 160, 160, 0, }, //glyph 'h' code=71
    { 1, 128, 0, 128, 128, 128, 0, }, //glyph 'i' code=72
    { 2, 0, 192, 64, 64, 64, 128, },  //glyph 'j' code=73
    { 3, 128, 160, 192, 160, 160, 0, }, //glyph 'k' code=74
    { 1, 128, 128, 128, 128, 128, 0, }, //glyph 'l' code=75
    { 5, 0, 248, 168, 168, 168, 0, }, //glyph 'm' code=76
    { 3, 0, 224, 160, 160, 160, 0, }, //glyph 'n' code=77
    { 3, 0, 224, 160, 160, 224, 0, }, //glyph 'o' code=78
    { 3, 0, 224, 160, 160, 224, 128, }, //glyph 'p' code=79
    { 3, 0, 224, 160, 160, 224, 32, },  //glyph 'q' code=80
    { 3, 0, 224, 128, 128, 128, 0, }, //glyph 'r' code=81
    { 2, 0, 192, 128, 64, 192, 0, },  //glyph 's' code=82
    { 3, 64, 224, 64, 64, 64, 0, }, //glyph 't' code=83
    { 3, 0, 160, 160, 160, 224, 0, }, //glyph 'u' code=84
    { 3, 0, 160, 160, 160, 64, 0, },  //glyph 'v' code=85
    { 5, 0, 168, 168, 168, 80, 0, },  //glyph 'w' code=86
    { 3, 0, 160, 64, 160, 160, 0, },  //glyph 'x' code=87
    { 3, 0, 160, 160, 224, 32, 224, },  //glyph 'y' code=88
    { 2, 0, 192, 64, 128, 192, 0, },  //glyph 'z' code=89
    { 3, 96, 64, 192, 64, 96, 0, }, //glyph '{' code=90
    { 1, 128, 128, 128, 128, 128, 0, }, //glyph '|' code=91
    { 3, 192, 64, 96, 64, 192, 0, },  //glyph '}' code=92
    { 3, 96, 192, 0, 0, 0, 0, },  //glyph '~' code=93
    { 4, 48, 64, 224, 64, 240, 0, },  //glyph '£' code=94
    };
    
    static const char str1[] PROGMEM="#"; //planeR
    static const char str2[] PROGMEM="%"; //planeL
    static const char str3[] PROGMEM="$"; //bomb
    static const char str4[] PROGMEM="'"; //rectangle 5 x 6
    static const char str9[] PROGMEM="'''''''"; //clear 'Hit' banner
    static const char str10[] PROGMEM="Hit !!!"; //'Hit' banner
    static const char str11[] PROGMEM="Arduino VGA Bomber";
    static const char str12[] PROGMEM="by Roberto Melzi";
    static const char str13[] PROGMEM="VGAx library";
    static const char str14[] PROGMEM="by Sandro Maffiodo";
    static const char str15[] PROGMEM="Game Over!!!";
    
    void line(float x1, float y1, float x2, float y2, int c){
      int divisions = int(1.1*1*sqrt(pow((y2 - y1),2) + pow((x2 - x1),2)));
      for (int i = 0; i <= divisions; i++){
        int x = int(x1 + i*(x2 - x1)/divisions);
        int y = int(y1 + i*(y2 - y1)/divisions);
        if(x > 0 && x < VGAX_WIDTH && y > 0 && y < VGAX_HEIGHT){
          vga.putpixel(x, y, c);
        }
      }
    }
    
    void cls(){
       vga.clear(0);
    }
    
    int shot = 12; //number of framer before next shot is possible ---------------------
    unsigned char x,y;
    boolean buttonStatus = 0;
    int wheelPosition;
    int BomberX;
    float bombX;
    float bombY;
    float bombV0;
    float bombV0y;
    float bombX0;
    float bombY0;
    float aereoX;
    float aereoY;
    float Vaereo;
    int ricarica = 0;
    int aereoColpito = 0;
    int bombaColpita = 1;
    int t = 0;
    int score = 40;
    int maxScore = 116;
    int level = 0;
    int frame = 0;
    int state = 1;
    float bombX_0;
    float bombY_0;
    int planeX;
    int planeY;
    int planeX0;
    int planeY0;
    int BomberX0;
    float radius;
    float x0;
    float y0;
    float x1;
    float y1;
    int color;
    int counterMenu = 0;
    int counterMenu2 = 0;
    
    void parameterIni() {
      aereoX = 10 + random(VGAX_WIDTH - 20);
      aereoY = 10 + random(VGAX_HEIGHT/4);
      bombX0 = aereoX;
      bombY0 = aereoY + 4;
      Vaereo = 0.1 + random(20)/100;
      bombV0 = Vaereo - random(-10,10)/100.;
      bombV0y = random(10)/100.;
      score = 40;
      level = 0;
      buttonStatus = 0;
      counterMenu = 0;
    }
    
    void processInputs() {
      wheelPosition = analogRead(WHEEL_PIN);
      buttonStatus = digitalRead(BUTTON_PIN) ;
    }
    
    void drawMenu() {
      counterMenu2++;
      vga.delay(10);
      if (counterMenu2 > 50){
        counterMenu++;
        vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str11, 20, 12, (counterMenu%3) + 1);
        vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str12, 30, 22, ((counterMenu + 1)%3) + 1);
        vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str13, 20, 32, (counterMenu%3) + 1);
        vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str14, 30, 42, ((counterMenu + 2)%3) + 1);
        counterMenu2 = 0;
      }
    }
    
    // this is the main function to draw the game screen
    void drawGameScreen() {
       //draw plane ----------------------------------------------
       if(Vaereo > 0 && planeX != planeX0) {
          vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str4, planeX0, planeY0, 0);
          vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str1, planeX, planeY, 1);
       }
       if(Vaereo < 0 && planeX != planeX0) {
          vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str4, planeX0, planeY0, 0);
          vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str2, planeX, planeY, 1);
       }
       //draw bomb -----------------------------------------------
       vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str3, bombX_0, bombY_0, 0);
       vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str3, bombX, bombY, 2);
       //draw score line ----------------------------------------
       vgaU.draw_line(VGAX_WIDTH - (maxScore - 1),1,VGAX_WIDTH-score-1,1,0);
       vga.putpixel(VGAX_WIDTH - maxScore,1,(level + 2)%3 + 1);
       vgaU.draw_line(VGAX_WIDTH - 1 ,1,VGAX_WIDTH-score,1,3);
       //draw ground ---------------------------------------------
       vgaU.draw_row(VGAX_HEIGHT-1,VGAX_WIDTH,1,2);
       //draw bombard --------------------------------------------
       //BomberX = int((((1024 - wheelPosition) / 8) * (VGAX_WIDTH - 12))/ 128 + 6);
       if (BomberX != BomberX0) {
          vgaU.draw_column(BomberX0, VGAX_HEIGHT-4, VGAX_HEIGHT-7, 0);
          vgaU.draw_row(VGAX_HEIGHT-4, BomberX0-2, BomberX0+3, 0);
          vgaU.draw_row(VGAX_HEIGHT-3, BomberX0-5, BomberX0+6, 0);
          vgaU.draw_row(VGAX_HEIGHT-2, BomberX0-5, BomberX0+6, 0);
    
          vgaU.draw_column(BomberX, VGAX_HEIGHT-4, VGAX_HEIGHT-7, 3);
          vgaU.draw_row(VGAX_HEIGHT-4, BomberX-2, BomberX+3, 3);
          vgaU.draw_row(VGAX_HEIGHT-3, BomberX-5, BomberX+6, 3);
          vgaU.draw_row(VGAX_HEIGHT-2, BomberX-5, BomberX+6, 3);
       }
       // draw shot ----------------------------------------------
       if ( ricarica == shot - 1) {
          line(BomberX0,VGAX_HEIGHT-8,BomberX0,0,0);
          //vgaU.draw_column(BomberX0, 1, VGAX_HEIGHT-8, 0);
          //vgaU.draw_line(BomberX0, 2, BomberX0, VGAX_HEIGHT-8, 0);
       }
       if ( ricarica == shot) {
          line(BomberX,VGAX_HEIGHT-8,BomberX,0,1);
          //vgaU.draw_column(BomberX, 1, VGAX_HEIGHT-8, 1);
          //vgaU.draw_line(BomberX, 2, BomberX, VGAX_HEIGHT-8, 1);
       }
    }
    
    void setup() {
      //Serial.begin(9600); // this gives flickers...
      pinMode(WHEEL_PIN, INPUT);
      pinMode(BUTTON_PIN, INPUT);
    
      vga.begin();
    }
    
    void loop() {
    
      processInputs();
    
      if(state == 1) {
         drawMenu();
         vga.delay(10);
      }
    
      if (buttonStatus == 1 && state == 1){
         buttonStatus == 0;
         vga.clear(0);
         parameterIni();
         state = 2;
      }
    
      if(state == 2) {
    
         // this is exectuted just after a shot -----------------------
         if (ricarica == shot) {
            if (BomberX > aereoX - 2 - level && BomberX < aereoX + 2 + level) { // plane is hit ------------------
               aereoX = -10;
               score += 8;
               vga.tone(110);
               vga.delay(60);
               vga.noTone();
             }
             if (BomberX > (bombX - 2 ) && BomberX < (bombX + 2)) { // bomb is hit ----------------
                //bombX0 = 2*VGAX_WIDTH;
                //bombY0 = -100.;
                //bombV0 = 0;
                bombY0 = aereoY + 4;
                bombX0 = aereoX;
                bombV0 = Vaereo - 0.1*level + random(-100,100)/1000.;
                bombV0y = 0.1*level + random(100)/1000.;
                t = 0;
                score += 8;
                vga.tone(165);
                vga.delay(60);
                vga.noTone();
              }
         }
    
         // plane has escaped ----------------------------------------------------
         if(aereoX > VGAX_WIDTH || aereoX < 0) {
            score += -4 - level;
            Vaereo = random(200)/1000.0 + 0.2 + 0.2*level;
            aereoY = random(VGAX_HEIGHT*2/3*100)/100.0 + 2;
            if(random(1000) > 500)
               {aereoX = 10;}
            else {
               aereoX = VGAX_WIDTH-10;
               Vaereo = -Vaereo;
            }
         }
    
         // plane position -------------------------------------------------------
         aereoX += Vaereo;
    
         // bomb position and trajectory -----------------------------------------
            bombX = bombX0 + bombV0*t;
            bombY = bombY0 + bombV0y*t + t*t/(600 - 200*level);
            if(bombY > VGAX_HEIGHT - 5 || bombX < 0 || bombX > VGAX_WIDTH){
              if(bombX >= BomberX - 6 && bombX < BomberX + 6){
                 //Bomber has been hit -------------------------------------------
                 vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str10, 50, 10, 1);
                 vga.tone(880);
                 vga.delay(200);
                 vga.tone(440);
                 vga.delay(200);
                 vga.tone(220);
                 vga.delay(200);
                 vga.tone(110);
                 vga.delay(200);
                 vga.noTone();
                 vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str9, 50, 10, 0);
                 vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str9, 51, 10, 0);
                 score += -30;
              }
              bombY0 = aereoY + 4;
              bombX0 = aereoX;
              bombV0 = Vaereo - 0.1*level + random(-100,100)/1000.;
              bombV0y = 0.1*level + random(100)/1000.;
              t = 0;
           }
           else
           {t = t + 1;}
    
           // check the shot ---------------------------------------------------------
           if(ricarica != 0 ) {
              ricarica += -1;
           }
           if ( buttonStatus == 1 && ricarica == 0) {
              score += -1;
              ricarica = shot;
              BomberX0 = BomberX;
              buttonStatus = 0;
            }
    
            // check score state and Game Over --------------------------------------
            if(score < 0) {
               buttonStatus == 0;
               vga.delay(300);
               vga.tone(660);
               vga.delay(200);
               vga.tone(330);
               vga.delay(200);
               vga.tone(165);
               vga.delay(200);
               vga.tone(82);
               vga.delay(200);
               vga.noTone();
               state = 3;
            }
            if (score > maxScore) {
              score = maxScore/2;
              vga.tone(440);
              vga.delay(100);
              vga.tone(880);
              vga.delay(100);
              vga.noTone();
              level++;
            }
    
         planeX = int(aereoX);
         planeY = int(aereoY);
         BomberX = int((((1024 - wheelPosition) / 8) * (VGAX_WIDTH - 12))/ 128 + 6);
    
         drawGameScreen();
    
         bombX_0 = bombX;
         bombY_0 = bombY;
         planeX0 = planeX;
         planeY0 = planeY;
         BomberX0 = BomberX;
       }
    
       if(state == 3){
         vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str15, 40, 20, 1);
         vga.delay(200);
         if(buttonStatus == 1){
            parameterIni();
            state = 1;
            vga.clear(0);
         }
       }
    }
    

    (click para ampliar)

    (click para ampliar)

    (click para ampliar)

    (click para ampliar)

    (click para ampliar)

    Tetris es un videojuego de puzzle originalmente diseñado y programado por Alekséi Pázhitnov en la Unión Soviética. Fue lanzado el 6 de junio de 1984, mientras trabajaba para el Centro de Computación Dorodnitsyn de la Academia de Ciencias de la Unión Soviética en Moscú, RSFS de Rusia. Su nombre deriva del prefijo numérico griego tetra- (todas las piezas del juego, conocidas como Tetrominós que contienen cuatro segmentos) y del tenis, el deporte favorito de Pázhitnov.

    En el Tetris se juega con los tetrominós, el caso especial de cuatro elementos de poliominós. Los poliominós se han utilizado en los rompecabezas populares por lo menos desde 1907, y el nombre fue dado por el matemático Solomon W. Golomb en 1953. Sin embargo, incluso la enumeración de los pentominós data de la antigüedad.

    El juego (o una de sus muchas variantes) está disponible para casi cada consola de videojuegos y sistemas operativos de PC, así como en dispositivos tales como las calculadoras gráficas, teléfonos móviles, reproductores de multimedia portátiles, PDAs, reproductores de música en red e incluso como huevo de pascua en productos no mediáticos como los osciloscopios. También ha inspirado servicios de mesa y ha sido jugado en los costados de varios edificios, manteniendo el récord de ser el juego completamente funcional más grande del mundo gracias al esfuerzo de estudiantes holandeses en 1995 que iluminaron 15 pisos del Departamento de Ingeniería Eléctrica en la Universidad Técnica de Delft.

    Mientras que las versiones de Tetris se vendieron para una amplia gama de plataformas de ordenadores domésticos de los años 1980, fue la muy exitosa versión portátil para la Game Boy lanzada en 1989 que estableció al juego como uno de los más populares de todos los tiempos. La edición número 100 del Electronic Gaming Monthly tuvo al Tetris en el primer lugar como el “mejor juego de todos los tiempos”. En 2007, Tetris ocupó el segundo lugar en los “100 mejores videojuegos de todos los tiempos” para IGN. Ha vendido más de 70 millones de copias. En enero de 2010, se anunció que el Tetris ha vendido más de 100 millones de unidades para teléfonos celulares (móviles) sólo desde el año 2005.

    Efectos mentales de Tetris

    De acuerdo con el Dr. Richard Haier, jugar al Tetris de forma prolongada puede llevar a una actividad cerebral más eficiente durante el juego.

    La primera vez que se juega al Tetris, aumentan la función y actividad cerebral, incrementándose también el consumo de energía y glucosa por parte de este. A medida que el jugador de Tetris se vuelve más hábil, el cerebro reduce su consumo de energía y glucosa, indicando una actividad cerebral más eficiente para el juego. Jugar al Tetris de forma moderada (media hora al día por un período de tres meses) incrementa las funciones cognoscitivas tales como “pensamiento crítico, razonamiento, procesamiento del lenguaje”, elevándose también el espesor de la corteza cerebral.

    El juego puede provocar que se imaginen combinaciones de Tetris de forma involuntaria aun cuando no se esté jugando (el llamado Efecto Tetris), aunque esto puede ocurrir con cualquier videojuego o situación real que proyecte las imágenes o escenarios de forma repetida, tales como rompecabezas.

    El circuito esquemático de Arduino Tetris se observa en la siguiente imagen:

    (click para ampliar)

    Lista de Componentes
    2 Resistencias 470Ω 0.25W
    2 Resistencias 68Ω 0.25W
    4 Resistencias 10KΩ 0.25W
    4 Pulsadores N.A o Push-Button N.A
    1 Conector VGA Hembra para chasis
    1 Placa Arduino Nanino
    Librería VGAx desarrollada por Sandro Maffiodo

    // VGA_Tetris.beta.ino
    //
    // Arduino Tetris for VGA output - beta Version
    // written by Roberto Melzi - December the 21th, 2015
    
    //A1: Right (Derecha)
    //A2: Rotate (Rotar)
    //A3: Left (Izquierda)
    //A4: Down (Abajo)
    
    #include <VGAXUtils.h>
    #include <math.h>
    
    #define  FNT_NANOFONT_HEIGHT        6
    #define  FNT_NANOFONT_SYMBOLS_COUNT 95
    
    VGAX vga;
    VGAXUtils vgaU;
    
    //data size=570 bytes
    const unsigned char fnt_nanofont_data[FNT_NANOFONT_SYMBOLS_COUNT][1+FNT_NANOFONT_HEIGHT] PROGMEM={
    { 1, 128, 128, 128, 0, 128, 0, }, //glyph '!' code=0
    { 3, 160, 160, 0, 0, 0, 0, }, //glyph '"' code=1
    //{ 5, 80, 248, 80, 248, 80, 0, },  //glyph '#' code=2
    { 5, 248, 248, 248, 248, 248, 0, },  //glyph '#' code=2 - full rectangle
    { 5, 120, 160, 112, 40, 240, 0, },  //glyph '$' code=3
    { 5, 136, 16, 32, 64, 136, 0, },  //glyph '%' code=4
    { 5, 96, 144, 104, 144, 104, 0, },  //glyph '&' code=5
    { 2, 128, 64, 0, 0, 0, 0, },  //glyph ''' code=6
    { 2, 64, 128, 128, 128, 64, 0, }, //glyph '(' code=7
    { 2, 128, 64, 64, 64, 128, 0, },  //glyph ')' code=8
    { 3, 0, 160, 64, 160, 0, 0, },  //glyph '*' code=9
    { 3, 0, 64, 224, 64, 0, 0, }, //glyph '+' code=10
    { 2, 0, 0, 0, 0, 128, 64, },  //glyph ',' code=11
    { 3, 0, 0, 224, 0, 0, 0, }, //glyph '-' code=12
    { 1, 0, 0, 0, 0, 128, 0, }, //glyph '.' code=13
    { 5, 8, 16, 32, 64, 128, 0, },  //glyph '/' code=14
    { 4, 96, 144, 144, 144, 96, 0, }, //glyph '0' code=15
    { 3, 64, 192, 64, 64, 224, 0, },  //glyph '1' code=16
    { 4, 224, 16, 96, 128, 240, 0, }, //glyph '2' code=17
    { 4, 224, 16, 96, 16, 224, 0, },  //glyph '3' code=18
    { 4, 144, 144, 240, 16, 16, 0, }, //glyph '4' code=19
    { 4, 240, 128, 224, 16, 224, 0, },  //glyph '5' code=20
    { 4, 96, 128, 224, 144, 96, 0, }, //glyph '6' code=21
    { 4, 240, 16, 32, 64, 64, 0, }, //glyph '7' code=22
    { 4, 96, 144, 96, 144, 96, 0, },  //glyph '8' code=23
    { 4, 96, 144, 112, 16, 96, 0, },  //glyph '9' code=24
    { 1, 0, 128, 0, 128, 0, 0, }, //glyph ':' code=25
    { 2, 0, 128, 0, 0, 128, 64, },  //glyph ';' code=26
    { 3, 32, 64, 128, 64, 32, 0, }, //glyph '<' code=27
    { 3, 0, 224, 0, 224, 0, 0, }, //glyph '=' code=28
    { 3, 128, 64, 32, 64, 128, 0, },  //glyph '>' code=29
    { 4, 224, 16, 96, 0, 64, 0, },  //glyph '?' code=30
    { 4, 96, 144, 176, 128, 112, 0, },  //glyph '@' code=31
    { 4, 96, 144, 240, 144, 144, 0, },  //glyph 'A' code=32
    { 4, 224, 144, 224, 144, 224, 0, }, //glyph 'B' code=33
    { 4, 112, 128, 128, 128, 112, 0, }, //glyph 'C' code=34
    { 4, 224, 144, 144, 144, 224, 0, }, //glyph 'D' code=35
    { 4, 240, 128, 224, 128, 240, 0, }, //glyph 'E' code=36
    { 4, 240, 128, 224, 128, 128, 0, }, //glyph 'F' code=37
    { 4, 112, 128, 176, 144, 112, 0, }, //glyph 'G' code=38
    { 4, 144, 144, 240, 144, 144, 0, }, //glyph 'H' code=39
    { 3, 224, 64, 64, 64, 224, 0, },  //glyph 'I' code=40
    { 4, 240, 16, 16, 144, 96, 0, },  //glyph 'J' code=41
    { 4, 144, 160, 192, 160, 144, 0, }, //glyph 'K' code=42
    { 4, 128, 128, 128, 128, 240, 0, }, //glyph 'L' code=43
    { 5, 136, 216, 168, 136, 136, 0, }, //glyph 'M' code=44
    { 4, 144, 208, 176, 144, 144, 0, }, //glyph 'N' code=45
    { 4, 96, 144, 144, 144, 96, 0, }, //glyph 'O' code=46
    { 4, 224, 144, 224, 128, 128, 0, }, //glyph 'P' code=47
    { 4, 96, 144, 144, 144, 96, 16, },  //glyph 'Q' code=48
    { 4, 224, 144, 224, 160, 144, 0, }, //glyph 'R' code=49
    { 4, 112, 128, 96, 16, 224, 0, }, //glyph 'S' code=50
    { 3, 224, 64, 64, 64, 64, 0, }, //glyph 'T' code=51
    { 4, 144, 144, 144, 144, 96, 0, },  //glyph 'U' code=52
    { 3, 160, 160, 160, 160, 64, 0, },  //glyph 'V' code=53
    { 5, 136, 168, 168, 168, 80, 0, },  //glyph 'W' code=54
    { 4, 144, 144, 96, 144, 144, 0, },  //glyph 'X' code=55
    { 3, 160, 160, 64, 64, 64, 0, },  //glyph 'Y' code=56
    { 4, 240, 16, 96, 128, 240, 0, }, //glyph 'Z' code=57
    { 2, 192, 128, 128, 128, 192, 0, }, //glyph '[' code=58
    { 5, 128, 64, 32, 16, 8, 0, },  //glyph '\' code=59
    { 2, 192, 64, 64, 64, 192, 0, },  //glyph ']' code=60
    { 5, 32, 80, 136, 0, 0, 0, }, //glyph '^' code=61
    { 4, 0, 0, 0, 0, 240, 0, }, //glyph '_' code=62
    { 2, 128, 64, 0, 0, 0, 0, },  //glyph '`' code=63
    { 3, 0, 224, 32, 224, 224, 0, },  //glyph 'a' code=64
    { 3, 128, 224, 160, 160, 224, 0, }, //glyph 'b' code=65
    { 3, 0, 224, 128, 128, 224, 0, }, //glyph 'c' code=66
    { 3, 32, 224, 160, 160, 224, 0, },  //glyph 'd' code=67
    { 3, 0, 224, 224, 128, 224, 0, }, //glyph 'e' code=68
    { 2, 64, 128, 192, 128, 128, 0, },  //glyph 'f' code=69
    { 3, 0, 224, 160, 224, 32, 224, },  //glyph 'g' code=70
    { 3, 128, 224, 160, 160, 160, 0, }, //glyph 'h' code=71
    { 1, 128, 0, 128, 128, 128, 0, }, //glyph 'i' code=72
    { 2, 0, 192, 64, 64, 64, 128, },  //glyph 'j' code=73
    { 3, 128, 160, 192, 160, 160, 0, }, //glyph 'k' code=74
    { 1, 128, 128, 128, 128, 128, 0, }, //glyph 'l' code=75
    { 5, 0, 248, 168, 168, 168, 0, }, //glyph 'm' code=76
    { 3, 0, 224, 160, 160, 160, 0, }, //glyph 'n' code=77
    { 3, 0, 224, 160, 160, 224, 0, }, //glyph 'o' code=78
    { 3, 0, 224, 160, 160, 224, 128, }, //glyph 'p' code=79
    { 3, 0, 224, 160, 160, 224, 32, },  //glyph 'q' code=80
    { 3, 0, 224, 128, 128, 128, 0, }, //glyph 'r' code=81
    { 2, 0, 192, 128, 64, 192, 0, },  //glyph 's' code=82
    { 3, 64, 224, 64, 64, 64, 0, }, //glyph 't' code=83
    { 3, 0, 160, 160, 160, 224, 0, }, //glyph 'u' code=84
    { 3, 0, 160, 160, 160, 64, 0, },  //glyph 'v' code=85
    { 5, 0, 168, 168, 168, 80, 0, },  //glyph 'w' code=86
    { 3, 0, 160, 64, 160, 160, 0, },  //glyph 'x' code=87
    { 3, 0, 160, 160, 224, 32, 224, },  //glyph 'y' code=88
    { 2, 0, 192, 64, 128, 192, 0, },  //glyph 'z' code=89
    { 3, 96, 64, 192, 64, 96, 0, }, //glyph '{' code=90
    { 1, 128, 128, 128, 128, 128, 0, }, //glyph '|' code=91
    { 3, 192, 64, 96, 64, 192, 0, },  //glyph '}' code=92
    { 3, 96, 192, 0, 0, 0, 0, },  //glyph '~' code=93
    { 4, 48, 64, 224, 64, 240, 0, },  //glyph '£' code=94
    };
    
    static const char str0[] PROGMEM="0";
    static const char str1[] PROGMEM="1";
    static const char str2[] PROGMEM="2";
    static const char str3[] PROGMEM="3";
    static const char str4[] PROGMEM="4";
    static const char str5[] PROGMEM="5";
    static const char str6[] PROGMEM="6";
    static const char str7[] PROGMEM="7";
    static const char str8[] PROGMEM="8";
    static const char str9[] PROGMEM="9";
    static const char str10[] PROGMEM="#";
    static const char str11[] PROGMEM="Arduino VGA Tetris";
    static const char str12[] PROGMEM="by Roberto Melzi";
    static const char str13[] PROGMEM="Game";
    static const char str14[] PROGMEM="Over!";
    
    boolean button = 0;
    boolean button_1 = 0;
    boolean button_2 = 0;
    boolean button_3 = 0;
    boolean button_4 = 0;
    boolean button_5 = 0;
    int block[4][2]={{0,0},{0,0},{0,0},{0,0}};
    int blockExt[4][2]={{0,0},{0,0},{0,0},{0,0}};
    int blockOld[4][2]={{0,0},{0,0},{0,0},{0,0}};
    int blockTmp[4][2]={{0,0},{0,0},{0,0},{0,0}};
    int blockTr[4][2]={{0,0},{0,0},{0,0},{0,0}};
    int yLine[4] = {0,0,0,0};
    int yLineTmp[4] = {0,0,0,0};
    int yCounter = 0;
    int x = 60;
    int y = 6;
    int z = 10;
    int score;
    int noLoop = (-1);
    int clock = 1;
    int delta = 0;
    int color = 1;
    int colorOld;
    int blockN;
    int blockNext;
    int busy;
    int noDelete = 0;
    int k = 0;
    int a = 40;
    int b = 10;
    int counterMenu = 0;
    unsigned long time = 0;
    int fast = 14; //14;
    
    void processInputs() {
      //button_5 = digitalRead(A5); //can be added for clockwise rotation
    
    	if(button_1 == 1) {
    		button_2 = digitalRead(A2);
    		button_3 = digitalRead(A3);
    		button_4 = digitalRead(A4);
    		button_1 = 0;
    		vga.delay(25);
    	}
      else{
         if(button_2 == 1) {
            button_1 = digitalRead(A1);
            button_3 = digitalRead(A3);
            button_4 = digitalRead(A4);
            button_2 = 0;
            vga.delay(25);
         }
         else{
            if(button_3 == 1) {
               button_1 = digitalRead(A1);
               button_2 = digitalRead(A2);
               button_4 = digitalRead(A4);
               button_3 = 0;
               vga.delay(25);
            }
            else{
               if(button_4 == 1) {
                  button_1 = digitalRead(A1);
                  button_2 = digitalRead(A2);
                  button_3 = digitalRead(A3);
                  button_4 = 0;
                  //vga.delay(25);
               }
               else{
                  button_1 = digitalRead(A1);
                  button_2 = digitalRead(A2);
                  button_3 = digitalRead(A3);
                  button_4 = digitalRead(A4);
               }
            }
         }
      }
      button = button_2 || button_4;
    }
    
    void clearInputs() {
    	button_1 = 0;
    	button_2 = 0;
    	button_3 = 0;
    	button_4 = 0;
    	button = 0;
    }
    
    void drawMenu() {
      while (button_1 == 0 && button_2 == 0 && button_3 == 0 && button_4 == 0) {
         processInputs();
         vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str11, 26, 16, (counterMenu%3) + 1);
         vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str12, 28, 28, (counterMenu%3) + 1);
         vga.delay(1000);
         counterMenu++;
      }
      vga.clear(0);
      drawGameScreen();
      drawScore(score);
    }
    
    void drawScore(int i) {
        if (i > 39){
           score = 0;
           i = 0;
           fast = fast - 4;
           if (fast < 3) {fast = 2;}
        }
        vgaU.draw_line(20, 10, 20, 50, 3);
        vgaU.draw_line(20, 50, 20, 50 - i, 1);
        vgaU.draw_line(19, 10, 22, 10, 3);
        vgaU.draw_line(19, 50, 22, 50, 3);
    }
    
    void drawBorder() {
       // total screen size = 120/60
       // tetris game board: width = 30; heigh = 60
       vgaU.draw_line(44,0,78,0,3);
       vgaU.draw_line(44,59,78,59,3);
       vgaU.draw_line(44,0,44,59,3);
       vgaU.draw_line(78,0,78,60,3);
    }
    
    // --------------------- this is for the beginning game window ------------------------
    void drawStartScreen() {
       drawBorder();
       drawGameScreen();
       button = 0;
       vga.delay(200);
    }
    
    // ---------------------- this is the main function to draw the game screen -----------
    void drawGameScreen() {
      drawBorder();
    }
    
    // ----------------------- Tetriminos definition --------------------------------------
    void blockDef(int i) {
      if (i == 1){
      // O
      block[0][0] = 0;
      block[0][1] = 0;
      block[1][0] = 1;
      block[1][1] = 0;
      block[2][0] = 0;
      block[2][1] = 1;
      block[3][0] = 1;
      block[3][1] = 1;
      color = 1;
      }
      if (i == 2){
      // L
      block[0][0] = -1;
      block[0][1] = 0;
      block[1][0] = 0;
      block[1][1] = 0;
      block[2][0] = 1;
      block[2][1] = 0;
      block[3][0] = -1;
      block[3][1] = 1;
      color = 2;
      }
      if (i == 3){
      // J
      block[0][0] = -1;
      block[0][1] = 0;
      block[1][0] = 0;
      block[1][1] = 0;
      block[2][0] = 1;
      block[2][1] = 0;
      block[3][0] = 1;
      block[3][1] = 1;
      color = 3;
      }
      if (i == 4){
      // I
      block[0][0] = -1;
      block[0][1] = 0;
      block[1][0] = 0;
      block[1][1] = 0;
      block[2][0] = 1;
      block[2][1] = 0;
      block[3][0] = 2;
      block[3][1] = 0;
        color = 1;
      }
      if (i == 5){
      // S
      block[0][0] = -1;
      block[0][1] = 0;
      block[1][0] = 0;
      block[1][1] = 0;
      block[2][0] = 0;
      block[2][1] = 1;
      block[3][0] = 1;
      block[3][1] = 1;
      color = 2;
      }
      if (i == 6){
      // Z
      block[0][0] = -1;
      block[0][1] = 1;
      block[1][0] = 0;
      block[1][1] = 1;
      block[2][0] = 0;
      block[2][1] = 0;
      block[3][0] = 1;
      block[3][1] = 0;
      color = 3;
      }
      if (i == 7){
      // T
      block[0][0] = -1;
      block[0][1] = 0;
      block[1][0] = 0;
      block[1][1] = 0;
      block[2][0] = 0;
      block[2][1] = 1;
      block[3][0] = 1;
      block[3][1] = 0;
      color = 1;
      }
    }
    
    // -------------------------- expansion for 4:3 monitors ------------------------------
    void blockExtension() {
       for (int i = 0; i < 4; i++){
          blockExt[0][0] = block[0][0]*3;
          blockExt[0][1] = block[0][1]*2;
          blockExt[1][0] = block[1][0]*3;
          blockExt[1][1] = block[1][1]*2;
          blockExt[2][0] = block[2][0]*3;
          blockExt[2][1] = block[2][1]*2;
          blockExt[3][0] = block[3][0]*3;
          blockExt[3][1] = block[3][1]*2;
       }
    }
    
    void blockRotation(int clock){
      for (int i = 0; i < 4; i++){
         blockOld[0][0] = block[0][0];
         blockOld[0][1] = block[0][1];
         blockOld[1][0] = block[1][0];
         blockOld[1][1] = block[1][1];
         blockOld[2][0] = block[2][0];
         blockOld[2][1] = block[2][1];
         blockOld[3][0] = block[3][0];
         blockOld[3][1] = block[3][1];
      }
      for (int i = 0; i < 4; i++){
         block[0][0] = blockOld[0][1]*clock;
         block[0][1] = -blockOld[0][0]*clock;
         block[1][0] = blockOld[1][1]*clock;
         block[1][1] = -blockOld[1][0]*clock;
         block[2][0] = blockOld[2][1]*clock;
         block[2][1] = -blockOld[2][0]*clock;
         block[3][0] = blockOld[3][1]*clock;
         block[3][1] = -blockOld[3][0]*clock;
      }
    }
    void blockTranslation(int x, int y) {
       for (int i = 0; i < 4; i++){
          blockTr[0][0] = blockExt[0][0] + x;
          blockTr[0][1] = blockExt[0][1] + y;
          blockTr[1][0] = blockExt[1][0] + x;
          blockTr[1][1] = blockExt[1][1] + y;
          blockTr[2][0] = blockExt[2][0] + x;
          blockTr[2][1] = blockExt[2][1] + y;
          blockTr[3][0] = blockExt[3][0] + x;
          blockTr[3][1] = blockExt[3][1] + y;
       }
    }
    
    void delBlock(){
      if (noDelete == 1) {noDelete = 0;}
      else {
          for (int i = 0; i < 4; i++){
             vgaU.draw_line(blockTr[i][0],blockTr[i][1],blockTr[i][0] + 3,blockTr[i][1],0);
             vgaU.draw_line(blockTr[i][0],blockTr[i][1] + 1,blockTr[i][0] + 3,blockTr[i][1] + 1,0);
          }
       }
    }
    
    void drawBlock(){
      for (int i = 0; i < 4; i++){
         vgaU.draw_line(blockTr[i][0],blockTr[i][1],blockTr[i][0] + 3,blockTr[i][1], color);
         vgaU.draw_line(blockTr[i][0],blockTr[i][1] + 1,blockTr[i][0] + 3,blockTr[i][1] + 1, color);
      }
      for (int i = 0; i < 4; i++){
         blockTmp[0][0] = blockTr[0][0];
         blockTmp[0][1] = blockTr[0][1];
         blockTmp[1][0] = blockTr[1][0];
         blockTmp[1][1] = blockTr[1][1];
         blockTmp[2][0] = blockTr[2][0];
         blockTmp[2][1] = blockTr[2][1];
         blockTmp[3][0] = blockTr[3][0];
         blockTmp[3][1] = blockTr[3][1];
      }
    }
    
    void drawBlockTmp(){
      for (int i = 0; i < 4; i++){
         vgaU.draw_line(blockTmp[i][0],blockTmp[i][1],blockTmp[i][0] + 3,blockTmp[i][1], color);
         vgaU.draw_line(blockTmp[i][0],blockTmp[i][1] + 1,blockTmp[i][0] + 3,blockTmp[i][1] + 1, color);
      }
    }
    
    void checkBlock(){
      busy = 0;
      for (int i = 0; i < 4; i++){
         busy = busy + vga.getpixel(blockTr[i][0], blockTr[i][1]) + vga.getpixel(blockTr[i][0] + 2, blockTr[i][1]);
      }
    }
    
    void replaceBlock(){
         blockExtension();
         blockTranslation(x, y);
         checkBlock();
         if (busy == 0){
            drawBlock();
         }
         else // ---------- else is run if the block cannot get down  -----------------
         {
            drawBlockTmp();
            checkForFullLine(); // ---- check il the line is filled when the block cennot get down anymore ----------------------
            noLoop = 0;
            noDelete = 1;
            if (y < 6) {
               gameOver();
            }
         }
         vga.delay(50);
    }
    
    void gameOver(){
       noLoop = -1;
       score = 0;
       fast = 14;
       clearInputs();
       time = 0;
       while (button_1 == 0 && button_2 == 0 && button_3 == 0 && button_4 == 0) {
          processInputs();
          vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str13, 92, 30, 1);
          vga.printPROGMEM((byte*)fnt_nanofont_data, FNT_NANOFONT_SYMBOLS_COUNT, FNT_NANOFONT_HEIGHT, 3, 1, str14, 92, 38, 1);
          vga.delay(200);
       }
       vga.clear(0);
    
    }
    
    void drawBlockNext(){ // ----- draw nenx block on the right side --------------------------------
         blockExtension();
         blockTranslation(100, 10);
         vgaU.draw_line(95, 8, 112, 8, 0);
         vgaU.draw_line(95, 9, 112, 9, 0);
         vgaU.draw_line(95, 10, 112, 10, 0);
         vgaU.draw_line(95, 11, 112, 11, 0);
         vgaU.draw_line(95, 12, 112, 12, 0);
         vgaU.draw_line(95, 13, 112, 13, 0);
         vgaU.draw_line(95, 14, 112, 14, 0);
         vgaU.draw_line(95, 15, 112, 15, 0);
         drawBlock();
    }
    
    void checkBlockTranslation(){
         x = x + delta;
         blockExtension();
         blockTranslation(x, y);
         checkBlock();
         if (busy == 0){
            drawBlock();
         }
         else
         {
            x = x - delta;
            blockExtension();
            blockTranslation(x, y);
            drawBlock();
         }
         vga.delay(50);
    }
    
    void checkBlockRotation(){
         //x = x + delta;
         blockExtension();
         blockTranslation(x, y);
         checkBlock();
         if (busy == 0){
            drawBlock();
         }
         else
         {
            clock = +1;
            blockRotation(clock);
            blockExtension();
            blockTranslation(x, y);
            drawBlock();
         }
         vga.delay(50);
    }
    
    void checkForFullLine() { // --------------------- check if the line is full and must be deleted --------------
       for (int i = 0; i < 4; i++){
          for (int j = 45; j < 76; j += 3) {
             if (vga.getpixel(j, blockTmp[i][1]) >0){k++; }
          }
          if (k == 11) { // ---- line is full and must be deleted ----------------------------------------------------------
             vgaU.draw_line(45, blockTmp[i][1], 78, blockTmp[i][1], 0);
             vgaU.draw_line(45, blockTmp[i][1] + 1, 78, blockTmp[i][1] + 1, 0);
             yLineTmp[yCounter] = blockTmp[i][1];
             yLine[yCounter] = blockTmp[i][1];
             yCounter++;
          }
          k = 0;
        }
        if (yLineTmp[yCounter - 1] < yLine[0]) { // ------------ qui va capito se va < o > (penso >) ----------------------
           for (int i = 0; i < yCounter; i++) { // ------------- inversion ---------------------------------------
              yLine[i] = yLineTmp[yCounter - i - 1];
           }
        }
        for (int i = 0; i < yCounter; i++){   // ----------- block translation to lower position --------------
          for (int y = yLine[i] - 2; y > 0; y = y - 2) {
             for (int x = 45; x < 76; x += 3) {
                colorOld = vga.getpixel(x, y);
                if (colorOld > 0) {
                   vgaU.draw_line(x, y , x + 3, y , 0);
                   vgaU.draw_line(x, y + 1, x + 3, y + 1, 0);
                   vgaU.draw_line(x, y + 2, x + 3, y + 2, colorOld);
                   vgaU.draw_line(x, y + 3, x + 3, y + 3, colorOld);
                }
             }
          }
       }
       if (yCounter != 0) {score = score + 2*int(pow(2, yCounter));}
       drawScore(score);
       yCounter = 0;
    }
    
    void setup() {
    	//Serial.begin(9600);
    	pinMode(A1, INPUT);
    	pinMode(A2, INPUT);
    	pinMode(A3, INPUT);
    	pinMode(A4, INPUT);
    	//pinMode(A5, INPUT);
    
    	vga.begin();
    
    	randomSeed(analogRead(A5));
    }
    
    //-----------------------------------------------------------------------------------------------
    //--------------------- This is the main loop of the game ---------------------------------------
    //-----------------------------------------------------------------------------------------------
    
    void loop() {
      processInputs();
      if (noLoop < 1){ // --------------- to generate new Tetraminos --------------------------------
         blockN = blockNext;
         if (noLoop == -1 ) { // -------------- only ath the game beginning  ------------------------
            drawMenu();
            while (button_1 == 0 && button_2 == 0 && button_3 == 0 && button_4 == 0) {
               blockN = int(random(7)) + 1;
               processInputs();
            }
         }
         drawGameScreen();
         drawScore(score);
         blockNext = int(random(8));
         blockDef(blockNext);
         drawBlockNext();
         blockDef(blockN);
         x = 57;
         y = 3;
         button_1 = 1;
         noLoop = 1;
      }
      if (button_2 == 1){ // ------------------------ rotation -------------------------
         if (button_2 == 1){clock = -1;}
         //if (button_5 == 1){clock = 1;}
         delBlock();
         blockRotation(clock);
         checkBlockRotation();
      }
      if (button_1 == 1 || button_3 == 1){ // ------- translation ----------------------
         if (button_1 == 1){delta = 3;}
         if (button_3 == 1){delta = -3;}
         delBlock();
         checkBlockTranslation();
      }
      time++;
      if (time % fast > fast - 2 || button_4 == 1){ // --- Tetraminos falling ----------
         if (fast < 3) {fast = 2;}
         y = y + 2;
         delBlock();
         replaceBlock();
      }
      vga.delay(10 + 2*fast);
    }
    

    (click para ampliar)

    (click para ampliar)

    (click para ampliar)

    (click para ampliar)

    (click para ampliar)