4. Diseño e Implementación del juego propuesto
4.3. Planteo de la solución
4.4.3. Modelado de las acciones de juego
Teniendo ya la lógica que da soporte a las variables básicas del juego, se tienen que modelar las acciones de juego; para esto, conviene revisar el formato propio de las acciones del juego. Como se asentó en la sección 2.1, el juego consta de ciertas acciones básicas que se ejecutan constantemente durante el juego (descartes de ciertas cartas, tiradas de dados, movimientos de fichas), y de actividades más complejas y muy diversas entre sí, únicas de cada escenario o momentos de juego (actividades que deben hacerse en ronda, descartes comunes a todos los jugadores, todo tipo de combinaciones de las actividades básicas). La idea es modelar estas acciones de manera tal de que sea simple incorporar acciones nuevas a partir de la composición de acciones más simples.
La solución empleada fue un esquema de actualizaciones de juego (“game updates”) que ocurren cuando existe algún cambio en la lógica de juego. Esto se llevó a cabo a través de la creación de una clase Activity, que sirve como superclase abstracta de una serie de clases que modelan las distintas actividades de juego.
Ante la llegada de un mensaje “game update” al servidor, se actualiza el estado de juego de acuerdo al código contenido una actividad de esta clase (con un método “update”); cada juego tiene una variable “currentActivity” que almacena la actividad en curso.
Ante la llegada de un evento de esta clase al cliente, se actualiza la interfaz para reflejar los cambios que la actividad ha generado en el estado de juego con el código contenido en la actividad (con un método “draw”).
De esta forma, la funcionalidad tanto como para el servidor como para el cliente queda encapsulada en una instancia de una clase derivada de Activity. Cada actividad, también, podrá tener subactividades en sí, a ejecutarse secuencialmente, para permitir la composición precisa para formar actividades más complejas.
Fig. 13. Diagrama de Clases mostrando la estructura de las actividades del juego.
El código que compone a las actividades del juego que extienden a la clase Activity se especificó en un archivo (“gameActions.js”) donde todas están presentes; este código, visible para el cliente y para el servidor, permite que el envío de mensajes posea simplemente el nombre de la actividad a ejecutar y algunos parámetros extra, propios de cada actividad, evitando así tener que enviar en cada mensaje de actualización todo el código de cada actividad. Ésto, además, permite encontrar salida a una limitación propia de la plataforma: los parámetros de formato JSON de los mensajes en socket.io permiten sólo el envío de objetos con pares clavevalor, pero no de código (en este caso, funciones de Javascript).
Se puede observar entonces, a través de un diagrama de secuencia, el flujo general de actualización a través de actividades:
Fig. 14. Diagrama de Secuencia de Actualización del juego a través de la clase Activity.
Se ha descrito la clase Activity y cómo se lleva a cabo la actualización del estado de juego a través de ella. Sin embargo, todavía queda el problema de la composición de actividades para formar otras más complejas.
En el juego, es muy común tener una actividad que represente una elección: de acuerdo al input del cliente, se deben agregar tales o cuáles subactividades a la actividad que actualmente está en curso. Es decir, además de soporte para la actualización, es preciso tener soporte para agregar nuevas subactividades a la actividad en curso en forma dinámica, y para señalar que una actividad ha terminado de manera tal de que el juego se actualice propagando la siguiente subactividad en la lista. Esto se lleva a cabo a través de los mensajes “add activity” y “resolve activity”. Fig. 15. Diagrama de Secuencia de Agregar nueva subactividad. Fig. 16. Diagrama de Resolver actividad. El siguiente diagrama de clases completa la implementación con las clases y atributos faltantes. No se representan las instancias de las distintas actividades que heredan la estructura de Activity, por su número.
Figs. 17 (a y b). Diagrama de Clases (dividido en dos partes por cuestiones de legibilidad). El código de la clase “Activity”, así como las derivadas de ella, son visibles tanto al cliente como al servidor. El diseño planteado permite, entonces, instanciar los componentes de juego, definir actividades simples y a partir de ellas actividades más complejas. Una parte central del juego es la resolución de las actividades y eventos propios de cada escenario; cada uno de estos eventos generalmente constan una decisión entre varias alternativas donde cada alternativa es una conjunción de acciones. De esta forma, así como las actividades básicas del juego utilizan el esquema de la clase Activity, también los eventos de cada escenario han sido definidos como extensiones de ella. Las actividades simples, que se repiten a lo largo del juego, se encuentran definidas en un archivo de acciones de juego, que se cargan e instancian de acuerdo a las necesidades de cada momento de la partida; lo mismo aplica para los eventos y actividades de cada escenario que presentan las decisiones y en los que se forman las acciones más complejas a partir de la combinación de acciones de juego, y para las cartas especiales que al utilizarse llevan a cabo alguna acción de juego especial.
4.5. Persistencia de los datos
Como último término de la solución planteada para dar respuesta a los requerimientos funcionales, hay que referirse a la persistencia de los datos. Los requerimientos especifican que se desea guardar toda la información sobre las partidas disputadas por los usuarios. Como se ha dicho, la solución elegida (una base de datos MongoDB) permite almacenar en un esquema orientado a documentos, objetos con estructura JSON, de atributos variantes, en colecciones que agrupan elementos de similares características.
Si bien en muchos casos se explota la capacidad de almacenar objetos de formato heterogéneo, si se han definido esquemas para los objetos de distintas colecciones, que especifican los atributos “mínimos”, aquellos que seguramente han de estar presentes en todos los documentos que conforman cada colección (es importante aclarar que, si bien estos esquemas modelan atributos que seguramente estarán presentes, no existen chequeos en la base de datos que fuercen este hecho). Las distintas colecciones (representadas en la Figura 6), agrupan de forma intuitiva los datos de distinta naturaleza que se desean guardar (información sobre usuarios, sobre partidas, etcétera).
La funcionalidad central y menos trivial a la que es preciso dar respuesta en este apartado es el almacenamiento de toda la información sobre las partidas. Además de datos precisos sobre sus participantes y la configuración inicial de la partida, existe el requisito de almacenar toda la información respecto a cada acción de cada jugador a lo largo de la partida.
Esto fue resuelto siguiendo el esquema de “actividades” planteado en el inciso anterior: para cada partida, se almacena en un arreglo JSON cada una de las actividades que han ocurrido y quién es el jugador activo que las llevó a cabo o fue objeto de los cambios ocasionados por ellas. En este apartado es donde se ve más explotada la prestación de MongoDB que permite almacenar objetos de diferentes formatos: en el esquema está definido un espacio para guardar las acciones de juego, pero éstas tienen un formato muy
heterogéneo (lo único que todas tienen en común es un nombre, pero los parámetros de cada una pueden ser completamente distintos).
El mecanismo a través del cual y momento donde se almacenan éstas acciones merece un análisis especial. Durante la creación de una partida se crea un nuevo objeto de la colección “game” y se almacena en la base de datos; a lo largo de ésta, por cada actividad que llegue al servidor (a través del mensaje “update game”, cuyo funcionamiento ya se ha detallado), se guarda el objeto actividad que llega al servidor para la actualización del estado de juego, tal cual llega (en formato JSON, admitido por la base de datos), con su nombre, parámetros, y el alias del jugador activo. Este flujo puede observarse a través de una extensión del diagrama de secuencia que describe la actualización del estado de juego: Fig. 18. Diagrama de Secuencia de Actualización del juego a través de la clase Activity (con guardado de la actividad en la Base de Datos).
A partir, entonces, del diseño planteado para cada uno de los componentes, se logró proveer la funcionalidad que da respuesta a los requisitos funcionales del sistema. A continuación, se evaluarán algunos escenarios de calidad particulares del trabajo y cómo la solución planteada para cada uno ha extendido el diseño básico de la plataforma.