Piedra Papel Tijeras Lagarto Spock con Arduino

Escrito por: Ulises Gascón

Jun 21, 20158 min read

Para los fans de Big Bang Theory, este es un juego muy conocido que resuelve un problema muy típico del clásico piedra papel tijeras, ya que cuando los participantes se conocen suelen repetirse los resultados... lo que limitaba el uso de este juego para resolver cuestiones practicas de la vida real...

Las nuevas normas

Grafico explicando las reglas del juego. Descripcion completa más abajo
  • Piedra gana a tijeras y lagarto, pierde con papel y spock.
  • Papel gana a piedra y spock, pierde con tijeras y lagarto.
  • tijeras gana a papel y lagarto, pierde con piedra y spock.
  • Lagarto gana a spock y papel, pierde con piedra y tijeras.
  • Spock gana a tijeras y piedra, pierde con papel y lagarto.

El factor Arduino

Cuando queremos que el contrincante sea nuestro Arduino, la misión se complica un poco. Para empezar necesitaremos un sistema que nos permita indicarle a Arduino cual es nuestra elección, y ademas otro método que nos indique los cambios durante el juego y el resultado final del mismo.

Yo personalmente, prefiero utilizar pulsadores, al tener cinco opciones, necesitaremos al menos cinco pulsadores. Es muy importante conectar estos a una resistencia (10K) a nuestra placa, ya que en caso contrario, podría fácilmente provocarse un cortocircuito y dañar o destruir por completo nuestra placa.

El esquema de conexión es el siguiente.

Esquema de conexiones versión protoboard

Todos los pulsadores van conectados a pines digitales de nuestro Arduino.

Por supuesto para la parte gráfica de nuestra aplicación, una de las maneras más sencillas y fáciles es usar la comunicación serial, como podemos ver en la imagen.

Ejemplo de comunicacion via serial entre el ordenador y Arduino

Llegados a este punto, ya tenemos resueltos los problemas de usabilidad más básicos. Tenemos una primera interfaz electrónica y una conexión serial para seguir la evolución del juego.

Ahora llega la parte más interesante, resolver los problemas propios de la programación.

Entendiendo el código

Como suele pasar en todo lo relacionado con programación, existen muchas maneras de resolver un mismo problema y este caso no es una excepción. Yo me he decantado por expresiones sencillas y poco optimizadas, dadas las características del proyecto. Pero seguramente tu puedas crear una versión mejorada. Te invito encareciadamente a que lo hagas. Recuerda que este proyecto es open source, así que no dudes en hacer tu versión :-)

Definiendo los pulsadores

Partiendo de la disposición y el orden deseado, definiremos los botones y la comunicación serial. Por supuesto incluiremos un mensaje de bienvenida

// ==== Definiendo los Botones  ====

// botones
int boton1 = 12;
int boton2 = 11;
int boton3 = 10;
int boton4 = 9;
int boton5 = 8;


// ==== EMPIEZA EL SETUP() ====
void setup() {

    // Modo Input en los pulsadores
    pinMode(boton1, INPUT);
    pinMode(boton2, INPUT);
    pinMode(boton3, INPUT);
    pinMode(boton4, INPUT);
    pinMode(boton5, INPUT);


    //Activando la comunicación Serial
    Serial.begin(9600);
    Serial.println("Comunicacion establecida con exito");


    // mensaje de bienvenida
    Serial.println("Bienvenido Humano! Es hora de jugar a -- Piedra papel tijeras lagarto Spock --");
    Serial.println("Tiempo de elegir. Presiona un boton");
 
}

Convirtiendo nuestra elección en un valor

El siguiente paso lógico es asociar a cada pulsador el valor correspondiente, para lo que necesitaremos definir una variable al principio de nuestro código y una estructura de control que nos permita guardar nuestra elección en la variable que hemos creamos previamente.

// Definimos la eleccion del jugador y Arduino a cero.
String eleccionJugador = "vacio";
    
// ==== EMPIEZA EL LOOP() ====
void loop (){
 
    // El jugador define su elección a traves de los 5 botones.
    if(digitalRead(boton1) == HIGH){
        eleccionJugador = "piedra";
    }else if(digitalRead(boton2) == HIGH){
        eleccionJugador = "papel";
    }else if(digitalRead(boton3) == HIGH){
        eleccionJugador = "tijeras";
    }else if(digitalRead(boton4) == HIGH){
        eleccionJugador = "lagarto";
    }else if(digitalRead(boton5) == HIGH){
        eleccionJugador = "spock";
    }
 
} 

La elección de Arduino

En el caso de Arduino, la elección es más compleja, ya que Arduino necesitará soportarse en el azar. La idea es que Arduino, elija al azar un número entre 1 y 5 para que posteriormente convirtamos ese valor en una cadena idéntica a la de los volares elegidos pro el jugador humano. Desgraciadamente, la función que más fácil nos haría esto, no funciona muy bien, así que es necesario buscar un método para "generar azar", lo que yo he decidido es incluirla en loop principal, de tal manera que esta constantemente cambiando hasta que el jugador humano hace su elección, lo que veremos más adelante.


// Definimos la eleccion del jugador y Arduino a cero.
String eleccionArduino = "vacio";


// ==== EMPIEZA EL LOOP() ====
void loop (){

    // === ELECCIÓN ARDUINO ===

    // la elección de Arduino es un numero al azar entre 1 y 5.
    int eleccionRandom = random(1, 6);

    // convertir ese numero en la cadena de texto correpondiente 
    // continuamente hasta que el jugador humano haga su elección.
    if(eleccionRandom == 1){
        // Arduino elige piedra
        eleccionArduino = "piedra";
        
    }else if( eleccionRandom == 2 ){
        // Arduino elige papel
        eleccionArduino = "papel";

    }else if( eleccionRandom == 3 ){
        // Arduino elige tijeras
        eleccionArduino = "tijeras";

    }else if( eleccionRandom == 4 ){
        // Arduino elige lagarto
        eleccionArduino = "lagarto";

    }else if( eleccionRandom == 5 ){
        // Arduino elige Spock
        eleccionArduino = "spock";  
        
    } else{
        // Si el valor se sale de lo esperado... avisamos al usuario.
        Serial.print("Arduino es un tramposo! Arregla su codigo");
    }
}

Codificando las reglas del juego a través de los resultados

Partiendo de la elección realizada por el jugador comparamos con la elección realizada por Arduino y resolvemos imprimiendo por pantalla el resultado con una frase especifica. Ejemplo: Jugador Gana! Piedra aplasta tijeras. Si renuncias a estas frases tan detalladas, el código puede simplificarse enormemente.


// Si el jugador es piedra
}else if( eleccionJugador == "piedra") {

    // Piedra gana a tijeras.
    if (eleccionArduino == "tijeras"){
        Serial.print("Jugador Gana! ...piedra aplasta las tijeras");

    // Piedra gana a lagarto.
    }else if( eleccionArduino == "lagarto"){
        Serial.print("Jugador Gana! ...piedra aplasta al lagarto");

    // Piedra pierde con papel.
    }else if( eleccionArduino == "papel") {
        Serial.print("Jugador Pierde! ...papel cubre a la piedra");

    // Piedra pierde con spock.
    }else if( eleccionArduino == "spock") {
        Serial.print("Jugador Pierde! ....Spock vaporiza la piedra");

    }

Problema: El Empate

No podemos olvidarnos de la posibildiad de empate. En nuestra estructura de control debemos incluir esa posibilidad

// Resolución en caso de empate.
if(eleccionJugador == eleccionArduino){
    Serial.print("Empate! "+String(eleccionJugador)+" no gana a "+String(eleccionArduino));
}

Problema: Matar el Loop principal

Un problema evidente, es que al resolverse el resultado en el loop principal, el mensaje resolutivo que te da la victoria o te la quita empieza a repetirse hasta que reiniciamos o apagamos Arduino. La mejor manera de salir de ese Loop es envolviendo nuestro código en un loop while, al que le cambiaremos el valor de la variable de controla false cuando queramos que se pare. ¡Importante! Arduino va muy rápido, sino incluimos un pequeño delay probablemente no se imprima en su totalidad el último mensaje.

// variable de control de loop while 
bool run = true;

// ==== EMPIEZA EL LOOP() ====
void loop (){

// Loop de control
    while(run == true){
        if(eleccionJugador == eleccionArduino){
                Serial.print("Empate! "+String(eleccionJugador)+" no gana a "+String(eleccionArduino)); 
                delay(1000); 
                run = false; // Para el Loop While y con ello todo dentro de Loop().
        }
    }
}

Extra: Debug

Es de buena gente... dar opciones a algún tipo de depurado. Una de las mejores formas es usando el componente #define, que nos permite ejecutar ciertas lineas de código si este valor esta definido (se puede comentar para no ejecutarlo) al principio de nuestro script. Con este tipo de cosas, justo a comentarios decentes y una documentación clara... realmente se contribuye a hacer un mundo mejor.


// Parámetros del depurador
#define DEBUGMODE


// ==== EMPIEZA EL SETUP() ====
void setup() {

//Activando la comunicación Serial
Serial.begin(9600);
Serial.println("Comunicacion establecida con exito");


// Aviso del DebugMode
#ifdef DEBUGMODE
Serial.print("DEBUGMODE Activado");
#endif


}


// ==== EMPIEZA EL LOOP() ====
void loop (){

// Loop de control
// === ELECCIÓN ARDUINO ===

    // DebugMode
    #ifdef DEBUGMODE
        Serial.print("Arduino elige: "+String(eleccionArduino));
    #endif

// === ELECCIÓN HUMANO ===

        // DebugMode
    #ifdef DEBUGMODE
        Serial.print("Humano elige: "+String(eleccionJugador));
    #endif


} //Loop()

Uniendo todo el puzzle

Va llegando el momento de juntar las piezas sueltas, para darle sentido a todo el conjunto. en este link podéis ver todo el código fuente listo para subirlo a vuestro a Arduino. ¡No te cortes y haz un fork!

Experiencia de usuario

A veces un post-it es suficiente para dotar a nuestra aplicación de una interfaz de usuario inolvidable.

Interfaz muy sencilla donde los botones se pinchan directamente sobre un postit con anotaciones directamente sobre la protoboard

Contribuye al mundo

Justo después de haber terminado este script, Nuria me explicó lo poco acertado que resulta que un papel desautorice a Spock, por lo que yo planteo arreglar este bug pidiendo a mis lectores que se animen a crear una versión de este script haciendo uso de una reglas diferentes. Mono Pirata Robot Ninja y Zombie

Imágenes | propias