Package: pyplAI
pyplAIIntroducciónEsta biblioteca paraPythones el resultado final de un Trabajo de Fin de Grado centrado en la implementación de algoritmos deMonte Carlo Tree Search (MCTS)yminimaxpara diferentes tipos de juegos de mesa, entre ellos, según clasifica la teoría de juegos, los juegos de información perfecta, como lo son elajedrezo lasdamas, en los que en todo momento cualquier jugador puede ver toda la información del juego, como los posibles movimientos del rival, y juegos de información imperfecta, como lo son la mayoría de juegos de cartas como por ejemplo elUnoo labrisca, en el que los jugadores solo conocen información propia, como sus cartas, o información general del juego, como el estado de la mesa, pero desconocen la información del rival, como las cartas de los demás jugadores.Juegos de Información PerfectaPara este tipo de juegos se han implementado los algoritmos deMCTS con UCT[1]yminimaxcon la técnica depoda de alfa-beta[2]. Además, como ejemplos para ver el correcto uso de la biblioteca, se han creado diferentes juegos de mesa, entre ellos, elTic-Tac-Toe(3 en raya), elUltimate Tic-Tac-Toe[3]y lasdamas.Juegos de Información ImperfectaPara este segundo tipo de juegos se han implementado dos algoritmos, elSingle Observer Information Set Monte Carlo Tree Search (SO-ISMCTS)[4]y elMultiple Observer Information Set Monte Carlo Tree Search (MO-ISMCTS)[4]. El primero de estos algoritmos genera un único árbol desde el punto de vista del jugador al que le toca jugar. Por otro lado, el segundo algoritmo crea un árbol para cada uno de los jugadores y agrupa las acciones que son indiferenciables desde el punto de vista de este jugador, es decir, si un jugador puede realizar varios movimientos que no aportarían información a los rivales, agrupa estos movimientos como si fuesen uno solo, creando un único nodo en el árbol de los jugadores rivales.Un ejemplo serían los juegos en los que un jugador intercambia una de las cartas de su mano con cualquier carta de la baraja, cuando el jugador efectúa este intercambio los demás jugadores no reciben ningún tipo de información sobre el intercambio de cartas, por lo que su conocimiento sobre el estado del juego no cambia.Para estos algoritmos también se han desarrollado varios juegos como ejemplos de uso de la biblioteca, aunque ambos algoritmos se pueden usar en todos los juegos de información imperfecta, se ha diferenciado entre juegos para elSO-ISMCTSy elMO-ISMCTS. Para el primer algoritmo se ha creado el juego de laescoba, elStrategoy elblackJack, y para el segundo se ha desarrollado elholjjak(juego de adivinar las canicas del rival) y elphantom(variante del 4 en raya en el que no se ven los movimientos del rival).Manual de UsoEl primer paso necesario para poder usar esta biblioteca es descargarla. Se puede descargar la biblioteca desde la consola usando el comando:pipinstallpyplAITras esto, ya se puede importar en el archivo dePythondel juego al que queramos implementarla.importpyplAIClase sobre el Estado del JuegoPara poder usar esta biblioteca es imprescindible que haya una clase que guarde la información necesaria sobre el estado del juego. Aunque se puede dar a esta clase el nombre que se desee, en este manual se utilizará una clase llamadaJuegoa modo de ejemplo.classJuego:...Atributo ObligatoriojugadorActualDebido a que los algoritmos necesitan saber cuál es el jugador al cual le toca jugar en cada uno de los turnos, es obligatorio añadir un atributojugadorActualen la clase del juego. Además, es muy importante que este atributo tenga este mismo nombre.classJuego:def__init__(self):self.jugadorActual=1...Este atributo debe contener un valor numérico que representa el índice del jugador al que le toca jugar (empezando por 1). También, debe ir actualizándose conforme se avanza en el juego, por ejemplo, en el3 en raya, este atributo se debe iniciar con el valor 1, pero cuando el primer jugador realice su movimiento, se actualiza al valor 2, indicando que el segundo jugador es el que debe realizar su jugada.Además del atributo para identificar el jugador actual del estado del juego, esta clase deberá implementar ciertos métodos para poder interactuar y obtener información sobre el estado del juego. Algunos métodos son comunes para todos los algoritmos, mientras que otros algoritmos requieren de métodos específicos. Cabe destacar que estos métodos pueden ser nombrados de forma distinta a como se mostrará a continuación, pero es totalmente necesario que reciban los parámetros de entrada y devuelvan la salida tal y como se especifica en este manual.Métodos GeneralesLos siguientes 4 métodos serán obligatorios para todos los algoritmos que se implementan en esta biblioteca:•	obtiene_movimientos(self):Devolverá una lista con todos los movimientos posibles del jugador actual del estado del juego, es recomendable devolver estos movimientos en un orden aleatorio.classJuego:...defobtiene_movimientos(self):...returnmovimientos•	aplica_movimiento(self, movimiento):Aplicará el movimiento dado al estado del juego y devolverá el estado resultante (self).classJuego:...defaplica_movimiento(self,movimiento):...returnself•	es_estado_final(self):Comprueba el estado actual del juego, devolviendoTrueoFalsedependiendo de si es un estado final o no.classJuego:...defes_estado_final(self):...returnTrueoreturnFalse•	gana_jugador(self, jugador):Comprueba si el jugador dado gana el juego en el estado actual de este, devolviendoTrueen caso de que sea ganador, oFalseen caso contrario. La variablejugadordebe ser un número entero positivo, y se debe situar en el rango de 1, para el primer jugador del juego, hastan, siendonel número total de jugadores.classJuego:...defgana_jugador(self,jugador):...returnTrueoreturnFalseMétodos SO/MO-ISMCTSAdemás de los 4 métodos anteriores, los algoritmos deSO-ISMCTSyMO-ISMCTSnecesitan un método adicional para su uso, el cuál se explica a continuación:•	determinación(self):Devuelve una determinación aleatoria del estado del juego, desde el punto de vista del jugador actual.classJuego:...defdeterminacion(self):...returndeterminacionPor ejemplo, para un juego de cartas en el que cada jugador tiene una mano, hay una mesa con cartas visibles, una pila de descartes y un mazo de robo inicial, el jugador actual no tiene conocimiento sobre las cartas del rival ni las cartas del mazo de robo, pero si sabe cuáles son sus cartas, las de la mesa y las que han sido mandadas a la pila de descartes, por lo que tiene la información sobre cuáles son las cartas restantes, pero aún no sabe donde se encuentran.Con esta información, este método debe dar un estado del juego en el que aleatoriamente se distribuyan las cartas que el jugador no tiene localizadas entre las zonas en las que no sabe que cartas hay, en este caso, las zonas serían las manos de los rivales y el mazo de robo.Métodos MO-ISMCTSAdemás, para el funcionamiento del algoritmoMO-ISMCTSse necesitará otro método adicional:•	es_movimiento_visible(movimiento):Este método, dado un movimiento, devuelveTruesi es un movimiento visible para los rivales, oFalseen caso contrario.classJuego:...defes_movimiento_visible(movimiento):...returnTrueoreturnFalsePor ejemplo, un movimiento visible para los rivales sería jugar una carta de tu mano sobre la mesa, por lo que, haría saber a los rivales el lugar exacto de esa carta, cosa que antes no sabían. Sin embargo, un movimiento no visible sería intercambiar una carta de tu mano con el mazo de robo sin revelar ninguna de las dos. Esta acción no mostraría nueva información a ningún rival, ya que no se muestran las cartas y los rivales seguirán sin saber que cartas hay en tu mano ni que cartas hay en el mazo.Métodos MinimaxEn el caso del algoritmo deminimax, además de los 4 métodos generales, se necesita un método que devuelva una heurística sobre el estado del juego, es decir, que evalúe su estado para saber como de bueno es desde el punto de vista de un jugador dado:•	heuristica(self, jugador):Este método debe devolver un número entero que refleje una evaluación sobre como de bueno es el estado del juego para un jugador dado. Esta evaluación debe ser mayor cuanto mejor sea el estado del juego para este jugador.classJuego:...defheuristica(self,jugador):...returnevaluacionPor ejemplo, para el juego de lasdamasuna posible heurística sería contar el número de fichas del jugador y restarle el número de fichas del rival. Hay multitud de posibles heurísticas para cada uno de los juegos, y se debe tener un conocimiento sobre el juego para poder crear una buena heurística, ya que la heurística tendrá un gran peso a la hora de decidir cuál es el mejor movimiento.Al igual que en el métodogana_jugador, el argumentojugadordebe ser un número entero positivo, desde el 1, para el primer jugador, hastan, para el último jugador, siendonel número total de jugadores.Constructor AlgoritmoUna vez tengamos todos estos métodos solamente debemos llamar a la biblioteca y al constructor del algoritmo que se desee utilizar. A esta llamada le pasaremos como argumentos los métodos necesarios para el uso de este algoritmo junto con algunas otras variables que detallaremos a continuación:•	numeroJugadores:Este argumento es obligatorio para todos los algoritmos,  representará el número de jugadores que contiene el juego, siendo este un número entero mayor que cero.•	tiempoEjecucion:Este argumento es común para todos los algoritmos de Monte Carlo, ya que, estos algoritmos necesitan un tiempo límite de ejecución antes de devolver el mejor movimiento encontrado. Este argumento debe ser un número real mayor que cero y representa el tiempo de ejecución en segundos.•	profundidadBusqueda:Este argumento solo es necesario en la llamada al constructor del algoritmo deminimax, y sirve para limitar la profundidad en el árbol de búsqueda. Este argumento debe ser un número entero mayor que cero.Sabiendo todo esto ya podemos ver como se deben hacer las llamadas a la biblioteca y los constructores para cada uno de los tipos de algoritmos. Es muy importante que se siga el orden mostrado en los argumentos de entrada. Además, recordar queJuegoes la clase de ejemplo que contiene la información sobre el estado del juego, como el atributojugadorActual, y los métodos explicados anteriormente.•	MCTS:mcts=pyplAI.MCTS(Juego.aplica_movimiento,Juego.obtiene_movimientos,Juego.es_estado_final,Juego.gana_jugador,numeroJugadores,tiempoEjecucion)•	Minimax:minimax=pyplAI.Minimax(Juego.aplica_movimiento,Juego.obtiene_movimientos,Juego.es_estado_final,Juego.gana_jugador,Juego.heuristica,numeroJugadores,profundidadBusqueda)•	SO-ISMCTS:so_ismcts=pyplAI.SOISMCTS(Juego.aplica_movimiento,Juego.obtiene_movimientos,Juego.es_estado_final,Juego.gana_jugador,Juego.determinacion,numeroJugadores,tiempoEjecucion)•	MO-ISMCTS:mo_ismcts=pyplAI.MOISMCTS(Juego.aplica_movimiento,Juego.obtiene_movimientos,Juego.es_estado_final,Juego.gana_jugador,Juego.determinacion,Juego.es_movimiento_visible,numeroJugadores,tiempoEjecucion)•	Modoverbose:Si queremos ver algunos detalles cuando ejecutemos el algoritmo, se debe añadir unTruecomo último argumento en la llamada al constructor. Este modo mostrará información útil del algoritmo por consola, como por ejemplo, tiempo de ejecución, número de nodos creados y visitados y demás características propias de cada uno de los algoritmos. Un ejemplo de como quedaría la creación del algoritmo deMCTScon este argumento extra sería el siguiente:mcts=pyplAI.MCTS(Juego.aplica_movimiento,Juego.obtiene_movimientos,Juego.es_estado_final,Juego.gana_jugador,numeroJugadores,tiempoEjecucion,True)Ejecución AlgoritmoUna vez tengamos el objeto del algoritmo que se quiere utilizar solamente se debe llamar a su métodoejecutay pasarle como argumento el objeto que contiene el estado actual del juego, esto devolverá el mejor movimiento encontrado durante el tiempo de computación dado, para los algoritmos deMonte Carlo, o la profundidad de búsqueda, en el caso del algoritmominimax. A continuación, se mostrará el código de ejemplo en el que se usa el algoritmo deMCTSpara obtener un movimiento y seguidamente aplicarlo al juego, obteniendo así un nuevo estado del juego:defmain():juego=Juego()movimiento=mcts.ejecuta(juego)juego=aplica_movimiento(movimiento)En caso de que el estado del juego no tenga ningún movimiento posible para aplicar los algoritmos devolveránNone, y si solo hay un posible movimiento para aplicar, para ahorrar cálculos innecesarios, se devolverá el único movimiento posible.Si se tienen dudas sobre como integrar la biblioteca a juegos propios se recomienda ver los juegos del repositorio deGitHub(pyplAI) como ejemplos de uso. Este repositorio contiene la biblioteca y todos los juegos nombrados en la introducción de este manual, incluyendo las implementaciones de los algoritmos correspondientes con cada uno de ellos.ContactoEn caso de tener alguna duda, idea o aportación sobre la biblioteca por favor contactar al siguiente correo:pepoluis712@gmail.comReferencias[1] Cameron B. Browne, Edward Powley, Daniel Whitehouse, Simon M. Lucas, Peter I.Cowling, Philipp Rohlfshagen, Stephen Tavener, Diego Perez, Spyridon Samoth-rakis, and Simon Colton. A survey of monte carlo tree search methods. IEEETransactions on Computational Intelligence and AI in Games, 4(1):1–43, 2012. doi:10.1109/TCIAIG.2012.2186810. URLhttps://ieeexplore.ieee.org/document/6145622.[2] Patricio Mendoza. Alpha-beta pruning algorithm: The intelligence behind strategygames. page 9, 2022. URLhttps://www.researchgate.net/publication/360872512.[3] Eytan Lifshitz and David Tsurel. Ai approaches to ultimate tic-tac-toe. page 5, 2013. URLhttps://www.cs.huji.ac.il/w~ai/projects/2013/UlitmateTic-Tac-Toe/files/report.pdf.[4] Peter I. Cowling, Edward J. Powley, and Daniel Whitehouse. Information setmonte carlo tree search. IEEE Transactions on Computational Intelligence and AIin Games, 4(2):120–143, 2012. doi: 10.1109/TCIAIG.2012.2200894. URLhttps://ieeexplore.ieee.org/document/6203567.