Mat|r tips: Desarrollando un tateti

Hola, soy Fernando, especialista en Mat|r, y en esta ocasión vengo a ustedes para mostrarles  algunas de las cosillas que pueden hacer con Mat|r Script. Para ello haremos un pequeño tateti que permita jugar a dos jugadores de forma remota. Bueno, sin nada más que agregar, ¡manos a la obra!. (Nota: El objetivo de esta publicación es mostrar cómo de sencillo es lograr algunas cosas. Para detalles les compartiré el código completo comentado al final.)

 

Primero definamos el juego para no tener dudas más adelante:

 

El tateti -o tres en raya- es un juego bastante simple. Consta de un tablero de 9 casillas donde dos jugadores se turnan para realizar sus movimientos. El objetivo es conseguir colocar tres fichas propias en línea -ya sea vertical, horizontal o diagonal- antes que el contrario. Quien consiga colocar tres piezas en línea gana la partida.

 

Con esto ya podemos pasar a definir la estructura básica del juego:

 

  • Tablero de 9 casillas
  • Dos jugadores (cada uno representado por una ficha)
  • X
  • O

 

Con las siguientes reglas:

  • Cada jugador se turna para colocar una ficha en el tablero.
  • El jugador que va primero es aleatorio.
  • A partir del 3er movimiento de cada jugador sobre el tablero, podría darse una victoria.
    • Para analizar una victoria basta con ver la columna y fila.
  • Si en el turno número 8 no hay ganador el resultado ya será empate.

 

Bien, con eso en mente vamos a comenzar con nuestra aplicación.

 

Primero tendremos una pantalla donde cada jugador podrá ingresar su nombre y darle a listo para esperar a su contrincante, algo similar a la pantalla que se encuentra a un costado.

 

Para esto vamos a crear una pequeña experiencia como la siguiente:


Experience Join {

   //Local Player Nick

   String nick label(‘Enter your Nick’) as TextField

   

   //Checking of online players

   Bool checkPlayer1 value(false)//Local

   Bool checkPlayer2 value(false)

   

   //Join

   Decision joining action(‘InContext.join’) label(‘Ready!’)

   

   //Labels

   String readyPlayer1 value(‘Ready!’) as Label

   String readyPlayer2 value(‘Ready opponent’) as Label

}

 

 

Como se puede observar, basta con definir esos componentes que se ven en la imagen de ejemplo de manera sencilla dentro de la experiencia y de esta manera ya queda listo el bindeo de cada componente de UI con los atributos de la experiencia.

 

La siguiente pantalla, una vez ambos jugadores estén listos, deberá ser el tablero de juego, entonces haremos una pantalla similar a la que muestro aquí al lado. Para lograrla debemos escribir el siguiente código para definir dicha experiencia:


Experience Game {

   //Players

   String player1 value(‘Player 1’) as Label

   String player2 value(‘Player 2’) as Label

   

   //Current turn

   Integer turn value(-1)

   String turnS as Label

 

   //Squares of the board

   String s00 value(”) as Image

   String s01 value(”) as Image

   String s02 value(”) as Image

   

   String s10 value(”) as Image

   String s11 value(”) as Image

   String s12 value(”) as Image

   

   String s20 value(”) as Image

   String s21 value(”) as Image

   String s22 value(”) as Image

 

   //Amount of moves and results

   Integer moves value(0)

   Bool winner value(false)

   String winerS value(”)

   

   //Logic board

   Array<Array<Integer>> chips value([[0,0,0],[0,0,0],[0,0,0]])

   

   //Last move

   Integer row value(-1)

   Integer column value(-1)

   

   //Labels

   String you value(‘You’)

   String opponent value(‘Opponent’)

}

 

 

Con solo definir esos pocos componentes ya podremos construir nuestro tablero de tateti, de forma rápida y sencilla. Como pueden ver, la forma que tendrán los jugadores para interactuar con el tablero en este caso será a través de eventos de las imágenes que se encuentran bindeadas a los Strings nombrados c00, c01, etc. Más adelante hablaré en detalle sobre cómo implementar dichos eventos.

 

Implementando ASYNC

Bien, ya tenemos ambas pantallas correctamente construidas y listas para editar su UI, pero ¿de qué manera se comunicarán dos jugadores que se encuentren jugando desde dispositivos totalmente diferentes? Para lograr esto vamos a apoyarnos en una nueva funcionalidad de Mat|r: “ASYNC”.

 

ASYNC nos permite registrar un dispositivo en un canal para que sea capaz de recibir mensajes de todos los dispositivos que envíen mensajes a dicho canal. Estos mensajes no son más que modelos Mat|r, por lo que si lo que queremos enviar es, por ejemplo, el nombre de usuario de uno de los jugadores bastará con definir un modelo en nuestra aplicación que tenga definido un String en el cual guardaremos el nombre de usuario. Luego deberemos crear una instancia de ese modelo y asignarle al String el nombre que ingrese el usuario, y enviar esa instancia al canal para que los demás dispositivos que estén escuchando dicho canal reciban al instante el modelo que contiene el nombre.

 

Pero esto no es todo, ya que para suscribir una aplicación Mat|r a un canal debemos primero definir una función que será el callback de dicho canal, es decir que se ejecutará cada vez que se reciba un mensaje que provenga de dicho canal. Esto permite facilitar mucho el tratamiento de los mensajes que reciben gracias a ASYNC.

 

Con esta explicación ya podemos ser capaces de ver el potencial que puede tener ASYNC. Para esta aplicación de ejemplo no solo lo vamos a usar para enviar y recibir los nombres de los jugadores, sino que, yendo más allá, lo usaremos también para enviar cada jugada de los jugadores y actualizar los tableros cada vez que uno juegue. Puede sonar complejo pero resultará muy sencillo.

 

Lo primero es crear los canales que vamos a utilizar. Nos apoyaremos en la herramienta de abm de canales disponible en el Modeller de la plataforma. Cada canal necesitará un modelo que será el mensaje que se enviará a través de él. Por lo que definiremos los siguientes modelos:

 

Model ReadyMessage {

   //To check of local publish

   String nick

}

 

Model Turn {

   //Counter of turn

   Integer number

}

 

Model Winner {

   String nick

}

 

Model Tie {

   String tie

}

 

 

Una vez tenemos los modelos definidos solo resta crear los canales. Para hacer esto tenemos que hacer click al tab de “Channels” que podemos encontrar en la izquierda. Una vez allí clickeamos en “create new channel”. A continuación coloco unas imagenes para que vean las configuraciones de cada uno de los canales.

Este canal lo utilizaremos para enviar avisos entre jugadores cuando ya estén listos, por lo que lo enviaremos cuando cualquiera de los jugadores haga tap en el botón “Listo!” de la primera pantalla.

 

 

Estos dos canales son los que nos ayudaran a enviar la información de cada movimiento de los jugadores de un extremo a otro. Cuando un jugador coloca una pieza en el tablero (O – X) lo primero que deberemos hacer es enviar la información de Juego. Con ello se actualizará el tablero del otro jugador, luego el otro canal avisara a ambos del cambio de turno para que sea simultáneo y no permita al jugador que ya jugó volver a hacerlo dos veces seguidas.

Por último, luego de cada jugada vamos a analizar si se trata de una jugada ganadora. Para ello en este caso vamos a emplear el método de “cuadro mágico” (para más detalle observar código). Luego del chequeo, si la última jugada hecha resulta ser ganadora, se publica por el canal de “Terminar” para notificar a ambos jugadores sobre el ganador de la partida, en cambio si no hay ganadores y ya se ha alcanzado el máximo número de movimientos en la partida el resultado es empate. En ambos casos, al notificar a ambos el resultado, también se limpiará el tablero y prepararemos todo para la siguiente partida.

 

Las reglas del juego

 

Para terminar, volviendo a la pantalla del tablero, implementaremos los eventos para cada casillero del mismo. Para ello solo deberemos generar una regla por cada celda donde haremos la jugada y publicaremos el mensaje en el canal “Juego”. Les dejo un vistazo de como se ve la regla que le corresponderá al casillero 0 – 0:

 

Rule r00 {

       if (game.turn == playerLocal.number && game.s00 == ”) {

           if (game.turn == 2) {

 

   //If the Player 2 is playing, we mark with “O”, then we increase the number of moves and upgrade the “logic” board.

               game.s00 = o

               game.moves = game.moves + 1

               game.chips.get(0).set(0, game.turn)

            

   // We save the last move to do controls.

               game.row = 0

               game.column = 0

               

               //publish game – We send the information of the board.

               broker.async.publish(‘Game’, game)

               turn.number = game.turn

 

               //publish turn – We send the notification of the turn change.

               broker.async.publish(‘Turn’, turn)

           } else {

 

   //The Player 1 is playing, we mark with “X”, then we increase the number of moves and upgrade the “logic” board.

               game.s00 = x

               game.moves = j.jugadas + 1

               game.chips.get(0).set(0, game.turn)

               

               game.row = 0

               game.column = 0

               

               //publish game

               broker.async.publish(‘Game’, game)

               turn.number = game.turn

 

               //publish turn

               broker.async.publish(‘Turn’, turn)

         }

     }

}

 

Con ello listo ya tendremos casi lista nuestra APP Tateti. Solo resta completar algunos parámetros de ejecución que dejaré en el código completo e implementar la interfaz gráfica.

Por ahora les compartiré la interfaz simple que se ve más arriba. En una próxima publicación vamos a pulir un poco más nuestro código, y también  jugaremos con el editor de UI de Mat|r. Les mostraré cómo generar e implementar una UI más profesional para sus APPs y no perecer en el intento.

El siguiente enlace los llevará a la página del hub de esta aplicación: https://platform.matrproject.com/hub/view/5b59cbd6f7d2e900112f8072 , allí podrán conseguir el código completo. Eso es todo por ahora, sigan atentos al contenido del blog, pronto habrán más posts!

 

Saludos!!!