scieee AI-readable full text Open interactive document viewer

Sistema para la enseñanza de automatización de juegos aplicada al estudio del póker en su variante Texas Hold’em

Rincón Martínez, Santiago

Abstract

Este trabajo pretende ofrecer soporte a la enseñanza de la asignatura Herramientas Informáticas para Juegos de Azar. Esta asignatura está orientada al desarrollo de estrategias matemáticamente consistentes para juegos de azar, y a la implementación de las mismas, y se imparte en la actualidad en la Facultad de Informática de la Universidad Complutense de Madrid. Específicamente, se orienta a la variante del póker conocida como No-Limit Texas Hold’em. Para ello, se han especificado en la Introducción tanto las reglas de dicho juego como un breve estudio de la importancia de este ámbito. Se han desarrollado dos herramientas independientes que han constituido los dos bloques de la presente memoria. La primera herramienta es un sistema que permite accesos diferentes a profesores y estudiantes. Los primeros tienen la posibilidad de diseñar tanto preguntas de práctica como exámenes. Además, pueden controlar el desempeño de los estudiantes en dichas preguntas y exámenes. Los estudiantes tienen la posibilidad de realizar las actividades diseñadas por su profesor. El sistema se ha desarrollado de forma modular mediante un modelo de desarrollo scrum para el que se ha mantenido una comunicación constante con el director del trabajo, profesor de la asignatura, que ha hecho las veces de cliente. La segunda herramienta dota a los estudiantes de una plantilla que permite, a través de un IDE de desarrollo, la implementación sencilla de algoritmos autónomos, con visibilización de los resultados, así como el testeo de los mismos a través de partidas con jugadores humano. Este sistema se ha desarrollado tomando como base el código creado por David Pérez para su herramienta ‘jpoker’ (https://github.com/dperezcabrera/jpoker).

Full text

Sistema para la Enseñanza de Automatización de Juegos aplicada al Estudio del Poker en su variante Texas Hold’em Santiago Rincón Martínez DOBLE GRADO EN INFORMÁTICA Y MATEMÁTICAS UNIVERSIDAD COMPLUTENSE DE MADRID TRABAJO FIN DE GRADO 15/05/2018 Director: Manuel Núñez García I A mi familia, que me ha apoyado cuando más falta me ha hecho, y ha creído en mí cuando ni siquiera yo lo hacía A las personas que he conocido en la universidad y que me acompañarán toda mi vida A Belén, que ha probado todos y cada uno de los fallos de esta aplicación varias veces II Agradecimientos Gracias a la Universidad Complutense por darme la oportunidad de realizar este trabajo, en particular a Manuel Núñez García por hacer las veces de corrector, cliente, tutor y amigo, ayudándome a llevarlo a buen puerto. Gracias asimismo a David Pérez Cabrera por su desarrollo de código abierto de la herramienta ‘jpoker’, que me ha facilitado sobremanera el desarrollo de la segunda parte de este trabajo. III Índice Resumen…………………………………………………………………………………………….VI Abstract…………………………………………………………………………………………….VII Introducción…………………………………………………………………………………………..1 Poker……………………………………………………………………………………………..1 Motivación del trabajo…………………………………………………………………………...4 Bloque I: Herramienta de Práctica y Puesta a Prueba………………………………………………..5 Herramientas y Métodos………………………………………………………………………....5 Primera Iteración: Desarrollo de la Aplicación Básica……………………………………...7 Especificación de Requisitos…………………………………………………………...7 Desarrollo de los Requisitos……………………………………………………………8 Presentación de Requisitos…………………………………………………………....10 Segunda Iteración: Correcciones e Introducción de Profundidad y Atractivo Visual……..11 Especificación de Requisitos………………………………………………………….11 Desarrollo de los Requisitos…………………………………………………………..12 Presentación de Requisitos……………………………………………………………15 Tercera Iteración: Correcciones e Introducción del Sistema de Sincronización…………..15 Especificación de Requisitos………………………………………………………….15 Desarrollo de los Requisitos…………………………………………………………..16 Presentación de Requisitos……………………………………………………………17 Cuarta Iteración: Correcciones, Introducción de Exportación de Datos y Eliminación de los Conjuntos de Práctica……………………………………………………………………...18 Especificación de Requisitos………………………………………………………….18 Desarrollo de los Requisitos…………………………………………………………..18 Presentación de Requisitos……………………………………………………………20 Conclusión: Aplicación Final…………………………………………………………………..20 Módulo de Utilidades……………………………………………………………………...20 Módulo de Profesores……………………………………………………………………..23 Módulo de Estudiantes…………………………………………………………………….31 Bloque II: Plantilla para el Desarrollo y Puesta a Prueba de Estrategias Automáticas……………..37 Herramientas y Métodos……………………………………………………………………….37 Estudio del Sistema ‘jpoker’………………………………………………………………37 Modificación del Sistema ‘jpoker’………………………………………………………..38 IV Creación del Sistema de Plantillas, estrategias expertas y análisis estadístico y probabilístico de las mismas………………………………………………………………38 Conclusión: Resultado Final…………………………………………………………………...42 Empaquetado “.jar”………………………………………………………………………..42 Plantilla de Programación…………………………………………………………………45 Método main………………………………………………………………………………………...46 Apéndices…………………………………………………………………………………………...47 Apéndice A: código de la herramienta jPokerLearningFun……………………………………47 Apéndice B: código del sistema de plantillas…………………………………………………..47 Apéndice C: código del archivo “.jar” usado en el sistema de plantillas………………………47 Bibliografía………………………………………………………………………………………….48 V Resumen Este trabajo pretende ofrecer soporte a la enseñanza de la asignatura Herramientas Informáticas para Juegos de Azar. Esta asignatura está orientada al desarrollo de estrategias matemáticamente consistentes para juegos de azar, y a la implementación de las mismas, y se imparte en la actualidad en la Facultad de Informática de la Universidad Complutense de Madrid. Específicamente, se orienta a la variante del póker conocida como No-Limit Texas Hold’em. Para ello, se han especificado en la Introducción tanto las reglas de dicho juego como un breve estudio de la importancia de este ámbito. Se han desarrollado dos herramientas independientes que han constituido los dos bloques de la presente memoria. La primera herramienta es un sistema que permite accesos diferentes a profesores y estudiantes. Los primeros tienen la posibilidad de diseñar tanto preguntas de práctica como exámenes. Además, pueden controlar el desempeño de los estudiantes en dichas preguntas y exámenes. Los estudiantes tienen la posibilidad de realizar las actividades diseñadas por su profesor. El sistema se ha desarrollado de forma modular mediante un modelo de desarrollo scrum para el que se ha mantenido una comunicación constante con el director del trabajo, profesor de la asignatura, que ha hecho las veces de cliente. La segunda herramienta dota a los estudiantes de una plantilla que permite, a través de un IDE de desarrollo, la implementación sencilla de algoritmos autónomos, con visibilización de los resultados, así como el testeo de los mismos a través de partidas con jugadores humano. Este sistema se ha desarrollado tomando como base el código creado por David Pérez para su herramienta ‘jpoker’ (https://github.com/dperezcabrera/jpoker). Palabras clave: Aprendizaje Automático, Enseñanza, Estadística, Hold’em, Juegos de Azar, Poker, Probabilidad. VI Abstract The main goal of this work is to offer support to the teaching of the subject Herramientas Informáticas para Juegos de Azar. This subject is oriented to the development of mathematically consistent strategies for gambling games, and to their implementation, and it is currently taught in the Ciomputer Science School of the Complutense University of Madrid. Specifically, it is oriented to No-Limit Texas Hold'em variant of poker. For this purpose, the rules of the game and a brief study of the importance of this area have been presented in the Introduction. We have developed two independent tools that conform the two blocks of the manuscript. The first tool allows lectures and students different access. Lectures can design practice questions and exams. In addition, they can check the performance of students in these questions and exams. Students can carry out the activities designed by their lecturer. The system has been developed in a modular way by using a scrum methodology having a permanent communication with the supervisor, lecturer of the subject, who has played the role of client. The second tool provides students with a template that allows them, through an IDE, the simple implementation of autonomous algorithms, with visibility of the results, as well as their testing by playing against a human player. The system has been developed on top of David Pérez's code for his 'jpoker' tool (https://github.com/dperezcabrera/jpoker). Keywords: Automatic Learning, Games of Chance, Hold’em, Poker, Probability, Statistics, Teaching. VII Introducción Póker El póker es, según la Real Academia Española de la Lengua (s.f.), un “juego de naipes con baraja francesa en el que se reparten cinco cartas a cada jugador, se hacen apuestas y descartes, y gana quien reúne la combinación superior entre las varias establecidas” (http://dle.rae.es/?id=TgAzL2H). Formalmente, se trata de un juego de azar, asimétrico, de suma cero y de información incompleta. Es evidente que el póker es un juego de azar: las cartas que cada jugador recibe, aleatorias, determinan el resultado de la partida. Ello complica la decisión de la estrategia a seguir, dado que el seguimiento de la estrategia “correcta” en absoluto garantiza la victoria en una partida concreta, sino que predice la victoria en términos generales en un conjunto de partidas lo bastante grande (consecuencia de la Ley de los Grandes Números), siempre que se enfrente a estrategias “menos correctas”. Además, el póker es un juego asimétrico, no solo por la aleatoriedad de las jugadas – diferentes – en la mano de cada jugador, sino porque, al jugarse por turnos, la información disponible para un jugador que juega después de otro es superior. Es de suma cero porque lo que un jugador gana lo pierde otro siempre, y no se pierde dinero salvo la comisión que cobra el casino o agente que organiza el juego. Lo que hace especialmente interesante el estudio de este juego es que se trata de un juego de información incompleta: no se conocen las jugadas ni las estrategias del resto de jugadores. Esto dificulta sobremanera la elección de una estrategia “correcta”, dando gran calado a la resolución de dicho problema. El problema de estrategias en el Póker es, además, muy diverso, pues existen un gran número de variantes del juego, y la situación cambia radicalmente dependiendo de factores tales como el número de jugadores que tomen partido en la mano o la cantidad que se establezca para la “ciega pequeña” (la primera apuesta del primer jugador a la izquierda del repartidor, obligatoria, que será doblada obligatoriamente – “ciega grande” – por el segundo jugador, a la izquierda del primero). 1 La variante en la que se centra este trabajo es la variante No-Limit Texas Hold’em, conocida también por sus siglas NLHE. Esta variante se caracteriza por jugarse en cinco etapas (pre-flop, flop, turn, river y showdown), con cinco cartas que se van desvelando en el centro de la mesa y con las cuales, además de las dos que tiene ocultas en su mano, cada jugador conforma su jugada final. La etapa de pre-flop es la primera que se juega: cada jugador recibe las dos cartas que mantendrá ocultas el resto de la partida y se sigue una ronda de apuestas, iniciada por la entrada en el “bote” (el dinero apostado en una determinada mano) de la “ciega pequeña” y la “ciega grande”. La etapa flop es la ronda de apuestas jugada tras el revelado de las tres primeras cartas de la mesa, que se revelan juntas, como una unidad. Las etapas turn y river son las rondas de apuestas tras el revelado de la cuarta y quinta carta, respectivamente. Finalmente, el showdown es la etapa en la que se decide el resultado de la mano. La rondas de apuestas se juegan por turnos, en sentido horario. La apuesta actual la marca el jugador que más fichas ha apostado en la ronda y, ante esa apuesta, cada jugador tiene las opciones de retirarse (fold) perdiendo las fichas que ya ha puesto en el bote en esa ronda o en anteriores y no volviendo a jugar la mano, igualar la apuesta (call) poniendo en el bote las fichas exactas que le permitan haber introducido en la ronda la misma cantidad de fichas que la apuesta actual, apostar a su vez (raise) introduciendo más fichas de las necesarias para hacer call y estableciendo así la apuesta en una superior, o bien hacer all-in introduciendo todas las fichas que posee y contando automáticamente como call aunque no llegase a la cantidad requerida, o como bet por el número de fichas concreto apostado si se supera la cantidad necesaria para el call. La ronda de apuestas termina cuando todos los jugadores hacen call, fold o all-in ante una apuesta concreta. Un jugador que ha hecho all-in solamente opta al bote acumulado en la ronda en la que hizo el all-in, las apuestas de rondas posteriores se dirimen entre los jugadores que participan activamente en ellas. En la etapa showdown se revelan las dos cartas en la mano de cada jugador activo y se decide el ganador (o ganadores, en caso de que haya habido algún all-in o empate). Entre varias manos posibles gana la mano que tenga la mejor jugada y, a iguales jugadas, las cartas más altas. Las jugadas que pueden conseguirse en póker son, por orden de mejor a peor: •Escalera Real de Color: As, rey, reina, jota y diez de un mismo palo. •Escalera de Color: Cinco cartas consecutivas de un mismo palo. •Poker: Cuatro cartas con el mismo número. 2 A continuación el reto a enfrentar fue el desarrollo de clases de utilidad que permitiesen, a través de métodos estáticos, el almacenamiento de los datos de estudiantes y profesores. Este requisito cristalizó en la creación de cuatro clases (StudentLoader, StudentSaver, TeacherLoader y TeacherSaver). Las clases Loader constaban de un método para comprobar la corrección de una contraseña concreta y de otro que, usando el primero, recibía como argumentos un nombre de usuario y una clave y, si la identificación era correcta, devolvía un objeto tipo Student en el caso de la clase StudentLoader o una pareja de listas enlazadas conteniendo preguntas la primera y exámenes y conjuntos de práctica la segunda en el caso de la clase TeacherLoader. Por su parte, las clases Saver solamente constaban de un método que, recibiendo o bien un objeto Student (StudentSaver) o bien un nombre, una clave y dos listas enlazadas, una de preguntas y otra de exámenes y conjuntos de práctica (TeacherSaver), almacenaban los datos en un archivo .data. Los profesores y estudiantes se almacenaban en carpetas separadas, y los archivos .data se identificaban unívocamente por el nombre de usuario. Así, podía haber un estudiante y un profesor con el mismo nombre, pero no dos estudiantes ni dos profesores. Esta forma de almacenamiento de los datos presentaba un problema: para cargar los datos de un estudiante era necesario cargar las preguntas disponibles desde los distintos archivos de profesores, y para las preguntas realizadas era poco práctico guardar una copia de toda la pregunta en cada estudiante. Para resolver esto se optó por mantener un archivo más que hiciese las veces de base de datos de profesores, de forma que para cargar un estudiante se pudiese recibir los datos de cada profesor. El mantenimiento de este archivo se haría a través de dos nuevas clases TeacherDatabaseSaver y TeacherDatabaseLoader. Las seis clases encargadas del mantenimiento de la información se desarrollaron de forma totalmente independiente y sin interacciones entre ellas (el método de carga de estudiantes recibía la base de datos como parámetro). El siguiente punto a enfrentar fue el desarrollo de una interfaz gráfica para profesores, que se hizo a través de una clase principal TeacherGUI y varias clases gráficas de apoyo. Para éste desarrollo se utilizaron las facilidades que ofrece el IDE (entorno de desarrollo) Netbeans, apoyándose en la guía que facilita en su propia web dicho IDE. Al desarrollar en esta clase principal las funcionalidades de edición de preguntas, en particular de preguntas de imagen, se decidió utilizar, con algunas modificaciones, el manejador de imágenes que ofrece en su proyecto ‘jpoker’ David Pérez. Además, se enfrentó el requisito de almacén propio de 9 las imágenes de pregunta mediante la inclusión en la clase TeacherGUI de un método estático que recibía una ruta de origen y otra de destino y copiaba el archivo. Este método era accedido una vez confirmada la creación de la pregunta para no copiar imágenes innecesarias. Por último, se diseñó, utilizando también las facilidades ofrecidas por Netbeans, el entorno gráfico a través del cual los estudiantes podían responder conjuntos de práctica o exámenes, apoyado en la clase principal StudentGUI. Para enfrentar el requisito de visualización de las preguntas textuales, se utilizaron las imágenes para cartas ofrecidas por David Pérez en el proyecto ‘jpoker’, y de nuevo su manejador de imágenes para ellas. Presentación de requisitos En la presentación de requisitos se comprobó la aplicación funcional desarrollada, imágenes de la cual se pueden observar en las Figuras 4, 5 y 6. Ante el desempeño de ésta se observaron los siguientes defectos: •En las preguntas se echaba de menos poder incluir una descripción de las mismas – se ofrecía la posibilidad de incluir notas, pero no se incluía un campo para una descripción extensa. •Era fácil salir de la aplicación por error, o incluso cerrar una ventana de opciones que estaba ocultando una de las ventanas principales y que la aplicación se siguiese ejecutando pero de forma invisible. •En el proceso de inicio de sesión como estudiante o profesor, se ofrecía la posibilidad de crear una nueva cuenta en la misma ventana que la de iniciar sesión en una cuenta ya creada, lo que resultaba anti-intuitivo. •En la creación de preguntas, posibilidad de ver en las opciones incluidas la puntuación asociada a las mismas, puesto que se presentaba la opción pero no se podía ver qué puntuación se le había asignado. 10 Figura 4: StudentGUI en la primera iteración Figura 5: Creación de pregunta textual en la primera iteración •Sustitución del rango de puntuaciones entre -1 y 1 que se había utilizado por un rango entre -10 y 10. •Posibilidad para los estudiantes de dejar la respuesta a una determinada pregunta en blanco. Además, en la parte de la retrospectiva, el desarrollador detectó una cierta falta de modularidad, concretamente en la parte de almacenamiento y carga de archivos de datos, que obligaba a la repetición de una importante cantidad de código y dificultaba la introducción de cambios en el mismo. Segunda Iteración: Correcciones e Introducción de Profundidad y Atractivo Visual Especificación de Requisitos La carga de la segunda iteración fue significativamente menor que la de la primera, si bien se introdujeron cambios muy relevantes en la aplicación, sobre todo en lo concerniente al atractivo visual general. El nuevo paquete de requisitos establecido se especifica a continuación: •Dotado a la parte visual de la aplicación de colores que hiciesen más placentero su uso tanto para profesores como para estudiantes. •Dotado a la creación y edición de preguntas textuales de una mayor parte gráfica en cuanto a la visibilidad de las cartas incluidas en una determinada jugada, tanto comunitarias como de mano. •Cifrado de los datos guardados de profesores y estudiantes. •Inclusión en las preguntas de posibilidad de etiquetado, simple o múltiple. •Inclusión para estudiantes de la posibilidad de filtrar los conjuntos de práctica o exámenes por etiquetas. •Almacenamiento de las respuestas dadas por los estudiantes a cada pregunta concreta, en lugar de solamente la nota del conjunto general de preguntas. 11 Figura 6: TeacherGUI en la primera iteración •Inclusión al responder los estudiantes preguntas de la posibilidad de añadir una explicación de su elección de respuesta, almacenando la misma junto con la respuesta. •Posibilidad para los estudiantes de revisar las respuestas dadas a un conjunto de práctica o examen, sin modificarlas. Además de este nuevo paquete de requisitos, en esta iteración se resolvieron los problemas identificados en la presentación de requisitos de la primera iteración, así como los identificados en la retrospectiva de la misma. Desarrollo de los requisitos Antes de empezar a enfrentar los nuevos requisitos especificados, el primer punto del desarrollo se centró en resolver lo que había quedado pendiente de la primera iteración. La modificación del rango de puntuaciones resultó trivial, así como la inclusión de la puntuación asociada a cada opción en el entorno visual de edición de preguntas de los profesores, puesto que las opciones se implementaban como una tabla hash donde la clave era el enunciado de la opción y el valor su puntuación, por lo que bastó imprimir por pantalla el esquema “clave: valor” en lugar del esquema “clave” que se imprimía previamente. Para ofrecer a los estudiantes la posibilidad de dejar una respuesta en blanco se incluyó en la confirmación de creación de las preguntas la inclusión automática de una opción adicional con texto vacío y valorada en cero puntos. Además, se eliminó en todas las ventanas el marco de las mismas, suprimiendo así la opción de cerrar la aplicación por un medio que no fuera el botón de cierre establecido, y a éste se le incluyó la apertura de un mensaje de confirmación de cierre para evitar salir por error. En cuanto al sistema de almacenamiento de datos, se suprimió del cuerpo de carga del estudiante la mayor parte del código – el relacionado con la carga de preguntas de profesores – y se sustituyó por una llamada al método de carga de cada profesor, manejando directamente la lista enlazada de conjuntos de práctica y exámenes devuelta por éste, consiguiendo así una disminución significativa del código duplicado y una mayor modularidad ya que en el modelo obtenido toda carga de archivos de profesores, y sólo la carga de archivos de profesores, se realizaba desde la clase TeacherLoader y toda carga de archivos de estudiantes, y sólo la carga de archivos de estudiantes, 12 se realizaba desde la clase StudentLoader. La división similar en las clases TeacherSaver y StudentSaver ya estaba conseguida al final de la primera iteración. Revisados estos puntos, se pasó al fin a la implementación en la aplicación de los nuevos requisitos establecidos. Para la introducción de colores se volvieron a utilizar las facilidades de Netbeans y, tras probar varios modelos de coloreado diferentes – sobre las ventanas, textos… – se decidió establecer un sistema de colores sobre los botones común a la aplicación, así como un muy puntual coloreado de textos en mensajes de error o confirmación. En cuanto al aumento del atractivo visual de la creación de preguntas textuales, se incluyó la imagen de las cartas añadidas, actualizándose ésta en tiempo real gracias a un listener sobre las ComboBoxes (cajas de elección múltiple) a través de las que se escogía el palo y el número de las mismas. Para ello se utilizaron las imágenes y el controlador de imágenes ya utilizados para la representación de cartas en la presentación de las preguntas a los estudiantes para su resolución. El siguiente paso fue el cifrado de los datos almacenados de profesores y estudiantes, para el que se utilizó la clase de cifrado ofrecida por Arturo Tena (https://gist.github.com/arturotena/9235042). Con esto se llegó a los dos grandes cambios funcionales de la aplicación en esta segunda iteración: la inclusión de etiquetado y filtrado respecto al mismo y la inclusión de explicaciones de las respuestas y el almacenamiento de explicaciones y respuestas. El primer cambio requirió la revisión ligera de la clase GeneralQuestion, en la que se incluyó un vector para almacenar en él las etiquetas pertinentes. De la mano de este cambio vinieron cambios sobre los entornos gráficos de creación de preguntas y sobre el método de almacenamiento de datos de profesores, pues era necesario almacenar las etiquetas. Por otro lado, para que se pudiese realizar el filtrado, las etiquetas debían ser conocidas independientemente del usuario que abriese la aplicación, por lo que se incluyeron nuevos métodos en las clases TeacherDatabaseSaver y TeacherDatabaseLoader, por entenderse una cierta similitud entre el almacenamiento de una base de datos de etiquetas y el almacenamiento que ya se daba de una base de datos de profesores – ambas almacenaban solamente un conjunto de Strings, y ambas debían ser accesibles de forma universal desde los entornos de distintos usuarios. 13 Para terminar con la inclusión de etiquetado, restaba ofrecer la posibilidad a los estudiantes de filtrar por etiqueta, para lo cual se incluyó en la clase StudentGUI un método que, dada una lista de paquetes de preguntas y una etiqueta concreta, extraía una sublista con los paquetes que tuvieran alguna pregunta con dicha etiqueta. También se modificó el sistema de selección de conjuntos de práctica o exámenes a realizar para que soportase el que la lista presentada difiriese de la lista completa de conjuntos de práctica o exámenes disponibles, añadiendo una comprobación de nombres en lugar del puntero de posición que había previamente (los conjuntos de práctica o exámenes disponibles se presentaban listados en un objeto tipo jList por orden). El último requisito considerado fue el relativo a introducir en las preguntas la posibilidad de dar explicación a la opción elegida y el almacenamiento de estas respuestas y de la explicación a ellas, puesto que requería una revisión profunda del sistema de almacenamiento de conjuntos de práctica o exámenes respondidos. Finalmente, se sustituyó el sistema de tabla hash con clave el conjunto de práctica o examen y valor la nota obtenida por un sistema de tabla hash con clave el conjunto de práctica o examen y valor una tabla hash de clave las preguntas del conjunto o examen y valor una pareja de String que contenía la opción escogida y la explicación ofrecida. Además, se incluyó en la clase GeneralQuestion un método que dado un String devolvía la puntuación asociada a la opción coincidente con dicho String o bien un error en caso de no existir tal opción. Análogamente, en la clase Quiz se incluyó un método que, dado una tabla hash de preguntas y parejas de respuesta y explicación, calculaba la nota del conjunto de práctica o examen de ser posible o devolvía un error en caso contrario. De la mano de estos cambios fueron sencillas modificaciones en el entorno gráfico de respuesta a preguntas para habilitar la opción de aportar una explicación, y cambios no tan sencillos en el almacenamiento de estudiantes, que pasó de almacenar los conjuntos de práctica y exámenes como parejas del nombre del conjunto o examen y el valor numérico de la nota a almacenarlos como estructuras más complejas, aunque aún sin llegar a almacenar duplicados de las preguntas ni de los conjuntos de práctica o exámenes. 14 Figura 7: Creación de pregunta textual en la segunda iteración Presentación de requisitos En la presentación de requisitos de la nueva versión de la aplicación (Figuras 7 y 8) se observaron los siguientes puntos: •Al retirar los marcos no podían moverse las ventanas de la aplicación, lo cual resultaba molesto. •El color del texto de los mensajes de advertencia (amarillo) dificultaba su lectura. •Al haber introducido descripciones extensas de las preguntas, las notas y, en las preguntas textuales, la especificación del número de jugadores resultaban innecesarias. En la retrospectiva, por otro lado, no se observó ningún punto relevante a mejorar. Tercera Iteración: Correcciones e Introducción del Sistema de Sincronización Especificación de Requisitos La carga de especificaciones de la tercera iteración fue muy concreta: la aplicación ya funcionaba y hacía falta implementar los siguientes puntos: •Almacenamiento de los datos en red de modo que se pudiesen acceder desde distintos dispositivos. •Actualización automática de los datos de la aplicación. •Guardado automático de los datos en la red en el mismo momento que se guardan en el dispositivo local. •Aligerado de la carga de espacio en el dispositivo local. •Minimización de los tiempos de espera de carga. •Seguridad en el canal de comunicación. •Independencia de la aplicación de contenidos externos en el dispositivo local. 15 Figura 8: TeacherGUI en la segunda iteración Desarrollo de los requisitos Si bien la carga de requisitos de la tercera iteración pudiera parecer menor que la de la segunda, se contaba con el hándicap de haber trabajado menos con tecnologías de almacenamiento en red que con tecnologías locales, lo cual obligaría a una importante búsqueda de soluciones y un estudio intenso de la materia para hallar la mejor manera de responder a los requisitos planteados. Antes de eso, sin embargo, se resolvieron los problemas identificados en la presentación de requisitos de la segunda iteración. Para ello se volvieron a incluir los marcos – manteniendo retirada la funcionalidad de redimensión de las ventanas por ser visualmente inadecuada – pero se dotó a los botones de cierre de distintos listener para que su uso fuera equivalente al de los respectivos botones de cancelar o salir; se modificó el color de los mensajes de advertencia, oscureciéndolo, y se retiraron los atributos de almacenamiento, en GeneralQuestion y Question respectivamente, de las notas y el número de jugadores, realizando en consecuencia un mínimo rediseño de los entornos gráficos de creación de preguntas y respuesta a ellas. A la hora de resolver el problema de la sincronización, se valoraron distintas opciones, como el diseño de una aplicación en servidor que se comunicase con la aplicación nativa para el manejo de los datos. Sin embargo, en aras de la sencillez, se resolvió contar con un servidor FTP en el que almacenar los datos, actualizando los ficheros de dicho servidor al guardar en el dispositivo nativo. Al estudiar esta solución en mayor profundidad, resaltó como inconveniente la inseguridad del protocolo de comunicación FTP, por lo que finalmente se decidió, en su lugar, utilizar un servidor SSH con una funcionalidad similar. Existía el obstáculo añadido de que no se contaba con un servidor en la universidad, y no se quería utilizar dicho servidor para pruebas, sino migrar allí los datos una vez la aplicación fuera estable. Por ello, se optó por la creación en el ordenador del desarrollador de un servidor SSH para poder realizar las pruebas sobre éste. En la aplicación se añadió una clase SSHConnector, que, a través de dos métodos get y put, recibía un directorio y un archivo en el mismo y o bien lo copiaba de ese directorio local al correspondiente directorio en el servidor o bien la inversa, creando en el dispositivo local el directorio si era necesario. 16 Para la codificación de esta clase se utilizaron como base distintas guías de manejo de SSH con java, que se pueden consultar en la bibliografía de este trabajo, resaltando la disponible en la web https://programacion.net/articulo/conectar_via_ssh_con_java_1163. Otra de las cuestiones a solventar fue el cómo manejar la firma digital necesaria para el establecimiento de la conexión SSH, pues no era una buena práctica acompañar la aplicación de ella de forma externa, además de atentar contra el requisito de que fuese una aplicación independiente de recursos externos en el dispositivo local. Finalmente, se decidió incluir la firma en el interior del .exe y extraerla cuando hiciese falta para establecer la conexión, borrándola inmediatamente al acabar la transferencia de archivos. La solución, tal como quedó definitivamente, consistía en que, cuando tenía que cargar, la aplicación se conectaba al servidor y descargaba los archivos correspondientes en el dispositivo local, donde los abría y manejaba. Posteriormente, al guardar un archivo, volvía a conectarse al servidor y copiaba allí el archivo guardado, sobrescribiendo el existente si era el caso. Este método se daba sobre cualquier archivo cargado o guardado, incluyendo los archivos de datos de profesores y estudiantes, las bases de datos de profesores y etiquetas y las imágenes tanto propias de la aplicación como de preguntas de imagen creadas. Este método presenta ciertos problemas con el manejo de la misma cuenta de la aplicación desde diferentes dispositivos, pero se desestimó ese escenario puesto que en principio cada estudiante o profesor debe tener su propia cuenta, por lo que no tenía sentido sacrificar la sencillez para dar respuesta a una problemática presumiblemente insignificante. Una vez decidido e implementado todo, se contactó con Luis Llana para solicitar la habilitación de un servidor SSH en la universidad, y se creó en él la estructura de directorios necesaria, modificando también en la aplicación los campos necesarios para que la conexión pasase a darse con dicho servidor. Presentación de requisitos A la vista de los resultados de desarrollo de esta tercera iteración resaltó solamente un punto: los tiempos de carga desconcertaban, por lo que era mejor, en lo posible, habilitar una pantalla de carga para aportar seguridad al usuario. 17 Por parte de la retrospectiva se consideró que era poco productivo mantener los archivos en el dispositivo local cuando se sustituirían por los del servidor SSH cuando hiciera falta, además de atentar contra el requisito de minimizar la carga de archivos locales. Cuarta Iteración: Correcciones, Introducción de Exportación de Datos y Eliminación de los Conjuntos de Práctica Especificación de Requisitos Dado el uso continuado de la aplicación durante el proceso de desarrollo, en esta fase de especificación de requisitos habían aparecido necesidades no consideradas previamente, que dieron lugar a la siguiente relación: •Eliminación de los conjuntos de práctica y sustitución de los mismos por preguntas de práctica. •Posibilidad para los profesores de exportar en un formato accesible las respuestas de los estudiantes a un determinado examen o pregunta. •Posibilidad para los estudiantes de exportar sus propios datos. •Posibilidad para los profesores de visualizar preguntas como las visualizaría un estudiante. Desarrollo de los requisitos Como en anteriores iteraciones, lo primero fue corregir los problemas identificados en la etapa de presentación de requisitos y retrospectiva de la iteración previa. Para ello, se tomaron dos medidas: por un lado, para dar visibilidad al proceso de carga de archivos, se diseñó una ventana de carga que se presentaría mientras la aplicación está obteniendo datos del servidor, y se ocultaría al acabar el proceso; por otro, se introdujo un paso adicional al copiar un archivo desde el dispositivo local al servidor: borrar ese archivo del dispositivo local. Corregidos estos problemas, se decidieron las soluciones para los nuevos requisitos. El más sencillo era la habilitación de la posibilidad para los profesores de visibilizar una pregunta como la visibilizarían los estudiantes, pues para resolverlo bastó utilizar el módulo gráfico de respuesta de preguntas de los estudiantes, solo que sin recibir la respuesta dada en sitio alguno. 18 ventana desplegada. La ventana tiene un marco no redimensionable, y ante el botón de cierre se comporta como ante el de “Cancel”. La clase CreateAccountTeacher recibe como parámetros un String con el nombre predeterminado del usuario y una lista enlazada de Strings con los nombres de los profesores existentes hasta el momento, y despliega una ventana con un cuadro de texto, relleno al principio por el nombre predeterminado recibido, dos cuadros de contraseña, un botón de “Cancel” y uno de “Create Account”. El botón de “Cancel” inicializa y hace visible un objeto tipo LoginTeacher, y cierra la ventana de la clase CreateAccountTeacher en cuestión. En cuanto al botón de “Create Account”, comprueba que el contenido del cuadro de texto no sea un nombre ya en uso (no esté en la lista enlazada recibida como parámetro), ni tenga espacios, y que los contenidos de los cuadros de contraseña coincidan; si alguna de esas premisas falla, muestra una ventana de error, en otro caso, llama al método save de la clase TeacherSaver, con el contenido del cuadro de texto como nombre, el del primer cuadro de contraseña como contraseña, y listas vacías de preguntas y exámenes como listas de ídem, y al método saveDatabase de la clase TeacherDatabaseSave, con la lista enlazada de nombres, añadiéndole el contenido del primer cuadro, como parámetro. Con esto, la cuenta de profesor ha quedado creada, y se inicializa un nuevo objeto TeacherGUI con los datos de dicha cuenta como parámetros, antes de cerrar la ventana desplegada por la clase CreateAccountTeacher. La ventana tiene un marco no redimensionable, y ante el botón de cierre se comporta como ante el de “Cancel”. La clase CreateOption recibe como parámetros al inicializarse el objeto tipo EditQuestion o EditOtherQuestion que la ha inicializado y un int que marca de cuál de los dos tipos de objetos se trata. Despliega una ventana con dos cuadros de texto, uno para el enunciado de la opción y otro para su valor, y dos botones: de “Cancel”, que se comporta como el botón de “Cancel” de las clases ChooseQuestion, AddLabel o similares, ya vistas; y de “Submit”, que trata de convertir el contenido del segundo cuadro de texto a tipo Double desplegando un error en caso de no lograrlo, comprueba que el valor se halle entre menos diez y diez, desplegando un error en caso contrario, y, de no haber habido error, se llama, con el par de enunciado y valor como atributo, al método addOption del objeto recibido por parámetro. La ventana tiene un marco no redimensionable, y ante el botón de cierre se comporta como ante el de “Cancel”. Entramos ahora en algunas las clases más grandes del módulo, como son EditQuestion, EditOtherQuestion o EditQuiz. Para simplificar su explicación consideremos que todas ellas reciben el objeto del que dependen como parámetro, al que llamaremos objeto padre, en el caso de las dos 25 primeras puede ser un objeto de tipo TeacherGUI o EditQuiz, por lo que también recibirán como parámetro un int que marque el tipo; en el caso de la tercera, se trata de un objeto tipo TeacherGUI. Además, las tres clases tienen también en común el contar con un doble constructor, según si se facilita un objeto base – tipo OtherQuestion para la clase EditOtherQuestion, tipo Question para EditQuestion y tipo Quiz para EditQuiz – o no. En el primer caso, el constructor actualiza la interfaz como si todos los elementos del objeto base se hubieran añadido ya, de tal modo que puede editarse el objeto en cuestión. En el segundo, la interfaz se inicializa como si el objeto a editar fuera un objeto vacío. Las tres ventanas tienen un marco no redimensionable, y ante el botón de cierre se comportan como ante el de “Cancel”, haciendo visible al objeto padre y cerrándose. Ambos constructores tanto de la clase EditOtherQuestion como de la clase EditQuestion utilizan la clase TeacherDatabaseLoad para cargar una lista enlazada de las etiquetas existentes. La clase EditOtherQuestion proporciona la interfaz para la edición o creación de una pregunta, contando con un cuadro de texto para el nombre, un área de texto para la descripción, una lista para las etiquetas añadidas, con dos botones – uno para añadirlas, que inicializa y hace visible un objeto de la clase SelectLabel, facilitando al propio objeto del tipo EditOtherQuestion, el número dos y la base de datos de etiquetas como parámetros; y otro para eliminarlas, usando para ello los índices mínimo y máximo de selección en la lista de etiquetas –, la imagen seleccionada para la pregunta (si no hay ninguna se utiliza una imagen predeterminada que marca esta carencia), con un botón “Choose Image” que abre un cuadro de diálogo de selección de archivos, guardando la ruta del archivo seleccionado; una lista que presenta las opciones con sus puntuaciones asociadas, con dos botones – uno para añadir opciones, que inicializa y hace visible un objeto de la clase CreateOption, facilitando al propio objeto del tipo EditOtherQuestion y el número dos como parámetros; y otro para elimilarlas, usando para ello los índices mínimo y máximo de selección en la lista de opciones. Esta ventana también consta de los botones de “Cancel”, ya explicado, y “Submit”, que comprueba que ni el nombre (el contenido del cuadro de texto correspondiente) ni la ruta de imagen estén vacías y que existan opciones. Si no está presente, se añade una opción de texto vacío y puntuación cero. Con los datos resultantes se crea un objeto de la clase OtherQuestion, facilitando el cual como parámetro se llama al método addQuestion del objeto padre antes de cerrar la ventana. Esta clase tiene, por otro lado, los métodos addLabel, selectedLabel y addOption para atender a los objetos que genera. El primero sirve para almacenar en la base de datos de etiquetas del servidor cuando se ha creado una nueva etiqueta: recibe ésta como parámetro, la añade a la lista enlazada de 26 etiquetas que ya había cargado y llama al método saveLabels de la clase TeacherDatabaseSaver con la nueva lista enlazada como parámetro. El segundo añade a la lista de etiquetas de la pregunta representada la recibida como parámetro, en caso de que dicha etiqueta no estuviera ya en la lista, y hace visible la ventana, reseteando la vista. El último recibe como parámetro una pareja de un String (el enunciado de la opción) y un Double (su valor), añade el enunciado como clave y el valor como valor a la tabla hash de opciones de la pregunta representada y hace visible la ventana, reseteando la vista. La clase EditQuestion es muy similar a la clase EditOtherQuestion, salvo porque no hay imagen ni ruta de la misma, y porque al inicializar instancias de la clase SelectLabel o CreateOption el número que facilita como parámetro es el uno en lugar del dos. Además, tiene nuevos componentes y métodos: tiene dos imágenes de cartas, manejadas de forma similar a la forma en la que se manejaba la imagen en la clase AddCard con los dos jComboBox que cada una de las imágenes tiene a su derecha. Estas dos cartas forman las cartas de la mano de la pregunta textual representada y, al pulsar “Submit”, se comprueba que no sean iguales entre sí ni a ninguna carta de la mesa, mostrando un error en lugar de crear la pregunta en caso contrario. También tiene una lista de cartas que representa a las comunitarias, con dos botones – uno para añadir cartas, que inicializa y hace visible un objeto de la clase AddCard, facilitándole el propio objeto de la clase EditQuestion como parámetro, salvo que ya haya cinco cartas comunitarias, en cuyo caso muestra un error; y otro para eliminarlas, usando para ello los índices mínimo y máximo de selección en la lista de cartas comunitarias –. También se comprueba, al pulsar “Submit”, que las cartas comunitarias sean cero, tres, cuatro o cinco, para que responda a alguna de las fases del juego. El método adicional de la clase EditQuestion respecto de EditOtherQuestion es el método addCard, que recibe como parámetro una carta, comprueba que no coincida con ninguna de las cartas comunitarias ya añadidas haciendo visible un error si lo hace, y añade, si no es así, la carta recibida a la lista de cartas comunitarias, haciendo visible la ventana y actualizando la vista. Tanto los objetos de la clase EditQuestion como los de la clase EditOtherQuestion, al pulsar “Submit”, si no hay opciones de puntuación máxima (diez puntos), dan de alta la pregunta correctamente pero hacen visible un mensaje de advertencia. La clase EditQuiz representa un objeto de tipo Quiz, para lo que hace visible una ventana con un cuadro de texto para el nombre del examen y una lista donde se representan las preguntas incluidas en el examen por su nombre. La ventana tiene también seis botones, un botón “Cancel”, que ya ha sido explicado, un botón “Delete Question” que elimina la(s) pregunta(s) seleccionada(s) en la lista 27 de preguntas usando los índices mínimo y máximo de selección, un botón “Edit Question” que elimina la pregunta seleccionada después de utilizarla para pasarla como parámetro para la inicialización de un objeto de la clase EditQuestion o EditOtherQuestion, según el tipo de pregunta, también se pasa como parámetro el propio objeto tipo EditQuiz y el número dos y se hace visible el objeto creado; también hay un botón “CreateQuestion” que crea y hace visible un objeto de la clase ChooseKindQues, facilitándole como parámetros el propio objeto tipo EditQuiz y el número dos. Los dos botones restantes son los botones “Add Existing Question” y “Submit”, el primero crea un objeto de la clase ChooseQuestion, con parámetros la lista de preguntas que se pueden utilizar y el propio objeto EditQuiz (la lista de preguntas la recibía previamente el objeto EditQuiz en su constructor); el segundo comprueba que el examen tenga nombre y al menos una pregunta, haciendo visible un mensaje de error si no es así, y construyendo con la lista de preguntas y el nombre un objeto tipo Quiz que se pasa como parámetro al método addQuiz de la clase padre antes de cerrar la ventana si sí es así. Otro campo de la ventana, no editable, informa del número máximo de puntos que tiene el examen en cada momento (cada pregunta se considera que da un máximo de diez puntos). También hay en la clase EditQuiz un método más para dar respuesta a los objetos que lo precisan: un método addQuestion que recibe como parámetro una pregunta y, si no está ya añadida, la añade a la lista de preguntas. Además, si la pregunta que recibe es tipo OtherQuestion copia la imagen en el directorio señalado por el valor imgRoute de aquella a través de un método del objeto padre (tipo TeacherGUI), y modifica el valor de imgRoute para que apunte en la nueva dirección. Después hace visible la vista, actualizada. En cuanto a la clase LoginTeacher, es la encargada de abrir las cuentas de profesor. Para ello, cuenta con una ventana con un campo de texto, uno de contraseña y tres botones, el de “Cancel”, que crea y hace visible una instancia de la clase ExitConfirmation; el de “Create Account”, que crea y hace visible un objeto de la clase CreateAccountTeacher, facilitándole como parámetro el contenido del cuadro de texto, antes de cerrar el propio objeto tipo LoginTeacher, y el de “Login”, que intenta realizar la carga de los datos de profesor mediante una llamada al método load de la clase TeacherLoader, con el nombre introducido en el cuadro de texto y la contraseña introducida en el campo de contraseña como parámetros. Si la carga no tiene éxito, se hace visible un mensaje de error y, si lo tiene, se crea y hace visible un objeto de tipo TeacherGUI con ese mismo nombre y clave, y las listas de preguntas y exámenes devueltas por el método load de la clase TeacherLoader, antes de cerrarse la propia ventana de LoginTeacher. 28 La ventana tiene un marco no redimensionable y ante el botón de cierre se comporta como ante el de Cancel. La clase SelectLabel recibe como parámetros un objeto padre tipo EditQuestion o EditOtherQuestion, así como un int que marca el tipo del objeto padre y una lista enlazada de objetos de tipo String (las etiquetas ya existentes). Para cumplir su función, la clase despliega una ventana con un elemento tipo jComboBox con las etiquetas ya existentes y tres botones, el de “Cancel”, que hace visible el objeto padre y cierra la ventana; el de “Create Label”, que crea un objeto tipo AddLabel con el propio objeto SelectLabel como parámetro, y el de “Add”, que llama al método selectedLabel del objeto padre con el String de la clase elegida mediante el desplegable jComboBox como parámetro y cierra la ventana. Además, también cuenta con un método addLabel que recibe un String como argumento y, si no lo contiene ya la lista de etiquetas disponibles, lo añade a ésta y llama al método addLabel de la clase padre para que se guarde la nueva lista de etiquetas disponibles. Después de esto, hace visible la ventana, actualizada. La ventana tiene un marco no redimensionable y ante el botón de cierre se comporta, de nuevo, como ante el de Cancel. La última clase gráfica de este módulo es la clase TeacherGUI, la que sirve como centro de control de la plataforma de profesor. Su constructor recibe un nombre, una clave y sendas listas de preguntas, preguntas de otros profesores y exámenes. Esta clase presenta una ventana con tres grandes listas – la de preguntas propias, la de preguntas de otros profesores y la de exámenes –, cada una con cinco botones: uno para añadir, uno para eliminar, uno para editar, uno para previsualizar y otro para exportar los datos, excepto la de preguntas de otros profesores, que solamente cuenta con un botón de previsualización. Además de eso, cuenta con un objeto jLabel que informa del nombre de usuario, un botón para cambiar la contraseña y otro para salir. Los botones de añadir, editar y eliminar de la lista de preguntas funcionan como los de la clase EditQuiz, solo que facilitando como parámetro un uno en lugar de un dos. El botón de previsualizar inicializa y hace visible una instancia de la clase Quizzing o OtherQuizzing (según el tipo de la pregunta) del paquete exercises.studentsSection, facilitándole como parámetros la pregunta en cuestión, el objeto TeacherGUI y el número tres. El botón de exportar datos ejecuta el método exportQuestdata de la clase TeacherSaver, con el nombre del profesor y la pregunta seleccionada 29 como argumentos. Si se ejecuta correctamente, hace visible un mensaje informativo de el archivo “.csv” de salida. En cuanto a los botones de la lista de exámenes, el de añadir inicializa y hace visible un objeto tipo EditQuiz facilitándole como argumentos la lista de preguntas creadas y de otros profesores y el propio objeto TeacherGUI, el de editar hace lo mismo pero eliminando el examen seleccionado después de facilitárselo al constructor de EditQuiz como tercer argumento, mientras que el de eliminar elimina el examen seleccionado. El de previsualizar inicializa un objeto tipo QuizController, facilitándole como argumentos el examen seleccionado, el objeto TeacherGUI y el número tres. El botón de exportar datos ejecuta el método exportTestdata de la clase TeacherSaver, con el nombre del profesor y el examen seleccionado como argumentos. Si se ejecuta correctamente, hace visible un mensaje informativo de el archivo “.csv” de salida. El botón de cambio de contraseña inicializa y hace visible un objeto de la clase ChangePassword, con la clave, el número uno y el propio objeto TeacherGUI como argumentos, mientras que el de salir inicializa y hace visible un objeto de la clase ExitConfirmation. La ventana tiene un marco no redimensionable y ante el botón de cierre se comporta, en este caso, como ante el de salir. La clase también cuenta con métodos para atender a otros objetos, como el método changedPassword, con la nueva clave como argumento, que modifica la clave y guarda los datos del profesor – con la clave modificada – a través del método save de la clase TeacherSaver; el método addQuestion que funciona de forma similar al método addQuestion de la clase EditQuiz solo que después almacena los datos del profesor, con la lista de preguntas modificada, a través del método save de la clase TeacherSaver; el método addQuiz que añade el examen que recibe como parámetro a la lista de exámenes si no está ya en ella, hace visible la ventana actualizada y almacena los datos del profesor, con la lista de exámenes modificada, a través del método save de la clase TeacherSaver, y el método estático moveImage que recibe una ruta de origen y otra de destino, copia el archivo que haya en el origen a la ruta destino y llama al método put de SSHConnector para que copie el archivo a la correspondiente dirección del servidor. Nos queda observar las clases TeacherSaver y TeacherLoader, que contienen los métodos estáticos save y load respectivamente y, en el caso de TeacherSaver, también los métodos exportQuestdata y exportTestdata y, en el caso de TeacherLoader, también el método getTeacherInfo. El método load recibe un nombre de usuario una contraseña y una base de datos de profesores, trae el archivo 30 correspondiente al nombre de usuario al dispositivo local usando el método get de SSHConnector, lo abre, lo descifra usando cipherUtils, comprueba que la contraseña recibida coincide y parsea el resto de los datos utilizando distintos marcadores con el formato “%EO[..]&”. Después, carga las preguntas del resto de profesores usando la base de datos. Si el proceso se da completo correctamente, devuelve una pareja con una pareja de listas, la primera de preguntas propias y la segunda de preguntas de otros profesores, y una lista de exámenes; en otro caso, lanza una excepción. El método save, por otro lado, recibe un nombre, contraseña, una lista de preguntas y una lista de exámenes y los almacena localmente en un archivo que viene dado por el nombre, con la contraseña en primer lugar y utilizando los marcadores correspondientes, y cifrando la información mediante cipherUtils. Una vez guardado localmente, se utiliza el método put de SSHConnector para dejar el archivo en el servidor. Los métodos exportTestdata y exportQuestdata reciben respectivamente un examen y una pregunta y revisan uno a uno los estudiantes, obteniendo los datos de los que hubieren realizado la pregunta o examen y almacenándolos en formato “.csv”, formateados y separados por comas. El nombre del archivo viene dado por el nombre de la pregunta o examen. El método getTeacherInfo sirve para dar servicio al módulo de estudiantes, es muy similar al método load pero no usa clave ni base de datos y simplemente devuelve una pareja de listas, la primera con las preguntas propias y la segunda con los exámenes. Con esto llegamos al último módulo: el módulo de estudiantes. Con el fin de aligerar la carga y no repetir información, al explicar dicho módulo nos referiremos a menudo a formas de estructurar las clases vistas en este módulo. Módulo de Estudiantes El módulo de estudiantes, en el paquete exercises.studentsSection, tiene ciertas similitudes con el de profesores. Consta de las clases CreateAccount, LoginStudent, OtherQuesView, OtherQuizzing, QuesView, QuizViewer, QuizController, Quizzing, Student, StudentGUI, StudentLoader y StudentSaver. La clase Student modela el estudiante, constando de atributos para nombre, contraseña, exámenes disponibles, preguntas disponibles, exámenes hechos y preguntas hechas. Los dos primeros son 31 atributos tipo String; los dos siguientes son listas enlazadas, de objetos tipo Quiz en el primer caso y tipo GeneralQuestion en el segundo, y los dos últimos son tablas hash con clave de tipo Quiz en el primer caso y GeneralQuestion en el segundo, y con valor tipo tabla hash de clave GeneralQuestion y valor parejas de String en el primer caso, y tipo pareja de String en el segundo caso. En cuanto a métodos, la clase tiene métodos getter de todos sus valores, y setter de todos menos el nombre. Además, cuenta con los métodos addTestDone, que comprueba que el test que recibe como argumento esté entre los disponibles y no entre los realizados, y lo añade a los realizados con la tabla hash de valor también recibida como argumento, para después retirarlo de los disponibles; y addQuestionDone, que hace lo mismo que addTestDone pero con preguntas en lugar de exámenes y no retira la pregunta de las disponibles. Las clases CreateAccount y LoginStudent son muy similares a las clases CreateAccountTeacher y LoginTeacher del módulo de profesores, solo que manejan objetos de tipo Student en lugar de conjuntos de nombre, clave, lista de preguntas y lista de exámenes, y se comunican con StudentSaver, StudentLoader y StudentGUI en lugar de TeacherSaver, TeacherLoader y TeacherGUI, respectivamente. La clase equivalente a TeacherGUI del módulo de estudiantes sería StudentGUI, sin embargo, dada la gran diferencia de funcionalidades entre ambas clases, es importante estudiar la clase StudentGUI con algo más de detalle. La clase se inicializa recibiendo siempre como parámetro un objeto de la clase Student, y consta de una ventana con tres listas: la primera muestra las preguntas disponibles, la segunda los exámenes disponibles y la tercera los exámenes realizados. Además, la primera tiene dos botones, uno para realizar la pregunta y otro para comprobar la respuesta que se dio, la segunda tiene un solo botón, para hacer el examen, y la tercera uno para comprobar la respuesta que se dio. La primera y la segunda listas tienen cada una un objeto jComboBox que permite elegir alguna de las etiquetas existentes o bien la etiqueta genérica “All”, de forma que se filtren los elementos de la lista a solo aquellos que contengan la etiqueta seleccionada – esto se implementa mediante un listener de cambios del jComboBox, controlado por un boolean que marque cuándo la ventana está inicializada y estable, como ya ocurría, por ejemplo, en la clase AddCard –. Por último, hay un jLabel que muestra el nombre del usuario, otro que muestra su puntuación media (de los exámenes) y unos botones “Change Password” y “Exit” equivalente a los homónimos en TeacherGUI. También hay un botón “Export Data” que permitirá exportar los datos del estudiante a un archivo en formato “.csv” separado por comas. 32 El botón de realizar pregunta inicializa y hace visible un objeto de la clase Quizzing u OtherQuizzing, según si la pregunta es de tipo Question u OtherQuestion, con parámetros la pregunta seleccionada, el propio objeto StudentGUI y el número uno; el botón de realizar examen, en cambio, solamente inicializa y corre un objeto (no gráfico) de tipo QuizController, con parámetros el test seleccionado, el propio objeto StudentGUI y el número dos. Ambos invisibilizan la ventana. Por su parte, el botón de visibilizar pregunta comprueba que la pregunta seleccionada se ha realizado, exponiendo un mensaje de error en caso contrario, y inicializa y hace visible un objeto de la clase QuesView u OtherQuesView según si la pregunta seleccionada es de tipo Question u OtherQuestion, con parámetros la pregunta seleccionada, la respuesta que se dio a dicha pregunta, la explicación que se ofreció y el objeto StudentGUI. El botón de visibilizar examen, por su parte, inicializa y hace visible un objeto QuizViewer, con parámetros el test seleccionado, la tabla hash asociada con las preguntas del test y sus respuestas y explicaciones ofrecidas y el propio objeto StudentGUI. Ambos botones invisibilizan la ventana. Resta comentar el botón de exportar datos, que simplemente ejecuta el método estático exportStuData, de la clase StudentSaver, y muestra un mensaje notificando la dirección del archivo “.csv” resultante si no hay errores. La ventana tiene un marco no redimensionable y ante el botón de cierre se comporta como ante el de salir. Además, la clase cuenta con métodos necesarios para implementar las relaciones con otros objetos: el método changedPassword es equivalente al método del mismo nombre en la clase TeacherGUI; el método answeredQuestion, por su parte, recibe como argumento una pregunta, un String de respuesta y un String de explicación, los usa como parámetros para llamar al método addQuestionDone del objeto Student – el que se recibió en el constructor, que se mantiene como atributo de clase – y, después, llama al método estático saveStudent de la clase StudentSaver con el mismo Student como parámetro, para finalmente hacer visible la ventana actualizada. El método quizzDone es muy parecido a addQuestionDone: recibe un objeto Quiz, y una tabla hash con las preguntas del examen con sus respuestas y explicaciones, y llama con ello al método addTestDone del Student, para luego usar éste como argumento de saveStudent y después hacer visible la ventana actualizada. 33 Las clases Quizzing y OtherQuizzing se encargan de mostrar una pregunta en un formato en el que pueda recibir respuesta, y de notificar dicha respuesta. Ambas se inicializan recibiendo como parámetros la pregunta que deben representar – tipo Question en la primera y OtherQuestion en la segunda –, el objeto padre al que deben notificar la respuesta, tipo StudentGUI, QuizController o TeacherGUI, y un int para marcar cuál es el tipo en concreto del objeto padre. Las ventanas generadas por estas clases se diferencian en el aspecto de los campos no modificables, que en la primera incluyen las dos cartas de la mano y las hasta cinco de la mesa, representadas gráficamente, y en la segunda la visualización de la imagen asociada a la pregunta. Además de esto, ambas ventanas poseen un campo en el que puede leerse la descripción de la pregunta, un área de texto en el que introducir una explicación a la opción elegida, un objeto jComboBox cuyas opciones son las distintas opciones de la pregunta representada, y un botón “Next”. Dicho botón llama al método answeredQuestion del objeto padre, con parámetros la propia pregunta, la opción elegida en el objeto jComboBox y la explicación ofrecida en el área de texto, y hace visible un mensaje informando de cuál era la opción más correcta, si el objeto padre es tipo StudentGUI o QuizController. Si es tipo TeacherGUI simplemente lo hace visible. En cualquiera de los dos casos, la ventana se cierra. La ventana tiene un marco no redimensionable, y ante el botón de cierre hace visible un mensaje de error informando que es necesario terminar la pregunta o examen antes de salir. La clase QuizController se encarga de manejar la realización de exámenes. Para ello, cuenta con los siguientes atributos: un atributo tipo Quiz, que almacena el examen que se está realizando, inicializado mediante un atributo de este tipo recibido en los parámetros del constructor; un objeto padre de tipo StudentGUI o TeacherGUI, también recibido en el constructor junto con un parámetro tipo int que marca el tipo de objeto, y que se almacena en otro atributo; una tabla hash de clave tipo GeneralQuestion y valor parejas de String, inicializada vacía, y en la que se guardan las respuestas y explicaciones que se van dando a las preguntas del examen, y un int más que sirve de señalador de cuál es la siguiente pregunta que toca responder, inicializado a cero en el constructor. El manejo de estos atributos se da gracias al método answeredQuestion y al método runQuizz. El primero recibe como parámetros una pregunta y dos String (la respuesta dada y la explicación), y los introduce en la tabla hash de respuestas, incrementa en uno el señalador y llama a runQuizz. Éste método runQuizz, por su parte, comprueba si el señalador apunta fuera del rango de preguntas del examen – si su valor es mayor que el tamaño de la lista enlazada de preguntas –. Si no es así, inicializa y hace visible un objeto Quizzing u OtherQuizzing según el tipo de la pregunta apuntada por el señalador, con dicha pregunta como atributo, además del propio objeto QuizController y el 34 Donde z representa el máximo valor para el cual, en una normal de media cero y varianza uno, la probabilidad de que se supere (P(Z≥z)) es menor que 0’05. Comprobando en la tabla correspondiente (Figura 10), vemos que ese valor es el valor 1’64. Sustituyendo en los puntos extremos: Y operando: O lo que es lo mismo: Es decir que, como se puede comprobar, la proporción tiene un margen de error pequeño y dependiente de c ( ). 41 Figura 10: Tabla de la distribución Normal de media cero y varianza uno Por último, se mejoraron ligeramente las estrategias para que en ningún caso apostaran fichas de más si podían forzar el all-in del rival con una cantidad menor al propio all-in. También se incluyó como jugable la estrategia aleatoria ya ofrecida por David Pérez en su proyecto. Implementados estos bots, así como las variaciones comentadas en el sistema ‘jpoker’, se empaquetó todo ello en un archivo “.jar” que hiciese las veces de librería del proyecto, y se procedió al desarrollo de plantillas para nuevas estrategias desarrolladas por los alumnos y de un método main que ejecutase el conjunto. Para las plantillas, se plasmó en comentarios sobre implementaciones vacías de la interfaz IStrategy un resumen del funcionamiento del sistema orientado a hacer lo más trivial posible el desarrollo. Por su parte, en el método main se incluyeron procesos para customizar las partidas, de forma que se pudiesen orientar a la prueba masiva de estrategias o a una visualización de la forma de jugar de las mismas. Conclusión: Resultado Final La herramienta tiene tres elementos principales: el empaquetado “.jar”, la plantilla de programación y el método main de ejecución. Empaquetado “.jar” En el archivo “.jar” encontramos el sistema ‘jpoker’ completo, con las variaciones introducidas, así como el bot experto contra el que jugar. El sistema ‘jpoker’, diseñado por David Pérez, ofrece la funcionalidad de lanzar partidas de poker mediante un sistema de máquina de estados, donde cada estado realiza las funciones pertinentes y, una vez ha terminado, transiciona a un determinado estado siguiente según si se cumplen una serie de requisitos u otros. Entre las funcionalidades implementadas sobre el sistema de David Pérez encontramos el jugador humano, una implementación particular de la interfaz IStrategy. 42 La interfaz IStrategy es, en el sistema de David Pérez, la que implementan los distintos jugadores. Cuenta con una serie de métodos que se explicarán en mayor profundidad al explicar la plantilla de programación. El principal método, sobre el que se implementa el jugador humano, es el método getCommand, al que el sistema llama esperando como resultado una determinada jugada entre las posibles (fold, call, bet con el número concreto de fichas apostadas o bien all-in). Para la implementación del jugador humano, en este método se procede a mostrar distintas ventanas de opciones, utilizando los métodos ofrecidos por la clase JOptionPane, de forma que se pueda decidir la jugada manualmente. En primer lugar se establece un listado de las opciones a elegir – las distintas posibles jugadas – en el formato de un array tipo String, así como el icono a mostrar en la ventana. Hecho esto, se invoca el método showInputDialog, en su versión de siete atributos (el objeto en el que se debe incluir, null en nuestro caso; el mensaje que debe mostrar, “Select your command”; el título, “Command”; el tipo de cuadro de diálogo entre varios ofrecidos por la misma clase JOptionPane, utilizamos DEFAULT_OPTION; el icono a mostrar, que será el antes establecido; las opciones posibles, también las establecidas previamente, y la opción predeterminada, que será fold en nuestro caso). Éste método devuelve un objeto que castearemos a String para conocer la opción elegida y tratarla con un switch que devolverá la jugada pertinente. En el caso concreto de bet, utilizaremos de nuevo un showInputDialog, ahora de cuatro atributos (objeto en el que incluirlo, a null; mensaje a mostrar, “Number of chips”; título, “Number Of Chips”, y tipo de diálogo, DEFAULT_OPTION), que muestra un cuadro de diálogo con un cuadro de texto para introducir en él el número de fichas que se apuestan. Si no se introduce un número válido, el parse a tipo long que se hace a continuación fallará, se mostrará usando JOptionPane un mensaje de error y se repetirá la pregunta. De este modo se consigue hacer interactiva la elección de jugada, devolviendo el comando seleccionado por el jugador. En esta implementación de IStrategy, implementada en la clase PlayerStrategy en el paquete org.poker.sample.strategies, en el que también se encuentran las implementaciones ya ofrecidas por David Pérez, también se especifica el nombre del jugador, “Player”. Otra variación importante en el sistema que ofrece David Pérez es la sustitución del método de lanzamiento del sistema por un nuevo método que recibe varios valores y ejecuta el sistema según éstos, devolviendo el resultado de la ejecución. 43 La implementación de este nuevo método run se encuentra en la clase MainController, en el paquete org.poker.main, y recibe como parámetros las dos estrategias que jugarán la partida (implementaciones de IStrategy), puesto que solamente se ha contemplado la implementación de un modo de juego Heads Up, o de dos jugadores; un entero que marca el modo de juego (uno para mostrar la interfaz gráfica y dos para no hacerlo); un entero que marca el número de iteraciones, el número de partidas a jugar; un long que marca el número de fichas de la ciega grande, y un long que marca el número de ciegas grandes con el que se juega la partida. Para facilitar la elección del modo de juego, la clase MainController incluye dos constantes públicas VISUAL_GAME y NO_VISUAL_GAME. A partir de los parámetros recibidos, el método run establece las opciones de partida incluyendo las estrategias recibidas como parámetro, haciendo visible la mesa si procede, mezclando los jugadores para que jueguen en un orden aleatorio, inicializando las puntuaciones a cero y entrando en un bucle que jugará tantas partidas como se hayan especificado, cada una de ellas con unas condiciones similares – las partidas serán de dos jugadores, si un jugador hace tres apuestas no válidas consecutivas se le expulsará de la partida, la partida tendrá un máximo de mil rondas si nadie ha perdido del todo antes, el tiempo para responder será el máximo posible para evitar que si un jugador humano duda antes de responder se tome su jugada como nula, el número de fichas iniciales de cada jugador será el establecido en la llamada a run, cada veinte rondas jugadas se aumentará la ciega y ésta empezará a la cantidad establecida en los parámetros de la llamada – y mezclando de nuevo el orden de los jugadores. Una vez establecidos así los ajustes de la partida, se inicializa el controlador de la misma y se ejecuta hasta que termine, hecho lo cual se almacenan los resultados obtenidos en las puntuaciones de los jugadores (una victoria suma uno y una derrota cero, pero si se ha terminado la partida sin que nadie pierda todas sus fichas, se sumará una proporción – es decir, si al acabar la partida un jugador tiene el doble de fichas que el otro, al primero se le suman dos tercios de punto y al segundo un tercio). Después de esto, si el modo de juego es gráfico, se hace una breve pausa para que el usuario observe el resultado del fin de la partida y se vuelve al principio del bucle. Una vez se han jugado las partidas establecidas, se devuelve una tabla hash con clave el nombre de los jugadores y valor su puntuación final. La última novedad implementada en el archivo “.jar” son los bots expertos, tanto agresivo como laid-back, entre los cuales el primero es mejor con un número menor de fichas iniciales y viceversa. 44 Plantilla de programación La plantilla de programación es la implementación de la interfaz IStrategy sobre la que se espera que los alumnos desarrollen sus propios bots. No es otra cosa que una implementación vacía de dicha interfaz profundamente comentada para que sea sencillo completarla. Aunque la explicación detallada puede observarse en la propia plantilla, es conveniente ver un breve resumen. Los métodos disponibles son el constructor, getCommand, initHand, endHand, endGame, check, onPlayerCommand y getName. De éstos métodos, los imprescindibles son el constructor, getCommand y getName. El constructor permite dar valores iniciales a los atributos que se decidan incluir en la estrategia, y es imprescindible dar valor al nombre, ya que se usará a lo largo de las partidas. Por este mismo motivo es imprescindible la existencia e implementación de un método getName. Como ya hemos comentado, el método getCommand es al que la partida llama cuando es necesario conocer la jugada que realizará el jugador en cuestión, por lo que debe ser el que use los atributos que se hayan incluido, junto con el estado de la partida, facilitado como parámetro, para decidir la jugada a realizar. El resto de métodos son métodos opcionales que permiten ir adaptando los atributos que se hayan incluido según el desarrollo de la partida o de partidas anteriores. El método initHand se ejecuta al principio de cada mano, y recibe como parámetro el estado de la partida; el método endHand se ejecuta al final recibiendo también como parámetro el estado de la partida; el método endGame se ejecuta cuando acaba cada partida y recibe como parámetro una tabla hash con los nombres de los jugadores asociados a los resultados; el método check se ejecuta cuando la mano está a punto de acabar y de decidirse los ganadores de las apuestas, y recibe como parámetro las cartas de la mesa, y el método onPlayerCommand se ejecuta cada vez que un jugador decide su jugada y recibe como parámetros el nombre del jugador y la jugada que ha decidido. Las clases plantilla a usar para la implementación de bots son las clases UserStrategy1 y UserStrategy2, en el paquete strategies. 45 Método main El método que ejecuta el sistema de bots es el método main, en la clase PokerStrategies del paquete pokerstrategies. Este método se encarga de ofrecer al estudiante distintas opciones de ejecución de la partida, y de lanzar ésta. En primer lugar, se crean dos arrays de tipo String para la elección de las estrategias que jugarán. El primer jugador puede ser un jugador humano o la estrategia UserStrategy1 creada por el estudiante, mientras que el segundo puede ser la estrategia UserStrategy2 creada por el estudiante, el bot experto agresivo, el bot experto conservador o la estrategia aleatoria diseñada por David Pérez, incluidos todos ellos en el archivo “.jar”. Después se establece el icono para los cuadros de opciones de JOptionPane que se crearán posteriormente, y se establecen los valores predeterminados de número de rondas a uno, modo de juego a VISUAL_GAME y estrategias de jugadores a null. Una vez hecha la preparación, se ofrece al estudiante, a través de elementos de JOptionPane como los ya descritos en la estrategia de jugador, la posibilidad de elegir qué estrategias jugarán la partida y el número de rondas de la misma. Si el primer jugador es un jugador humano, el modo de juego es necesariamente VISUAL_GAME; si no, se ofrece también la posibilidad de elegir el modo de juego. También se ofrece la posibilidad de establecer el número de fichas de la ciega grande y el número de fichas iniciales de cada jugador (entre cinco y veinte veces el tamaño de la ciega grande). Finalmente, una vez establecidas todas las opciones según se desee, se ejecutará con ellas el método run de la clase MainController en el archivo “.jar”, recogiendo los resultados en una tabla hash y mostrándolos mediante un cuadro de mensaje de los ofrecidos por JOptionPane. Esto permite lanzar una gran cantidad de ejecuciones del sistema para poder observar en grandes números cómo de buenas son las estrategias diseñadas. 46 Apéndices Apéndice A: código de la herramienta jPokerLearningFun El código de la herramienta presentada en el Bloque I puede encontrarse en el repositorio jPokerLearningFun (https://github.com/rincon-santi/jPokerLearningFun) Apéndice B: código del sistema de plantillas El código del sistema de plantillas presentado en el Bloque II puede encontrarse en el repositorio jPokerStrategies (https://github.com/rincon-santi/jPokerStrategies) Apéndice C: código del archivo “.jar” usado en el sistema de plantillas El código del archivo “.jar” usado en el sistema de plantillas presentado en el Bloque II puede encontrarse en el repositorio jPokerMasterJar (https://github.com/rincon-santi/jPokerMasterJar) 47 Bibliografía [1] Proyecto ‘jpoker’, por David Pérez Cabrera (13 nov. 2017) [en línea]. Available: https://github.com/dperezcabrera/jpoker [2] Clase java de cifrado, por Arturo Tena (20 dic. 2017) [en línea]. Available: https://gist.github.com/arturotena/9235042 [3] Guía Netbeans para proyectos Java (25 abr. 2018) [en línea]. Available: https://netbeans.org/features/java/index.html [4] Pokerstars School (30 abr. 2018) [en línea]. Available: https://www.pokerstarsschool.es [5] proyectosagiles.org (28 oct. 2018) [en línea]. Available: https://proyectosagiles.org [6] Programacion.net (27 abr. 2018) [en línea]. Available: https://programacion.net [7] Web de Vladimir Stankovic (10 ene. 2018) [en línea]. Available: http://www.svlada.com [8] Pagina Medium de Chanaka Lakmal (12 ene. 2018) [en línea]. Available: https://medium.com/@ldclakmal [9] Stack Overflow (3 may. 2018) [en línea]. Available: https://stackoverflow.com [10] Thomas Bakker, Analytical No-Limit Hold’em. Las Vegas, Nevada: Two Plus Two Publishing LLC, 2010. [11] Bill Chen y Jerrod Ankenman, The Mathematics Of Poker, Pittsburg: ConJeiCo LLC, 2006. [12] Collin Moshman y Douglas Zare, The Math Of Hold’em, Duluth, Georgia: Dimat Enterprises, Inc, 2011. 48