El juego de la serpiente con Arduino

Creo que todos recordamos el mítico juego de la serpiente y lo adictivo que era. En este turorial aprenderemos a programar el juego de la serpiente con Arduino, para ello necesitaremos los siguientes materiales: Un Arduino mega en mi caso, 2 breadboard o una grande, cable, 16 resistencias de 220 ohmios, una matriz led 16×16 y un Joysticks.

Si no disponéis de este material y queréis adquirirlo aquí os dejamos unos enlaces para comprarlos online:

Una vez que tenemos todo el material, pasamos al montaje de la matriz sobre la o las breadboards, en mi caso las, la matriz es de ánodo común, por lo que los leds de una misma fila comparten la alimentación, estos 16 pines los conectaremos directamente en orden, en mi caso en los pines digitales del 22 al 37. Los otros 16 pines se corresponden a cada uno de los leds de la fila, estos irán conectados  antes a una resistencia de 220 ohmios para regular la intensidad y en mi caso los conectaré en los pines digitales del 0 al 15.

20150113_185405

Una vez hecho el montaje pasamos a  la programación, los programas serán escalables e iremos poco a poco añadiendo le las características del fabuloso juegos. Para empezar haremos un primer programa de prueba para comprobar que la matriz y las conexiones funcionan correctamente:

/*Programa para comprobar el correcto funcionamiento.
 y estado de la matriz 16x16 */
void setup(){
//definimos los pines digitales como salida.
  for(byte i=0; i<=15; i++){
    pinMode(i, OUTPUT);
    digitalWrite(i, HIGH);  //Al ser de ánodo común ponemos los cátodos en 1 para que no                                 
                            ////estén encendidos al inicio del programa.
  } 
 
  for(byte i=22; i<=37; i++){
    pinMode(i, OUTPUT);
 
  }
}
 
void loop(){
  //Hacemos un doble bucle para ir comprobando uno a uno el funcionamiento de las celdas
  for(byte i=22; i<=37; i++){
    digitalWrite(i, HIGH);
    for(byte j=0; j<=15; j++){
      digitalWrite(j, LOW);
      delay(100);
      digitalWrite(j, HIGH);
    }
    digitalWrite(i, LOW);  
  }
}

Comprobado el funcionamiento vamos a pasar a hacer un programa mas interesante, haremos un programa de un puntero que se desplaza automáticamente por la matriz, nosotros controlaremos su dirección y sentido, a través de un joysticks.

El funcionamiento del joysticks es sencillo, requerimos de dos entradas analógicas para cada uno de los ejes, el joysticks se comporta como un doble potenciometro, en su posición de equilibrio marcará 512 aproximadamente, si lo desplazamos  en una dirección el valor de uno de los ejes tenderá a 0 o a 1024 dependiendo del sentido.

Una vez dicho esto pasemos a programar el código que en este caso será mas complejo.

/*Programa de movimiento de un punto en la matriz controlando su dirección
*y su sentido a través de un joysticks.
*/
//posición del punto en la matriz a través de dos coordenas entre el 0 y el 15.
byte posicion[1][2];
 
//varibles que controlan el valor del joysticks.
int x;
int y;
 
//variable que guarda la dirección en la que se mueve el punto.
byte direccion;
/*
* 0-> Derecha.
* 1-> Izquierda.
* 2-> Arriba
* 3-> Abajo
*/
 
long time;
 
void setup(){  
  for(byte i=0; i<=15; i++){
    pinMode(i, OUTPUT);
    digitalWrite(i, HIGH);  //Al ser de ánodo común ponemos los cátodos en 1 para que 
                            //no estén encendidos. 
 }
 
  for(byte i=22; i<=37; i++){
     pinMode(i, OUTPUT);
       }
   //Inicializamos la variables del programa.   
   direccion=0;
   posicion[0][0]=0;   
   posicion[0][1]=0;   time=0;
 }
 void loop(){   //función para dibujar el punto en la matriz.
   Escribir_matriz();
   // Este if se encarga de que el desplazamiento y cambio de dirección se efentuen
   // cada 150 milisegundos.
   if(millis() &gt;= time+150){  
    time=millis();
    //Función que lee el estado del joysticks.
    Leer_joysticks();
    //función que determina la nueva dirección del puntero.
    Cambio_direccion();
    //Función encargada de cambiar la posición del punto.
    Cambio_posicion();
  }
}
 
void Leer_joysticks(){
  //Lectura analçogica del joysticks.
  x=analogRead(A1);
  y=analogRead(A0);
 
}
 
void Cambio_direccion(){
  //Si el joystick se desplaza en dirección Izquierda o derecha, 
  //solo puede cambiar su dirección a Arriba o Abajo.
  if( direccion==0 || direccion==1 ){
    //Si el joysticks apunta arriba la dirección cambia arriba.
    if(y>=900){
      direccion=2;
      return ;//Cuando se da un cambio de dirección se vuelve a la función principial
      //Solo puede haber un cambio de dirección por desplazamiento
    }
    //Si el joysticks apunta abajo la dirección cambia abajo.
    if(y<100){
         direccion=3;
         return ;
       }
     }    //Para el resto de cambios de dirección el comportamiento es análogo.     if( direccion==3 || direccion==2 ) {
      if(x>900){
      direccion=0;
      return ;
    }
    if(x&lt;100){
      direccion=1;
      return ;
    }
  }
}
 
void Cambio_posicion(){
  //Apaga el punto escrito en la matriz
  digitalWrite( posicion[0][1], HIGH);
  digitalWrite( posicion[0][0]+22, LOW);
  // si la dirección es arriba el primer parámetro de la variable 
  //posicion[][] aumenta en uno.
  if(direccion==2){
    if(posicion[0][0]==15){
      posicion[0][0]=0;//Si este ha llegado ya a 15 que es el máximo pasa a valer 0.
      //El punto atravesará la pared y aparecerá en la contraria.
      return ; //Una vez que se produce un desplazamiento se vuelve a la función principal
    }   
    posicion[0][0]++;
    return ;
  }
  //Para el resto de cambios de posición el comportamiento es análogo
  if(direccion==3){
    if(posicion[0][0]==0){
      posicion[0][0]=15;
      return ;
    }
    posicion[0][0]--;
    return ;
  }
  if(direccion==0){
    if(posicion[0][1]==15){
      posicion[0][1]=0;
      return ;
    }
    posicion[0][1]++;
    return ;
  }
 
  if(direccion==1){
    if(posicion[0][1]==0){
      posicion[0][1]=15;
      return ;
    }
    posicion[0][1]--;
    return ;
  }
}
 
void Escribir_matriz(){
  //Para escribir en la matriz en primer lugar habilitamos la fila correspondiente
  digitalWrite( posicion[0][0]+22, HIGH);
  //Despues ponesmo a cero la columna correspondiente. Recuerda que 
  //es una configuración en ánodo común
  digitalWrite( posicion[0][1], LOW);
}

Una vez hecho esto la funcionalidad debería ser algo parecida a lo siguiente:



Ahora introduciremos el alimento que comerá la serpiente e irá cambiando aleatóriamente de posición. Lo primero que haremos será definir una variable análoga a posición, que será alimento:Una vez hecho esto ya tenemos el programa base, a partir de ahora a este programa simplemente le tendremos que añadir una serie de funciones, hasta que sea el juego de la serpiente. Para ello lo próximo que vamos  añadir es el alimento:

 

byte alimento[1][2];

La posición del alimento debe ser aleatoria, para ello nos ayudaremos de las funciones de Arduino :

randomSeed(i): Esta función nos generara un sucesión aleatoria en la función random() en función del valor i.
random(i, j): Esta función nos dará un numero aleatorio mayor o igual a i o menor que j. Nunca igual que j.

Dicho esto pasamos a poner en la función setup() lo siguiente:

  randomSeed(analogRead(A2));//lee el pin A2 que esta al aire lo 
                             //utiliza como valor semilla de la sucesión.
  alimento[0][0]= random(0, 16);
  alimento[0][1]= random(0, 16);

Finalmente solo queda añadir una función que compruebe si el punto llega a la posición del alimento:

void Comprobar_alimento(){
  if(posicion[0][0]==alimento[0][0]){
    if(posicion[0][1]==alimento[0][1]){
      alimento[0][0]= random(0, 16);
      alimento[0][1]= random(0, 16);
      Cambio_posicion();
    }
  }
}

Para terminar no olvidéis colocar esta función correctamente en la loop() y escribir el alimento en la matriz, añadiendo esta escritura de la misma forma que escribimos el punto, que no se te olvide tampoco apagar lo. Si todo esta correcto tendréis algo parecido a esto:

Llegados a este punto ha llegado el momento de crear el cuerpo de nuestra serpiente, cada vez que nuestra serpiente se coma un punto esta crecerá en 1. Para ello tendremos que insertar el siguiente código:

byte posicion[100][2];
byte longitud;

La posición de nuestra serpiente ahora es la de cada uno de su cuerpo por eso debemos ampliar el array para poder guardar todos estos valores, y crearemos una nueva variable llamada longitud que nos dirá el tamaño de la serpiente, dicha variable la inicializaremos a 1 en la función setup().

A continuación en la función cambio_posicion() introduciremos al principio un bucle que para que la posición de cada punto de la serpiente pase a ser la del siguiente punto en dirección a la cabeza. Esto no se le aplicará a la cabeza ya que esta será la única que realmente avance y lo hará como se ha hecho hasta ahora:

 

  for(byte i=longitud; i>1; i--){
  posicion[i-1][0]=posicion[i-2][0];
  posicion[i-1][1]=posicion[i-2][1];  
  }

A continuación al igual que hacemos un bucle para cambiar cada punto de la serpiente, en la función escribir matriz debemos hacer otro para escribir cada uno de los puntos:

void Escribir_matriz(){
  for(int i=longitud; i>=0; i--){
  digitalWrite( posicion[i-1][0]+22, HIGH);
  digitalWrite( posicion[i-1][1], LOW);
  digitalWrite( posicion[i-1][1], HIGH);
  digitalWrite( posicion[i-1][0]+22, LOW);
 
  }
  digitalWrite( alimento[0][0]+22, HIGH);
  digitalWrite( alimento[0][1], LOW);
  digitalWrite( alimento[0][0]+22, LOW);
  digitalWrite( alimento[0][1], HIGH);
  }

Una vez hecho esto,  si en la función alimento la cabeza coincide con el alimento esta llamará la función crecer, que aumentará en 1 la longitud y le dará la posición al nuevo punto de la serpiente, que será en el que se encontraba el alimento:

void crecer(){
longitud++;
posicion[longitud-1][0]=alimento[0][0];
posicion[longitud-1][1]=alimento[0][1];
}

Finalmente nos debería quedar algo así:

Ya solo nos queda una cosa, la muerte de la serpiente, si la cabeza entra en contacto con un punto del cuerpo se pierde el juego, es decir, se vuelve a la longitud inicial, a la posición inicial y se genera un nuevo punto de alimento.

Para ello crearemos una función llamada control_muerte(), que con un doble if() comprobará que las dos coordenadas de la cabeza sean iguales a uno de los puntos del cuerpo, si esto se da se llama a una función llamada muerte(), que como dijimos reiniciará los valores:

void Control_muerte(){
 
  for( byte i=longitud; i>1; i--){
    if(posicion[i-1][0]==posicion[0][0]){
      if(posicion[i-1][1]==posicion[0][1]){
        Escribir_matriz();
        muerte();
      }
    }
    if(longitud==1){
      break;
    } 
  }
}
 
void muerte(){
  longitud=1;
  posicion[0][0]=8;
  posicion[0][0]=7;
  alimento[0][0]= random(0, 16);
  alimento[0][1]= random(0, 16);
}

Con esto damos terminado nuestro juego de la serpiente, esperemos que os haya gustado. Si vuestro programa no funciona correctamente y con los pasos que os he ido dando no se os soluciona no os preocupes, en nuestro repositorio podéis encontrar el código final y utilizarlo sin problemas. Esperamos que hayáis disfrutado con este turorial. Proximamente haremos uno de como hacer un Shields, para que montar nuestro juego nos resulte mas sencillo. Hasta otra.

14 Comments on "El juego de la serpiente con Arduino"

  1. He probado el mismo código que tú pero me salen 2 serpientes y 2 puntos (alimentos). Una se las serpientes se mueve como la otra y uno de los dos alimentos no sirve. ¿Dónde es posible que esté el error?

  2. Tengo problemas con el apartado de “punto con alimento”. ¿Podrías poner el programa completo para esta parte?
    Gracias.

  3. Hola muchas gracias por el post solo tengo un problema al correrlo me pone dos semillas aleatorias pero una no me la puedo comer y se mueve cada vez que muevo el joystick espero me uedas ayudar gracias

    • Así sin ver el código es difícil de encontrar el fallo, mira que no este utilizando en un Array una posición no declarada o que te hayas confundido y hayas puesto una variable donde iba otra XD

  4. Me podrian pasar un codigo para el juego de snake en arduiono uno con matrix 8×8?

  5. buenas…estoy intersado en hacer este juego”snake” …pero cuento con una matriz 8×8 de catodo comun ……como seria las conexiones para el arduino mega …y que cambis tendria que hacer en la programacion….gracias

  6. buenas...estoy intersado en hacer este juego"snake" me harías el gran favor de regalarme la gráfica de conexiones de la matriz al arduino | noviembre 30, 2015 en 5:14 pm | Responder

    gracias

    • En el tutorial “Como hacer un shield para Arduino” se hace una placa diseñada con fritzing, para dichas conexiones, que son algo liosas, echale un ojo, haber si te sirve para orientarte .

      Saludos

  7. Hola pues el Arduino uno cuenta con 20 posibles salidas digitales, utilizando las analógicas como digitales, pero necesitas dos para el joysticks, te quedarían un total de 18, y necesitas convertirlas en 32. La solución mas sencilla es utilizando un registro de desplazamiento como el 595, pero si no tienes mucha experiencia con circuitos digitales te va a ser complicado ya que necesitarías 4 de estos integrados para lograr las 32 salidas digitales.

    Si quieres también puedes hacer el juego en una matriz 8×8 que solo requiere de 16 pines digitales, aunque es mas pequeña.

    Saludos y no dudes en preguntar lo que necesites.

  8. También funcionaría con un arduino uno?

    • Hola Marc:

      Efectivamente funcionaría en un Arduino UNO pero el problema es que no tendría suficientes salidas ya que la matriz de 16×16 necesita 32 conexiones. Además de las conexiones del joistick. Quizás ahí tendrías que jugar con un 595(registro con desplazamiento) para aumentar las salidas. Pero es mejor que uses una Mega como este caso.

Deja un comentario.

Tu dirección de correo no será publicada.


*


*