viernes, 4 de marzo de 2016

Regresa al GATO ahora es Android

Para la impartición de cátedra de asignaturas relacionadas a la programación de computadoras se han utilizado un sin fin de ejercicios, algoritmos, proyectos y artilugios para demostrar habilidades para el manejo de estructuras de control.

Es inevitable por lo tanto, no pensar en desarrollar un juego para motivar a los estudiantes, uno de los clásicos infaltables es sin duda el tres en raya, tres en línea, TIC - TAC - TOE, o el Gato o con el nombre que se conozca en cada país.


Es por ello, que por motivos de la programación de dispositivos móviles ANDROID hemos puesto manos a la obra y desarrollado nuestra app demostrativa mediante el empleo de orientación a objetos. Esperemos este material sea de ayuda a los que quieran aprender el desarrollo de apps móviles.

PASO 1. Lo que necesitamos

  • Y un poco de paciencia para la instalación de las herramientas 

Algo importante a mencionar, una vez finalizada la instalación del IDE será necesario definir cual será el lugar donde podremos ver y probar las aplicaciones, para ello existen algunas alternativas:

1.- Crear un AVD (Android Virtual Device) desde el menu "Tools-Android-AVD Manager" 


donde deberemos elegir "+ Create Virtual Device", yo personalmente he preferido crear una unidad virtual con la API 15 con Android 4.0.3 "Ice Cream Sandwich" con la finalidad de que mis aplicaciones sean soportadas por muchos mas dispositivos. Y para gozar de las ventajas de las nuevas APIs dejo la maquina virtual creada por default con la versión actual de android. Los detalles de las características del AVD no nos he analizado a profundidad, pero la imagen muestra algunos datos que podrán guiarles.

¡Aquí tenemos una pequeña nota importante a considerar! en muchas de las ocasiones el emulador no quiere arrancar pues indica que tu computadora no soporta aceleración de hardware o que falta la instalación del componente de aceleración; yo conozco dos formas de solucionarlo:  a) Debemos ir a la BIOS de la computadora e indicar soporte de HAXM , b) Podemos descargar el paquete de la aceleración de gráficos de Intel https://software.intel.com/en-us/android/articles/installation-instructions-for-intel-hardware-accelerated-execution-manager-windows no olvidando instalar el programa que queda en <sdk folder>/sdk/extras/intel/Hardware_Accelerated_Execution_Manager/ 


2.- Utilizar tu dispositivo android fisico o smartphone, todos los dispositivos que cuenten con el sistema operativo android tienen una opción denominada "Depuración USB", la localización de dicha opción ha variado con algunas versiones, aunque casi siempre se encuentra en el menu CONFIGURACIONES. 

Aqui hay un link donde puedes ver información relacionada http://www.elandroidelibre.com/2015/01/como-activar-el-modo-depuracion-usb-en-android.html

Una vez realizado los pasos anteriores solo habrá que conectar vía USB tu dispositivo para que el entorno de programación lo detecte al momento de ejecutar un programa Android.


3.- Utilizar un emulador externo de dispositivos Android, como lo es "Bluestacks"
Donde solo es necesario instalar la aplicación y cuando intente ejecutar un programa el IDE lo reconocerá siempre y cuando este funcionando dicho emulador.

Si has librado el paso 1, estas listo para empezar a desarrollar cualquier aplicación Android y realizar las pruebas necesarias.


PASO 2. El diseño

Para obtener un resultado visual agradable y evitando pelear con el código XML junto con "donde pongo esto", es necesario planear la interfaz final del proyecto.
Este gato en particular fue planeado como el siguiente diagrama:



Como los dispositivos android son muy variados en tamaño, es muy recomendable desarrollar una interfaz basada en tamaños relativos y no absolutos, por lo tanto, haciendo uso de los controles "LinearLayout" vertical y horizontal logramos el acomodo de las partes principales y además para indicar el "peso" de cada elemento dentro del layout utilizamos la propiedad android:layout_weight. En el siguiente link esta la explicación completa del manejo de esto:
http://androideity.com/2012/06/01/ui-fluidas-y-la-propiedad-weight-en-android/

Finalmente, el código XML de la vista de la aplicación quedo de la siguiente manera (las imágenes pueden descargarse a continuación y colocarse dentro de la carpeta "drawable":
















    

        

        
    

    

        

        

        
    


    

    

        

        

        
    

    


    

        

        

        
    

    

        

        

    





PASO 3. La programación

Una vez completado el diseño, es tiempo de empezar a programar, aunque si lo has notado en el mismo diseño del XML ya hemos agregado un poco de código mediante una propiedad llamada android:onClick="Tirando", más adelante platicaremos de ésta.

Sin lugar a dudas la parte más difícil de programar una aplicación es decidir por donde comenzar, nosotros hemos optado por programar una clase llamada Gato (además recuerdo que este juego tiene su origen dentro de una clase de programación orientada a objetos je).  Pues el diseño de la clase Gato y también una llamada Jugador son las siguientes:
quedando el código de ambas clase de la siguiente manera:

Clase GATO
/**
 * @author MartinC
 */
public class Gato {
    public int JugadorActual;
    public int Tamanio;
    public String Color;
    private String[] tablero;
    
    public Gato(){
        tablero = new String[10];
    }
    
    
    public boolean ColocarTiro(int posicion) {
        if (tablero[posicion]!=null) {
            return false;
        }
        
        if (JugadorActual==1) {
            tablero[posicion] = "X";
        } else {
            tablero[posicion] = "O";
        }
        
        return true;
    }
    
    public char BuscarGanador() {
        // Existen 8 combinaciones para ganar
        String cadena="";
        String valores = "";
        char resultado =' ';
        boolean ganador = false;
        
        if (JugadorActual ==1) 
            cadena = "XXX";
        else
            cadena = "OOO";
        
        valores = tablero[1] + tablero[2] + tablero[3];
        if (valores.equals(cadena)) { ganador = true; }

        valores = tablero[4] + tablero[5] + tablero[6];
        if (valores.equals(cadena)) { ganador = true; }

        valores = tablero[7] + tablero[8] + tablero[9];
        if (valores.equals(cadena)) { ganador = true; }

        valores = tablero[1] + tablero[4] + tablero[7];
        if (valores.equals(cadena)) { ganador = true; }

        valores = tablero[2] + tablero[5] + tablero[8];
        if (valores.equals(cadena)) { ganador = true; }

        valores = tablero[3] + tablero[6] + tablero[9];
        if (valores.equals(cadena)) { ganador = true; }

        valores = tablero[1] + tablero[5] + tablero[9];
        if (valores.equals(cadena)) { ganador = true; }

        valores = tablero[3] + tablero[5] + tablero[7];
        if (valores.equals(cadena)) { ganador = true; }
        
        if (ganador){ 
            if (JugadorActual ==1) 
                resultado = 'X';
            else
                resultado = 'O';
        }
        else {
            if (EstaLleno()) {
                resultado = '-';
            }
        }
        
        return resultado;
    }
    
    private boolean EstaLleno() {
        boolean lleno = true;
        for (int i=1; i<=9; i++) {
            if (tablero[i]==null) {
                lleno = false;
            }
        }
        return lleno;
    }
    
    public void CambiarTurno() {
        if (JugadorActual==1) {
            JugadorActual=2;
        }else
        {
            JugadorActual=1;
        }        
    }
}


Clase JUGADOR
/**
 * @author MartinC
 */
public class Jugador {
    public String Nombre;
    public String Simbolo;
}


Ahora,  recordemos el onClick de cada imageView del tablero del gato, dicho atributo permite el llamado de una función Java denominada "Tirando" que a su vez mandara a llamar al objeto Gato en este caso "Miau" para que este se encargue de procesar el tiro. NOTA: para que esta propiedad funcione debe indicarse también el atributo  "clickeable= true" como lo muestra el siguiente fragmento de código:

        

Otra cosa interesante es que cada ImageView se inicia con una imagen en blanco (algunos no están tan en blanco, al final de este paso se aclara la razón), y tiene un número de posición:



El código de la función "Tirando" que debe colocarse dentro de la clase que contiene el método Main(), es bueno aclarar que dicho método se vale de otros auxiliares llamados RealizarTiro (que hace uso del objeto "miau" para decidir la acción a realizar en la interfaz) y MostrarMarca (que muestra efectos visuales mediante la clase animation):

    public void Tirando(View v) {
        ImageView img = (ImageView) findViewById(v.getId());
        int index = Integer.parseInt(img.getTag().toString());
        RealizarTiro(index, img);
    }

    private void RealizarTiro(int posicion, ImageView btn) {
        if (!miau.ColocarTiro(posicion)) {
            return;
        }

        MostrarMarca(btn, posicion);

        char valor = miau.BuscarGanador();
        if (valor!=' ') {
            switch (valor){
                case 'X':
                    MostrarMensaje("Ganaste " + Jugador1.Simbolo);
                    break;
                case 'O':
                    MostrarMensaje("Ganaste " + Jugador2.Simbolo);
                    break;
                case '-':
                    MostrarMensaje("EMPATE!!!");
                    break;
            }
        }
        else {
            miau.CambiarTurno();
            if (miau.JugadorActual == 1) {
                txtNombre.setText(Jugador1.Nombre + " " + Jugador1.Simbolo);
                imagen.setImageResource(R.drawable.hombre);
            }
            else {
                txtNombre.setText(Jugador2.Nombre + " " + Jugador2.Simbolo);
                imagen.setImageResource(R.drawable.hombre2);
            }
        }
    }

    private void MostrarMarca(ImageView btn, int posic) {
        AnimationSet animacion = new AnimationSet(true);

        int rs = ScaleAnimation.RELATIVE_TO_SELF;
        ScaleAnimation escala = new ScaleAnimation(1,2,2,5,rs,0.5f, rs, 0.5f);
        escala.setDuration(500);

        ScaleAnimation escala2 = new ScaleAnimation(2,1,2,1,rs,0.5f, rs, 0.5f);
        escala2.setDuration(500);
        escala2.setStartOffset(500);

        AlphaAnimation aparicion = new AlphaAnimation(0,1);
        aparicion.setDuration(1000);

        animacion.addAnimation(escala);
        animacion.addAnimation(escala2);
        animacion.addAnimation(aparicion);

        int recurso=0;
        if (miau.JugadorActual == 1) {
            recurso = R.drawable.x;
            if (posic==2 || posic==5 || posic==8) {
                recurso = R.drawable.x_;
            }
        }
        else {
            recurso = R.drawable.o;
            if (posic==2 || posic==5 || posic==8) {
                recurso = R.drawable.o_;
            }
        }

        btn.setImageResource(recurso);

        btn.startAnimation(animacion);

    }

    private void MostrarMensaje(String msj) {
        Dialog dialogo = null;
        Builder builder = new Builder(this);

        builder.setTitle(msj);
        builder.setIcon(R.drawable.gato);
        builder.setPositiveButton("Volver a iniciar", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                miau = new Gato();
                miau.JugadorActual = 1;
                txtNombre.setText(Jugador1.Nombre + " - " + Jugador1.Simbolo);
                VaciarTablero();
            }
        });
        builder.setNegativeButton("Salir", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                finish();
            }
        });
        dialogo = builder.create();
        dialogo.show();
    }
    
    private void VaciarTablero() {
        ((ImageView)findViewById(R.id.imageView1)).setImageResource(R.drawable.blancoc);
        ((ImageView)findViewById(R.id.imageView2)).setImageResource(R.drawable.blanco);
        ((ImageView)findViewById(R.id.imageView3)).setImageResource(R.drawable.blancoc);
        ((ImageView)findViewById(R.id.imageView4)).setImageResource(R.drawable.blancoc);
        ((ImageView)findViewById(R.id.imageView5)).setImageResource(R.drawable.blanco);
        ((ImageView)findViewById(R.id.imageView6)).setImageResource(R.drawable.blancoc);
        ((ImageView)findViewById(R.id.imageView7)).setImageResource(R.drawable.blancoc);
        ((ImageView)findViewById(R.id.imageView8)).setImageResource(R.drawable.blanco);
        ((ImageView)findViewById(R.id.imageView9)).setImageResource(R.drawable.blancoc);
    }


IMPORTANTE:
Las lineas horizontales del tablero del gato fueron agregadas fácilmente con:

    
    

pero las lineas verticales, la verdad no fue algo a lo que encontré una solución rápida en su momento, por lo cual opte por generar una imagen que tuviera lados con linea, para que hicieran el efecto de linea vertical, tal como este:
lo cual hizo necesario también, la modificación de O y X para que si lanzaban a la posición correspondiente 2, 5 o 7 se mostrará la variante con linea jejeje, si alguien logra hacerlo sin dicho truco por favor compartan.


El código completo estará disponible más adelante...

PASO 4. La publicación

Para publicar aplicaciones es necesario registrarse como desarrollador Android Developer http://developer.android.com/intl/es/index.html e ingresar a la consola del desarrollador, pero para publicar es necesario realizar un pago de aprox. 25 dólares a la fecha de hoy. Los detalles de como publicar espero en algún momento poder subirlos, más no es el objetivo de esta entrada.

El resultado final esta publicada en el play store
https://play.google.com/store/apps/details?id=martinc.elgato

O puedes buscarla mediante las palabras clave:

martinc.elgato

Esto de esperar a que pasen las horas de salida del autobús, no dejan nada bueno....
Saludos Ing PAGA