MEMORIA DEL PROYECTO

UNIVERSIDAD DE SALAMANCA FACULTAD DE CIENCIAS DEPARTAMENTO DE INFORMÁTICA Y AUTOMÁTICA INGENIERÍA TÉCNICA EN INFORMÁTICA DE SISTEMAS UNA APLICACIÓN P

2 downloads 120 Views 2MB Size

Recommend Stories


MEMORIA DEL PROYECTO
MEMORIA DEL PROYECTO ESTUDIO DESCRIPTIVO DE LAS INFECCIONES PRECOCES POR EL VIRUS VARICELA-ZOSTER: VARICELA EN LACTANTES (MENORES DE 13 MESES) Y HERPE

MEMORIA TECNICA DEL PROYECTO DOMNES
SRO/PROYECTO/DOMNES/2012 MEMORIA TECNICA DEL PROYECTO DOMNES PROYECTO DE PROSPECCION NACIONAL DE PROCEDIMIENTOS DE DIAGNÓSTICO EN MEDICINA NUCLEAR UT

Story Transcript

UNIVERSIDAD DE SALAMANCA FACULTAD DE CIENCIAS DEPARTAMENTO DE INFORMÁTICA Y AUTOMÁTICA INGENIERÍA TÉCNICA EN INFORMÁTICA DE SISTEMAS

UNA APLICACIÓN PARA JUGAR AL HEX

MEMORIA DEL PROYECTO

Salamanca, junio de 2007

Tabla de contenidos 1

Introducción .............................................................................................................9 1.1

Historia ..............................................................................................................9

1.1.1 1.2 2

Desarrollo actual .............................................................................................10

Objetivos del proyecto ...........................................................................................11 2.1

3

Shannon y Moore......................................................................................10

Objetivos generales.........................................................................................11

Conceptos teóricos ................................................................................................13 3.1

La teoría de juegos..........................................................................................13

3.2

Cómo jugar al hex ...........................................................................................14

3.2.1

La regla de intercambio.............................................................................14

3.2.2

Reconocer estructuras y plantillas ............................................................15

3.2.3

Algunos principios generales ....................................................................17

3.3

Teoría del hex .................................................................................................18

3.3.1 3.4

Estrategia ganadora..................................................................................19

Complejidad ....................................................................................................20

3.4.1

Resolviendo el hex por fuerza bruta..........................................................20

3.4.2

Clasificación..............................................................................................21

3.4.3

Clases P y NP...........................................................................................22

3.4.4

El hex es ESPACIOP-completo ................................................................23

3.5

Una aproximación formal.................................................................................23

3.5.1

La jerarquía como punto de partida...........................................................24

3.5.2

Conexiones y semiconexiones virtuales....................................................24

3.6

Descripción de la heurística.............................................................................25

3.6.1

¿Qué es una heurística?...........................................................................26

3.6.2

Árboles de jugadas ...................................................................................26

3.6.3

Poda alfa–beta..........................................................................................27

3.6.4

Una primera idea ......................................................................................28

3.6.5

Reglas de deducción ................................................................................28

3.6.6

Descubrir conexiones con H-Search .........................................................29

3.6.7

Función de evaluación y circuitos eléctricos..............................................30

3.7

Encontrando la mejor jugada...........................................................................31

3.7.1

Calcular el valor de una jugada.................................................................32

4

3.7.2

Pseudocódigo ........................................................................................... 34

3.7.3

Conclusiones ............................................................................................ 34

Técnicas y herramientas........................................................................................ 37 4.1

Orientación a objetos ...................................................................................... 37

4.2

Prototipado...................................................................................................... 37

4.3

Algunas decisiones ......................................................................................... 37

4.3.1

Lenguaje de programación ....................................................................... 37

4.3.2

Biblioteca para matemáticas ..................................................................... 38

4.3.3

¿Applets o servlets? ................................................................................. 38

4.4

5

Herramientas de desarrollo ............................................................................. 38

4.4.1

Entorno de desarrollo ............................................................................... 38

4.4.2

Planificación ............................................................................................. 38

4.4.3

Elicitación de requisitos ............................................................................ 39

4.4.4

Herramientas CASE ................................................................................. 39

4.4.5

Diseño gráfico........................................................................................... 39

Aspectos relevantes .............................................................................................. 41 5.1

Metodología .................................................................................................... 41

5.1.1

Reutilización ............................................................................................. 41

5.1.2

Prototipado ............................................................................................... 41

5.1.3

Validación ................................................................................................. 42

5.1.4

Una adaptación del Proceso Unificado ..................................................... 42

5.2

Modularidad .................................................................................................... 42

5.3

Procesamiento paralelo................................................................................... 43

5.3.1

Estudio práctico ........................................................................................ 44

5.3.2

Conclusiones ............................................................................................ 47

5.3.3

Otras posibilidades evaluadas .................................................................. 48

5.4

Multiplataforma................................................................................................ 49

5.5

Eficiencia de las estructuras............................................................................ 50

5.5.1

Mapas hash .............................................................................................. 50

5.5.2

Listas y vectores ....................................................................................... 51

5.6

Simulación de jugadas .................................................................................... 51

5.7

Simplificación de celdas .................................................................................. 52

5.8

Otros aspectos interesantes............................................................................ 53

5.8.1

Diferentes modos de juego ....................................................................... 53

5.8.2

Sugerir jugadas ........................................................................................ 53

5.8.3

Regla de intercambio ................................................................................ 53

5.8.4

Tablas de aperturas.................................................................................. 54

5.8.5

Nivel de juego configurable....................................................................... 54

5.8.6

Interfaz gráfica de usuario ........................................................................ 54

5.9

Dificultades ..................................................................................................... 54

6

Conclusiones .........................................................................................................55 6.1

7

Reconocimientos ...................................................................................................57 7.1

8

Líneas de trabajo futuras .................................................................................55 Beta testers .....................................................................................................57

Referencias ...........................................................................................................59 8.1

Otra bibliografía...............................................................................................59

8.2

Recursos en Internet .......................................................................................60

Tabla de figuras Figura 1. Un tablero típico de hex ........................................................................................9 Figura 2. Turno para las blancas........................................................................................14 Figura 3. Turno para las negras .........................................................................................15 Figura 4. Una conexión implícita ........................................................................................15 Figura 5. Una conexión explícita ........................................................................................15 Figura 6. Una plantilla de borde .........................................................................................16 Figura 7. Y otra plantilla de borde ......................................................................................16 Figura 8. Plantilla para distancia 5 en tableros grandes .....................................................16 Figura 9. Un muro. (Copyright © 2005 Thomas Maarup)....................................................17 Figura 10. Cortar el paso. (Copyright © 2005 Thomas Maarup) .........................................17 Figura 11. Tiempo frente a dimensión del tablero ..............................................................21 Figura 12. Diferentes conexiones y semiconexiones..........................................................25 Figura 13. Un segmento del árbol de jugadas para el tres en raya.....................................27 Figura 14. Esquema de la regla AND .................................................................................28 Figura 15. Esquema de la regla OR ...................................................................................29 Figura 16. Una deducción entre A y C ...............................................................................29 Figura 17. ¿Circuito o tablero? ...........................................................................................30 Figura 18. Repartiendo casillas entre procesadores para un tablero de 2x2 ......................43 Figura 19. Hextress mostrando resultados .........................................................................44 Figura 20. Gráfico de tiempos para sistemas de un único procesador ...............................46 Figura 21. Tabla de tiempos para sistemas de dos procesadores......................................46 Figura 22. Tabla de tiempos para sistemas de cuatro procesadores..................................47 Figura 23. Escala logarítimica para sistemas duales..........................................................48 Figura 24. Hexodus sobre Mac OS X .................................................................................49 Figura 25. Hexodus sobre Linux con GNOME (Ubuntu) .....................................................49 Figura 26. Hexodus sobre Linux con KDE (Kubuntu) .........................................................50 Figura 27. Hexodus sobre Windows 2000..........................................................................50 Figura 28. Un esquema algo complicado ...........................................................................51 Figura 29. Una posición en el tablero .................................................................................52 Figura 30. El equivalente en grafo......................................................................................52 Figura 31. Simplificando casillas ........................................................................................52 Figura 32. Eliminando tres casillas.....................................................................................53

Introducción

9

1 Introducción El hex es un juego abstracto y de suma cero que se desarrolla entre dos contrincantes sobre un tablero en forma de rombo, compuesto por celdas hexagonales. Las fichas suelen ser de dos colores diferentes, asociado uno a cada oponente, tal como se muestra en la figura 1:

Figura 1. Un tablero típico de hex

Gana el jugador que primero logre formar una cadena de fichas de su color que conecte los dos laterales opuestos del tablero que le hayan sido asignados previamente, y que normalmente están marcado en su mismo color. Inicialmente el tablero se encuentra vacío. A cada jugador se le asigna un color y dos laterales opuestos del tablero que debe intentar conectar con sus fichas. A partir de aquí, y por turnos, los jugadores van colocando sus fichas sobre el tablero hasta que uno de ellos conecte sus dos bordes. El juego es de suma cero y este hecho y la geometría del tablero hacen que no sea posible empatar una partida. Aunque estas reglas son muy sencillas es extremadamente difícil jugar bien al hex, y de hecho su complejidad táctica a niveles avanzados se equipara a la del ajedrez. Jugar al hex es muy sencillo. Jugar bien al hex es muy complicado, y generalmente es necesaria ayuda para profundizar en la táctica del juego y mucho tiempo de estudio. Esta aparente sencillez y su complejidad endiablada ha entusiasmado y obsesionado a algunas de las mentes más brillantes de nuestro tiempo.

1.1 Historia El hex fue inventado en 1942 por el matemático danés Piet Hein y de forma independiente por John Nash a finales de 1949. Piet Hein inventó por casualidad el juego como parte de sus estudios, difundiéndolo en ciertos ambientes académicos. Finalmente, el 26 de diciembre de 1942 el juego fue descrito para el público en general en el periódico danés Politiken, bajo el nombre de Polygon. Por su lado, John Nash lo inventó por su cuenta mientras estudiaba en la Universidad de Princeton, como un ejemplo de juego en el que se sabe que existe una estrategia ganadora aunque ésta se desconozca. El juego se popularizó rápidamente entre los estudiantes.

10

Una aplicación para jugar al hex

En 1953 la compañía americana Parker Brothers descubrió y dio cierta publicidad al juego, comercializándolo bajo el nombre de Hex, en referencia a las celdillas hexagonales que componen el tablero.

1.1.1 Shannon y Moore Shannon y Moore se encargaron de construir la primera máquina capaz de jugar al hex siguiendo un método sistemático. Concretamente Shannon fue un pionero al identificar una posición cualquiera del juego con una distribución de cargas eléctricas en dos dimensiones, con lo que logró que la máquina tomara decisiones sobre el juego basándose en las propiedades del correspondiente campo eléctrico, aplicando lo que es sin duda una idea de gran belleza. A día de hoy, los modernos programas de hex se siguen basando en ella.

1.2 Desarrollo actual Actualmente el hex es un juego de popularidad creciente, sobre todo a lo largo del continente americano, donde goza de una salud envidiable. Poco a poco, y sobre todo gracias a Internet, comienza a popularizarse en Europa. La relación del hex con la informática ha sido tradicionalmente difícil, especialmente por su elevada complejidad. Por ello, no es de extrañar que existan sólo unos pocos programas que nos permiten jugar al hex contra un ordenador. Actualmente, existen dos grandes juegos de hex para ordenadores personales muy potentes e interesantes: Six y Hexy. Hexy es un juego desarrollado por Vadim V. Anshelevich para la plataforma Windows. Su autor es la principal figura en la difusión de métodos para computación del hex [1], y a sus trabajos debemos todo cuanto sabemos sobre este tema. Sin embargo, esta aplicación es de código cerrado y de pago, y restringida a su plataforma. Por otro lado, Six es un programa verdaderamente fuerte que funciona sobre KDE en Linux. Esta aplicación es software libre y gratuito. Sin embargo, su documentación es muy escasa –por no decir inexistente–. Por supuesto, no funciona en Windows ni está previsto que llegue a hacerlo. El resultado global es que existe una documentación muy interesante, aunque no del todo completa, del autor del Hexy, y no existen apenas otras fuentes de documentación desde la perspectiva del desarrollo de software. La única alternativa libre, Six, carece de una documentación que pueda ser útil para futuros desarrollos. En cuanto a los usuarios de Mac OS, quedan totalmente marginados en el panorama actual al no existir ningún programa de hex, por limitado que sea, dirigido a este sistema. En consecuencia, está claro que existen notables carencias en el panorama computacional del hex, carencias que este proyecto quiere contribuir a solucionar. Por un lado, es el primer esfuerzo por desarrollar una versión multiplataforma y de código abierto de un juego de hex, con el objetivo primordial de servir como base a un futuro trabajo en el ámbito del software libre. Por otro lado, es preciso poner a disposición de la comunidad cierta cantidad de documentación sobre la computación del hex, aportando nuevos puntos de vista. Respecto esto último, si bien es cierto que se dispone de cierta cantidad de documentación en inglés, apenas se ha escrito nada sobre el hex en español a un nivel avanzado. Tampoco existen programas en este idioma, y se entiende que esta situación, a nivel global, no favorece la expansión de este juego en nuestro país en particular ni en el mundo hispanohablante en general. Este proyecto también quiere colaborar con esta expansión.

Objetivos del proyecto

11

2 Objetivos del proyecto De acuerdo con lo anterior, se pretende desarrollar una aplicación capaz de jugar al hex, de código abierto, multiplataforma y acompañada de una buena cantidad de información detallada y de libre distribución. También es una ocasión excepcional para estudiar los procedimientos actuales implantados en el hex y ver cómo se pueden mejorar o qué cambios se pueden sugerir. En un sentido más amplio, este proyecto se concibe como una base sobre la que la comunidad pueda colaborar, democratizando el conocimiento sobre este juego.

2.1 Objetivos generales Así pues, con la realización de este proyecto se pretende: 1. Desarrollar un programa que juegue al hex basado en el estudio de determinados algoritmos de búsqueda. 2. Desarrollar el primer juego de hex libre multiplataforma, de tal manera que pueda llegar a todos los usuarios y favorezca la participación de la comunidad. 3. Crear una base de conocimiento libre y sin restricciones en torno a la computación del hex y que facilite el estudio de este juego, sirviendo de complemento a futuros trabajos. Al mismo tiempo, se pretende servir como base a la popularización del hex y formar la primera base documental sólida en español sobre la computación del hex. 4. Investigar y probar la viabilidad y rendimiento de otros sistemas, en concreto de los sistemas de varios hilos y cómo integrarlos en los procedimientos de búsqueda estándares. Estos objetivos generales se concretarán a lo largo de las fases de requisitos y de análisis.

Conceptos teóricos

13

3 Conceptos teóricos En esta sección se van a repasar algunos conceptos útiles para quien se acerque al hex por primera vez, y que ayudarán a comprender ciertas sutilezas tácticas y una parte de la lógica subyacente del juego. La primera reacción, y la más comprensible antes de jugar por primera vez, es la carencia total de expectativas en que un juego tan simple pueda resultar medianamente entretenido o emocionante. Sin embargo, esta percepción se transformará en asombro cuando se comience a vislumbrar la dificultad real del juego. Es muy complicado establecer unas líneas generales o una estrategia básica, pues corremos el riesgo de que esta estrategia resulte demasiado obvia o generalista. Y justamente esta dificultad en la descripción precisa de las técnicas del juego tiene mucha influencia en lo complicado que resulta diseñar programas que jueguen al hex.

3.1 La teoría de juegos La teoría de juegos es un área de las matemáticas que utiliza modelos para estudiar interacciones en estructuras formalizadas de incentivos (juegos). Sus investigadores estudian las estrategias óptimas así como el comportamiento previsto y observado de individuos en juegos. Desarrollada en sus comienzos como una herramienta para entender el comportamiento de la economía, la teoría de juegos se usa actualmente en muchos campos, desde la biología a la filosofía. Experimentó un crecimiento muy notable y se formalizó por primera vez a partir de los trabajos de John von Neumann y Oskar Morgenstern [7], antes y durante la Guerra Fría, debido sobre todo a su aplicación a la estrategia militar. La teoría de juegos ha encontrado aplicaciones en ciencias políticas, ética y filosofía; y en los últimos años, ha atraído también la atención de los investigadores en informática, usándose principalmente en el campo de la inteligencia artificial. Aunque tiene algunos puntos en común con la teoría de la decisión, la teoría de juegos estudia decisiones realizadas en entornos de interacción. En otras palabras, estudia la elección de la conducta óptima cuando los costes y los beneficios de cada opción no están fijados de antemano, sino que dependen de las elecciones de otros individuos. Desde el punto de vista de la Inteligencia Artificial, los juegos resultan atractivos por su naturaleza abstracta y por ser sencillos de comprender y representar. Además, sus agentes suelen estar restringidos a un número muy limitado de acciones definidas por reglas precisas y normalmente sin ambigüedad. Lograr que los ordenadores pudieran practicar diferentes juegos fue una de las primeras tareas que se emprendieron en el campo de la IA, y de hecho ya en 1950, coincidiendo con el desarrollo de los primeros computadores programables, la computación del ajedrez fue abordada por Konrad Zuse, Alan Turing o Claude Shannon. Los juegos son precisamente interesantes porque, a diferencia de otros problemas, son demasiado difíciles para resolverlos, o lo suficiente para que evitemos la tentación de resolverlos por fuerza bruta. Así, el ajedrez tiene un factor de ramificación calculado

14

Una aplicación para jugar al hex

entre 35 y 38, como promedio, y el del hex se calcula en torno a 100, por tanto los juegos requieren de los sistemas la capacidad de tomar una decisión razonablemente buena en un tiempo razonablemente reducido, con independencia de que sea la decisión óptima.

3.2 Cómo jugar al hex No es el objetivo de esta memoria ofrecer un manual estratégico completo sobre el juego, y en Internet pueden encontrarse algunas referencias sobre el tema. Sin embargo, sí se considera necesario hacer algunas valoraciones tácticas y ofrecer algún ejemplo para completar la percepción del juego. Al jugar al hex, tanto las personas como los ordenadores deben aplicar dos enfoques que han de estar balanceados si se quiere tener éxito. La idea general de la estrategia consiste en bloquear el avance de la conexión del rival mientras reforzamos la nuestra. Las propias características del juego son muy relevantes aquí: puesto que uno de los contrincantes tiene que resultar vencedor, nuestra estrategia puede consistir tanto en intentar crear una conexión propia como en evitar las de nuestro rival. Si conseguimos anular las posibilidades del rival de conectar sus bordes, habremos ganado la partida; de modo que se trata de encontrar la jugada que más beneficie a nuestros intereses al tiempo que perjudique los de nuestro adversario.

3.2.1 La regla de intercambio La regla de intercambio es la única situación excepcional que puede tener lugar a lo largo de una partida de hex. El jugador que comienza la partida tiene una gran ventaja en este juego. En particular, en tableros pequeños es muy difícil evitar que el primero en jugar gane la partida si éste realiza determinados movimientos (usualmente, ocupar la casilla central del tablero). Con el objetivo de equilibrar las fuerzas, existe lo que llamamos “regla de intercambio” (swap rule en inglés), que permite al segundo jugador decidir si se apropia del movimiento realizado por el primer jugador. Por supuesto, si este jugador lo desea puede realizar otro movimiento sin intercambiar los colores. Esta regla sólo se ejecuta en la primera jugada, y en la práctica supone que el jugador que comienza debe medir la jugada que realiza: si es muy mala le quitará ventaja, y si es definitivamente buena, el contrario la intercambiará y esa ventaja pasará al oponente. De modo que es necesario realizar un primer movimiento razonablemente bueno pero poco tentador para el oponente. Esto se suele conseguir ocupando casillas cercanas a nuestros bordes, que resultan poco interesantes para el oponente. Generalmente esta regla tiene un carácter opcional, y los jugadores suelen acordar al principio de la partida si la utilizarán o no. Los juegos de hex para ordenador también permiten activar o desactivar esta regla. Como ejemplo, dado el siguiente tablero (3x3), las negras han colocado su ficha y es turno para las blancas:

Figura 2. Turno para las blancas

Las blancas pueden ejecutar cualquier movimiento y en todos los casos perderán la partida, pues no pueden evitar que en las jugadas decisivas, el jugador negro conecte sus

Conceptos teóricos

15

bordes. Sin embargo, pueden intercambiar la jugada –por ser la primera–, y entonces el juego quedaría así:

Figura 3. Turno para las negras

Las blancas ocupan ahora la casilla elegida por las negras, que se hayan ahora en posesión del turno y que han perdido la partida por ejecutar un primer movimiento demasiado ambicioso.

3.2.2 Reconocer estructuras y plantillas Existen en el juego determinadas estructuras cuyo análisis puede resultar muy interesante, principalmente porque aparecen repetidas exactamente igual en muchas ocasiones. Denominamos a estas estructuras plantillas, y consisten en una posición específica e instrucciones concretas y sin ambigüedad para responder a los intentos del contrincante de anularlas. Las plantillas nos ayudan a establecer "conexiones implícitas" entre dos grupos de celdas. La idea es que una conexión implícita es tan sólida (en ciertas condiciones) como una firmemente establecida (como la que tienen dos casillas vecinas entre sí). Sabiendo esto podemos evitar muchas jugadas inútiles y jugar a un nivel de abstracción alto. A primera vista estas ideas pueden resultar un poco complejas, de modo que es mejor repasar algunos ejemplos.

Puentes Obviamente, la conexión firme entre dos celdas vecinas es la más fuerte. Pero podemos plantear una conexión que perdure con independencia del movimiento del rival. Esta es la versión más sencilla de una conexión virtual o implícita y recibe el nombre de “puente”:

Figura 4. Una conexión implícita

El oponente no puede evitar que el negro conecte sus dos celdas; si el blanco se interpone en una de las casillas de la intersección, el negro se colocará en la otra y la conexión quedará establecida. En ambos caso se llega a un equivalente de:

Figura 5. Una conexión explícita

Esto es consecuencia directa de la geometría hexagonal de las casillas y provoca verdaderos dolores de cabeza a los jugadores principiantes. De modo que siguiendo este

16

Una aplicación para jugar al hex

razonamiento, podemos distinguir en el hex dos tipos de conexión: las implícitas (o débiles) y las explícitas (o fuertes).

Plantillas de borde Cuando un jugador se aproxima a un borde y quiere conectar su cadena al mismo, conocer un pequeño número de reglas le facilitará enormemente este trabajo. Estas plantillas consisten en posiciones concretas y conjuntos de instrucciones para responder a una eventual intrusión del oponente en la conexión con el borde. La figura representa una celda ocupada junto a un borde (extremo inferior). El jugador blanco no puede impedir la conexión.

Figura 6. Una plantilla de borde

Otro ejemplo: el blanco no puede obstruir los dos puntos simultáneamente, por lo que no puede evitar la conexión:

Figura 7. Y otra plantilla de borde

La figura anterior muestra dos ejemplos de plantilla de borde: si el oponente se coloca en cualquier celda tratando de obstruir la conexión, el jugador blanco puede colocar su ficha en uno de los puntos marcados para mantener la conexión implícita. Las plantillas de bordes se agrupan en función de la distancia que queda hasta llegar al borde, y la propia definición de cada plantilla nos marca las celdas que necesitamos que estén libres para asegurar la posición, lo que nos permite conocer cuándo podemos utilizar esta plantilla. La siguiente ilustración nos muestra la plantilla para distancia 5:

Figura 8. Plantilla para distancia 5 en tableros grandes

Existen multitud de plantillas para muchas situaciones distintas del juego, aunque las más útiles e importantes son las plantillas de borde. En todo caso, dominarlas no siempre es necesario si se aplica un razonamiento sólido a la posición y no se olvidan ciertos principios fundamentales.

Conceptos teóricos

17

Murallas Las murallas o muros aparecen cuando un jugador es forzado por el oponente a formar una fila paralela a uno de sus bordes del tablero. El oponente consigue así, en principio, evitar la conexión. El muro supone, al mismo tiempo, una conexión válida en el sentido del jugador obstructor, por lo que si el jugador bloqueado no rompe de alguna manera el muro perderá la partida.

Figura 9. Un muro. (Copyright © 2005 Thomas Maarup)

En la figura anterior, el jugador negro impide que el blanco termine una conexión con este borde del tablero, forzándole a continuar el muro si quiere tener posibilidades de completar esta conexión. A su vez, el negro está componiendo una conexión, por lo que el blanco deberá partir el muro de alguna forma. Existen plantillas para “romper” o “escapar” de los muros, aunque no es necesario utilizarlas.

Bloqueos La naturaleza hexagonal del tablero hace que obstruir una conexión sea una tarea verdaderamente complicada. Si intentamos detener el avance de un jugador situando nuestra ficha una casilla más lejos, estamos construyendo un bloqueo que puede esquivarse utilizando las casillas adyacentes. La defensa clásica contra el puente es intentar bloquear la conexión tres celdas más lejos, como muestran los movimientos etiquetados con un 2 en la siguiente ilustración:

Figura 10. Cortar el paso. (Copyright © 2005 Thomas Maarup)

3.2.3 Algunos principios generales Existen algunos principios generales muy útiles en este juego, que no obstante, deben ser tomados como ayudas y no como normas estrictas: debemos apoyarnos siempre el sentido común y el cálculo.

Ataque = defensa En el hex, un buen ataque es tan potente como una buena defensa. Precisamente por ser un juego de suma cero, nuestra mejor jugada ofensiva es a la vez nuestra mejor defensiva. Muchas veces, en el transcurso de la partida, no se aprecia con claridad cuál es la mejor jugada ofensiva para un jugador, pero sí se puede pensar en cuál perjudica más al

18

Una aplicación para jugar al hex

adversario. Existe una equivalencia muy interesante en este sentido: un bloqueo perfecto es, a su vez, una conexión.

Iniciativa Se dice que el jugador que lleva el control de la dirección del juego tiene la iniciativa. Aunque la iniciativa oscila entre ambos jugadores, en el hex estar en posesión de la misma supone una ventaja muy destacable. Este concepto está relacionado con el de “tempo” o “tiempo” del ajedrez, que se refiere a la optimización de jugadas: no se debe hacer con dos movimientos lo que se puede hacer en uno, ya que dejamos de aprovechar una jugada y esto representa una ventaja muy clara para el oponente. En ajedrez, mantener la iniciativa es una gran ayuda táctica, pero en el hex es absolutamente imprescindible si se quiere ganar la partida.

Ramificación del movimiento Una buena jugada en hex ha de permitir desarrollar alternativas, para hacer frente a los intentos de bloqueo del rival: si un movimiento consta de varios “hilos” el contrincante no podrá detenerlos todos. Un puente entre dos celdas es un buen ejemplo de esto.

Jugar en todo el tablero En el hex el largo plazo es muy importante, y en consecuencia no debemos limitarnos a desarrollar nuestro juego en una única parte del tablero. Sin embargo, es muy complicado mantener un plan para el tablero completo, por lo que es mejor, a corto plazo, dividir el juego en secciones y concentrarse en cada una de ellas. Esto no significa que podamos obviar el resto del tablero.

Forzar movimientos Existen muchas secuencias de movimientos forzados en el hex, que dan la victoria al oponente si no se ejecutan. Manteniendo la iniciativa, puede ser interesante forzar al rival a hacer ciertos movimientos. Existen otras variantes, como las derivadas de intrusiones en conexiones y la aplicación de plantillas.

Dominio del centro Es importante controlar el centro del tablero. Desde aquí, las conexiones puede tomar muchos sentidos, muchos más que si parten del borde, desde donde hay menos posibilidades.

3.3 Teoría del hex El hex es un juego muy interesante desde un punto de vista puramente matemático, y como tal ha fascinado muchos amantes de esta disciplina desde el instante mismo de su invención. Dentro de la teoría de juegos, el hex se encuadra en la categoría de juegos abstractos finitos de dos jugadores con información completa y sin influencia del azar (determinista). El hex es, además un juego de suma cero, lo que en la práctica implica que siempre tiene que existir un ganador. En este caso, además, sabemos que este ganador ha de ser único. De acuerdo con esto último, una partida de hex no puede terminar en empate, lo que se deduce de la propiedad de que si todas las celdas del tablero están ocupadas, debe existir una conexión ganadora para uno de los jugadores. Aunque hecho parece evidente,

Conceptos teóricos

19

esta propiedad no es trivial y de hecho es el equivalente del teorema del punto fijo de Brouwer para cuadrados de dos dimensiones [12]. Siguiendo este razonamiento, es sencillo llegar a la conclusión de que ha de existir una estrategia ganadora para uno de los jugadores, como veremos más adelante. En todo juego entre dos jugadores, con información completa y determinista (no depende del azar), existe una estrategia óptima que consiste en maximizar o minimizar los valores asignados a cada estado del juego. El valor del estado Ti se asigna recursivamente siempre que Ti sea seguido de al menos un movimiento posible Ti+1. v(Ti) = 1 si el jugador A ha ganado. v(Ti) = -1 si el jugador B ha ganado. v(Ti) = máx(v(Ti+1)) si el jugador A mueve. v(Ti) = mín(v(Ti+1)) si el jugador B mueve. Dados estos valores orientativos, obviamente el jugador B ha de minimizar cada movimiento y A maximizarlo. De esto se puede extraer la conclusión de que el segundo jugador en mover no tiene nada que hacer si el primer jugador sigue su estrategia óptima. Conocer la estrategia óptima de un juego es una buena forma de acabar con él. Sin embargo, la estrategia ganadora en el hex continúa siendo una incógnita.

3.3.1 Estrategia ganadora Informalmente, llamamos estrategia ganadora a una secuencia de movimientos que un jugador tiene disponibles, que el adversario no puede evitar y que le conducen a la victoria. Como se ha dejado establecido en la sección anterior, del hecho de que el hex sea un juego de suma cero podemos deducir que el primer jugador dispone de una estrategia ganadora, a través de un razonamiento que a continuación enunciaremos. Probablemente el hecho de que el primer jugador dispone de una estrategia ganadora fue descubierto de forma independiente por varios matemáticos tras la aparición del juego. Los escritos originales de Hein sugieren que conocía esta circunstancia, y a través de varios trabajos de J. Nash se sabe que también llegó a esta misma conclusión empleando un razonamiento similar al siguiente. Podemos argumentar una demostración de este extremo utilizando un "robo de estrategia" [1, 6]: 1. Dado que el primer o el segundo jugador deben ganar, entonces debe haber una estrategia ganadora para alguno de los jugadores, sea el primero o el segundo. 2. Supongamos que por lo menos el segundo jugador posee una estrategia ganadora. 3. El primer jugador puede adoptar la siguiente defensa: realizar un primer movimiento aleatorio, y posteriormente ejecutar la estrategia ganadora del segundo jugador que asumimos como existente. Adoptar esta estrategia le convierte implícitamente en el segundo jugador, aunque con una ficha extra sobre el tablero. 4. La primera ficha no puede interferir en la imitación de la estrategia ganadora, porque una pieza extra supone siempre una ventaja. Luego el primer jugador puede ganar.

20

Una aplicación para jugar al hex

5. Esto entra en contradicción con el hecho de asumir que existe una estrategia ganadora para el segundo jugador, por lo que debemos descartar esta posibilidad. En consecuencia, existe una estrategia ganadora para el primer jugador. Esta breve e informal demostración se apoya en un lema y un teorema enunciados a continuación, cuyas demostraciones no se verán aquí por ser poco relevantes. Pueden consultarse en la bibliografía sobre el tema, en concreto en [6].

Lema 1: Una partida de hex no puede terminar en empate. Esto implica que ni ambos jugadores pueden ganar ni puede terminar el juego sin que haya ganado ningún jugador. Este lema puede demostrarse analizando el tablero de hex como un grafo plano no dirigido.

Teorema 1: Para cualquier juego de dimensión n x n, el primer jugador posee una estrategia ganadora. Hay que advertir, no obstante, que este teorema sólo enuncia la existencia matemática de la estrategia ganadora y no implica que exista una construcción práctica de esta estrategia ni que tal construcción sea conocida.

3.4 Complejidad Acabamos de ver cómo en el hex existe una estrategia ganadora para el primer jugador para tableros de cualquier tamaño. También comentamos que no podíamos esperar descubrir dicha estrategia. En esta sección se buscará algún tipo de aproximación a una estrategia ganadora general.

3.4.1 Resolviendo el hex por fuerza bruta Esta es una solución del hex que en principio es directamente accesible: jugar todas las partidas posibles de hex. Aparentemente, automatizando este proceso, no debería ser demasiado complicado. Pero lo es. Podríamos considerar cualquier tablero de n x n celdas, numerarlas desde 1 hasta n2 y mover ficha sobre ellas en orden. Así, el primer juego completo empezaría {1, 2, 3, ..., n2 1, n2}, el siguiente sería {1, 2, 3,..., n2 - 2, n2, n2 - 1} y así sucesivamente. Podemos descartar un gran número de partidas: Las partidas que comienzan {1, 2, 3, 4, 5,...} son la misma que las que empiecen por {3, 2, 1, 4, 5,...} Muchos juegos terminarán antes de que el tablero esté completo, por lo que podríamos ignorar el resto de movimientos. Podemos hacer una aproximación optimista y suponer que como media, los juegos usan la mitad de las celdas disponibles. Finalmente, la mitad de los juegos quedan cubiertos por simetría. Esto nos lleva a que el número total de partidas es el número de formas en que un jugador puede ocupar un cuarto de las celdas multiplicado por las formas en que el otro jugador puede ocupar otro cuarto de las celdas, dividido entre 2:

"3 % " n2 % $ n2' $ 1 2' ( $ 4 ' $ n ' 1 2 #4 & $ n ' #4 & 2

!

Conceptos teóricos

21

Si fuéramos capaces de ejecutar un movimiento por segundo, una partida de dimensión n nos llevaría, en segundos [6]:

#3 & n 2 !"% n 2 (!"n 2 $4 ' # 1 2& # 3 2& # 1 2& #1 2& % n (!"% n (!"% n (!"% n (!"4 $ 4 ' $ 4 ' $ 4 ' $2 '

!

Así por ejemplo, resolver totalmente el tablero de 2 x 2 llevaría 12 segundos. El de 3 x 3 nos llevaría 2400 segundos, hasta legar al tablero estándar de 11 x 11, para cuya resolución necesitaríamos 1054 segundos, o dicho de otro modo, 3·1046 años. La imagen siguiente da una idea más clara de la magnitud de los tiempos, representando tiempo frente a dimensión, el último valor visible corresponde a dimensión 4.

Figura 11. Tiempo frente a dimensión del tablero

Por supuesto, las computadoras son mucho más rápidas que esto, pero nos da una idea del factor de tiempo necesario para resolver un tablero grande. No parece viable resolver completamente el hex en un periodo razonable de tiempo.

3.4.2 Clasificación Es importante distinguir entre los problemas verdaderamente indecidibles y aquellos que son solo intratables. Un problema es indecidible cuando dado un algoritmo general y una entrada concreta no se puede saber si se ejecutará siempre o se detendrá después de un número finito de pasos. En principio el hex de dimensión n x n es un problema resoluble, aunque sea intratable en la práctica –es decir, dada una posición cualquiera, no disponemos de un algoritmo que pueda ejecutarse en un tiempo polinómico que nos permita conocer qué jugador dispone de una estrategia ganadora y obtenerla–.

22

Una aplicación para jugar al hex

El hex está parcialmente resuelto para tableros pequeños, pero realmente en este caso estamos interesados en encontrar una solución general en función de la dimensión. Que el juego esté resuelto significa que para cualquier posición dada se conoce qué jugador de los dos tiene una estrategia ganadora. Un tratamiento adecuado puede conducirnos además a obtener dicha estrategia. Una solución para el hex consistiría en un algoritmo que puede ejecutarse en tiempo polinómico y que devolviera un movimiento ganador para cualquier posición sobre un tablero de cualquier dimensión. El concepto de intratabilidad se refiere a la disponibilidad de los recursos computacionales necesarios para resolver un problema. En función de estas necesidades los problemas se clasifican en diferentes clases de complejidad. Una característica importante de la teoría de la computabilidad es que los problemas son generalmente reescritos como problemas de decisión, en concreto como preguntas que pueden responderse con un “si” o un “no”. Esto es útil porque nos permite transformar los problemas y comparar unos con otros. En nuestro caso, podríamos convertir al hex en un problema de tipo: Tipo de problema

Hex

Datos

Un tablero de hex parcialmente relleno (o vacío) de dimensión n x n con una posición p.

Pregunta

¿El movimiento p es parte de alguna estrategia ganadora?

La pregunta puede ser formulada para el jugador que posee el turno. En caso de que se responda con un “no”, el jugador debería volver a formular la pregunta hasta que la evaluación de todas las celdas vacías arrojara respuesta negativa. En este caso el oponente sería el poseedor de la estrategia ganadora en el movimiento siguiente. Es posible contestar esta pregunta y utilizarla para descubrir una estrategia ganadora para cualquier tablero en un tiempo finito. Sin embargo, el tiempo requerido para llegar a una solución crece por encima del polinómico en proporción al tamaño del problema (en nuestro caso, el tamaño del tablero).

3.4.3 Clases P y NP En la sección anterior mostramos algunos cálculos que evidencian la enorme cantidad de tiempo necesaria para resolver la versión estándar del hex mediante la única aproximación conocida, que es la exhaustiva. El tiempo crece exponencialmente en función del tamaño del tablero. En torno a 1960, Jack Edmonds introdujo la noción de buen algoritmo como aquel que produce una respuesta en un tiempo inferior al polinómico, relativo al tamaño del problema. Para el hex, el tamaño del problema es el número n de celdas del tablero. Normalmente denotamos como n el número de celdas del borde –la dimensión–, pero de cara a un objetivo computacional se prefiere denotar el número total de casillas del tablero. En cualquier caso, cualquier confusión entre ambas no da lugar a ninguna diferencia práctica, ya que se puede considerar que n2 equivale a n. A esta definición se corresponde una clase de complejidad P (de polinómica) de problemas que son resolubles con buenos algoritmos. Hasta la fecha, no se conoce ningún algoritmo de este tipo que permita resolver el hex. Muchas veces, sin embargo, una solución a un problema complicado puede ser verificada en algunos casos en un tiempo polimómico: es el caso del conjunto de problemas que pueden ser resueltos en tiempo polinómico por una máquina de Turing no determinista, y que reciben el acrónimo NP, de polinómico no determinista. No está

Conceptos teóricos

23

claro, pero hay razones para pensar que P = NP. Sin embargo, esta cuestión todavía no ha sido aclarada, aunque hay un motivador premio de un millón de dólares para quien demuestre o rechace la igualdad anterior. En todo caso, normalmente se supone que P es un subconjunto de NP. Una consecuencia de la ausencia de una respuesta sobre si es cierto P = NP es que existen problemas en NP de los que no podemos saber si tienen una buena solución o no. Algo que conocemos sobre este grupo es que si existe una solución polinómica para al menos uno de ellos, entonces podremos encontrar soluciones polinómicas para todos los problemas NP-completos. Un problema se considera NP-completo si es posible reducir todos los problemas de NP a éste mediante una transformación polinómica, lo que implica que transformar un problema NP-completo conocido en un problema NP probará que es NP-completo. Es sabido que el Hex es al menos tan complicado como un problema NP-completo, a lo que nos referimos como NP-difícil.

3.4.4 El hex es ESPACIOP-completo Stefan Reisch demostró [4] que el hex es un problema completo en el espacio P. Por su lado, unos años antes, Simon Even y R. E. Tarjan mostraron [3] que una versión generalizada del hex llamada “Shannon Switching Game” es ESPACIOP-completo. Ambas demostraciones parten de la clasificación razonada en la sección anterior y son lo suficientemente complejas como para no tratarlas aquí. Pueden consultarse en la bibliografía referida.

3.5 Una aproximación formal Hasta ahora se han hecho algunas valoraciones generales en torno al hex, aportando algunas ideas útiles para comprender un estudio más profundo del juego y los recursos que podemos utilizar los humanos en el hex. Sin embargo, nuestro objetivo es construir un sistema capaz de jugar por sí solo al hex y para ello debemos formalizar lo máximo posible algunos de los conceptos estudiados anteriormente. Un soporte más sistemático nos permitirá trazar equivalencias entre las ideas y técnicas del hex y el lenguaje computacional más fácilmente. Esto puede parecer una contradicción, sobre todo cuando se acaba de mostrar la dificultad de encontrar una solución sistemática computacionalmente. Sin embargo, los ordenadores son capaces de jugar razonablemente bien al hex, por lo que podemos pensar que existe algún método para simplificar el estudio del juego. A lo largo de esta sección y las siguientes introduciremos algunas ideas en este sentido, hasta llegar a un método que nos permita elegir el mejor movimiento disponible. Aunque las reglas del Hex son sencillas, el juego requiere un conocimiento profundo de la estrategia e importantes habilidades tácticas. Las técnicas de búsqueda masiva en árbol, desarrolladas en los últimos cuarenta años principalmente por el ajedrez y utilizadas muy satisfactoriamente en las damas, son bastante menos útiles en juegos con factores de ramificación elevados como el Hex o el Go. De hecho, para un tablero convencional de Hex de 11 x 11, la media de movimientos legales es de 100, mientras que para el ajedrez es de 38 y para las damas es de 8, tal y como se adelantó en la introducción. El análisis del hex requiere, en este sentido, un planteamiento más sofisticado que en el caso de otros juegos: el hex es muy sencillo en su concepción, y lo que hace que una jugada sea mejor que otra responde a diferencias muy sutiles y a un planteamiento a largo plazo. Además, apenas existe espacio para el fallo, salvo si la situación está muy bien asegurada. Esto hace que el trabajo de análisis computacional de las posiciones del hex sea muy delicado y bastante complejo.

24

Una aplicación para jugar al hex

3.5.1 La jerarquía como punto de partida En el estudio de los juegos en general, las posiciones complejas pueden ser descompuestas en sumas de posiciones más sencillas. Puede considerarse que un juego puede descomponerse en varios subjuegos. Este es un método que se aplica en la computación del Go, juego de reconocida complejidad computacional. Aunque no todas las posiciones de Hex pueden descomponerse en una suma de juegos “locales”, muchas pueden considerarse combinaciones de juegos más sencillos. Aplicando diversas reglas de deducción a los subjuegos más sencillos podemos ver cómo éstas pueden ser usadas recursivamente para calcular valores de subjuegos más complejos. A continuación se detallan algunas consideraciones dirigidas al estudio posterior de una función heurística que permita analizar el juego. Para este estudio deben tenerse en cuenta determinadas características del juego que se explican a continuación.

3.5.2 Conexiones y semiconexiones virtuales Los bordes pueden considerarse como celdas del color correspondiente, para generalizar la forma en que el sistema trata las conexiones. Nota: Las siguientes definiciones se plantean, por simplicidad, desde el punto de vista de un jugador (las negras), pero la idea para el adversario es equivalente.

Definición 1: Subjuego Sean x e y dos celdas diferentes, y sea A un conjunto de celdas vacías en una posición dada. Asumiendo x ∉ A, y ∉ A, podemos considerar la terna (x, A, y) como un subjuego, donde el jugador negro intenta conectar x con y por medio una cadena de piezas negras, el jugador blanco trata de prevenirlo y ambos jugadores sólo pueden colocar sus piezas en las celdas de A. Definimos x e y como finales o extremos del subjuego y A como el camino entre ambos.

Definición 2: Conexión virtual Un subjuego es una conexión virtual si y sólo si el jugador negro tiene una estrategia ganadora aunque el blanco juegue primero.

Definición 3: Semiconexión virtual Un subjuego es una semiconexión virtual si y sólo si el jugador negro tiene una estrategia ganadora cuando mueve primero y no cuando mueve en segundo lugar. Aunque una conexión virtual no está restablecida físicamente, es imposible de evitar, por lo que las casillas implicadas en una conexión virtual están conectadas de facto o virtualmente. Las semiconexiones virtuales reciben este nombre porque por sí solas no pueden considerarse conexión, aunque sí en combinación con otras. Así, dos semiconexiones virtuales entre dos casillas equivalen a una conexión virtual. En la práctica es conveniente usar las siguientes definiciones recursivas (definiciones 4 y 5).

Definición 4 (recursiva): Conexión virtual Un subjuego es una conexión virtual si y sólo si para todo movimiento de las blancas existe un movimiento de las negras que hace que el subjuego resultante sea una conexión virtual.

Definición 5 (recursiva): Semiconexión virtual

Conceptos teóricos

25

Un subjuego es una semiconexión virtual si y sólo si no es una conexión virtual y existe un movimiento de las negras que hace que el subjuego resultante sea una conexión virtual. Solemos representar las conexiones virtuales con un rectángulo blanco entre celdas y las semiconexiones virtuales con un rectángulo tachado (en este caso, oscurecido) entre las celdas, como se muestra en la siguiente figura:

Figura 12. Diferentes conexiones y semiconexiones

Definición 6: Profundidad de una conexión virtual Sea una posición conexión virtual y el blanco el primero en jugar. El número de movimientos que deben ejecutarse para que el negro gane el subjuego, considerando que el negro trata de minimizarlo y el blanco de maximizarlo, caracteriza la profundidad de la conexión virtual. La profundidad de una conexión virtual equivale a la profundidad de búsqueda en árbol de juego requerida para establecer sobre el tablero la conexión virtual. Ejemplos: Dos celdas vecinas forman una conexión virtual con una ruta vacía, con una profundidad de 0. Un puente doble forma una conexión virtual de profundidad 2.

Definición 7: Conexión mínima La conexión virtual (x, A, y) es mínima si y sólo si no existe una conexión virtual (x, B, y) tal que B ⊂ A (B subconjunto de A) y B ≠ A. Si un subjuego (x, A, y) es una conexión virtual y (x, C, y) es un subjuego tal que A ⊂ C, el juego (x, C, y) es conexión virtual.

Definición 8: Conexión virtual ganadora Una conexión virtual entre las celdas de borde equivale a ganar el juego. Si existe, existe también una estrategia ganadora para el jugador negro aunque el jugador blanco mueva antes.

3.6 Descripción de la heurística Una vez sentadas ciertas ideas, podemos introducir ya una primera aproximación a la parte más “inteligente” de la aplicación. Pero primero veamos algunas nociones en torno a este concepto. En concreto, es importante la idea de que este tipo de sistemas son, en realidad, problemas de búsqueda de soluciones. Un algoritmo de búsqueda toma como entrada un problema y devuelve una solución en forma de secuencia de acciones, que después pueden, opcionalmente, ejecutarse. En la búsqueda de soluciones a problemas, generalmente diferenciamos la búsqueda “a oscuras” o no informada de la búsqueda informada. En una estrategia de búsqueda no informada sólo disponemos de información del sistema a un nivel de observación, por lo que no podemos basarnos en otro tipo de información adicional en nuestro proceso de búsqueda.

26

Una aplicación para jugar al hex

Al contrario que una estrategia no informada, una estrategia de búsqueda informada utiliza un conocimiento específico del problema más allá de su definición, pudiendo encontrar soluciones al problema de una manera más eficiente que una estrategia no informada. Las búsquedas mediante estrategias informadas son también conocidas como heurísticas.

3.6.1 ¿Qué es una heurística? Se denomina heurística a la capacidad de un sistema para realizar de forma inmediata innovaciones positivas para sus fines. La capacidad heurística es un rasgo característico de los humanos, desde cuyo punto de vista puede describirse como el arte y la ciencia del descubrimiento y de la invención o de resolver problemas mediante la creatividad. Desde un punto de vista computacional, dos objetivos fundamentales para la mayoría de casos son encontrar algoritmos con buenos tiempos de ejecución y buenas soluciones, usualmente las óptimas. Una heurística es un algoritmo que ofrece uno o ambos objetivos; por ejemplo, normalmente encuentran buenas soluciones, aunque en ocasiones no hay pruebas de que la solución no pueda ser arbitrariamente errónea; o se ejecuta razonablemente rápido, aunque no existe tampoco prueba de que deba ser así. A menudo, pueden encontrarse instancias concretas del problema donde la heurística producirá resultados muy malos o se ejecutará muy lentamente. Aún así, estas instancias concretas pueden ser ignoradas porque no deberían ocurrir nunca en la práctica por ser de origen teórico, y el uso de heurísticas es muy común en el mundo real. En su aplicación a los juegos, una heurística se compone de: 1. Un estado inicial, compuesto, en este caso, por el tablero y la información sobre el jugador que tiene el turno. 2. Una función de cómputo, también denominada función de evaluación o función heurística, que determina el valor de un movimiento para el jugador actual. 3. Una función sucesor, que determina el siguiente movimiento a evaluar. 4. Una prueba o test terminal, que determina cuándo finaliza el juego y qué jugador resulta ganador. Evidentemente, una heurística sólo tiene sentido en un contexto de búsqueda de la mejor solución disponible (en este caso, la mejor jugada). El estado inicial y los movimientos legales para cada lado definen el árbol de jugadas.

3.6.2 Árboles de jugadas En un enfoque tradicional, podemos considerar la existencia de dos jugadores, MIN y MAX, que compiten por lograr el objetivo del juego. Estos jugadores reciben estos nombres por el uso que hacen de la función de evaluación. Generalmente, una jugada buena para un jugador es tan buena como mala es para el otro. Esto parece un poco enrevesado, pero es una consecuencia de la oposición de intereses que se da en los juegos: una jugada buena para un jugador le aproxima al objetivo, lo que implica que su oponente se aleja del mismo. Así, podemos pensar que el objetivo de un jugador es maximizar el valor de las jugadas y el del oponente minimizarlo. Normalmente al primer jugador lo llamamos MAX y al segundo MIN. Cuando trazamos un árbol de jugadas, dibujamos para cada posibilidad, las alternativas disponibles que tiene el oponente. Luego en el árbol de jugadas se alternan los nodos MAX y MIN.

Conceptos teóricos

27

El siguiente gráfico muestra parte del árbol de jugadas del “tres en raya”. Podíamos considerar que el primer nodo corresponde al jugador MAX, que en este caso serían las fichas marcadas como estrellas. El primer nodo es MAX porque este jugador tiene el turno, de modo que debe ejecutar un movimiento que maximice su cómputo. Cuando haya ejecutado este movimiento nos encontraremos en el nivel 2 del árbol, con una estrella colocada en el tablero. En este momento, será el turno del jugador MIN, que deberá contrarrestar esta situación mediante un movimiento que minimice su cómputo. Y así sucesivamente.

Figura 13. Un segmento del árbol de jugadas para el tres en raya

Definimos valor-minimax de un nodo como la utilidad para un jugador de un nodo correspondiente, asumiendo que ambos jugadores juegan óptimamente desde ese nodo hasta el final. El valor minimax de un estado terminal es el de su función de utilidad o heurística. Considerando las opciones, MAX preferirá moverse a un estado de valor máximo, mientras que MIN preferirá uno de valor mínimo. Informalmente,

# Utilidad(n) si n es un estado terminal % valor " minimax = $ Máximo(valor - minimax(sucesores)) si n es un nodo MAX % Mínimo(valor - minimax(sucesores)) si n es un nodo MIN &

!

El algoritmo minimax calcula la decisión minimax más apropiada al estado actual. Se basa en un cálculo recursivo de los valores-minimax para cada sucesor de un nodo. La recursión avanza hacia las hojas del árbol y al llegar a estas y calcular la función utilidad, estos valores retroceden por el árbol.

3.6.3 Poda alfa–beta La búsqueda minimax tiene que examinar un número de estados que crece de forma exponencial con el número de movimientos, aunque esta circunstancia puede reducirse al menos a la mitad. La técnica que se utiliza para reducir el árbol se denomina poda, y en el caso del algoritmo minimax se suele utilizar la llamada poda alfa-beta, que cuando se aplica a un árbol minimax convencional devuelve el mismo movimiento que devolvería el algoritmo sin poda. La idea general de la poda es que no hace falta ahondar en lo mala que es una posibilidad. Es decir, si se encuentra una variante peor que la peor que se había detectado hasta el momento, no merece la pena evaluarla. Para delimitar y mantener estos valores se utilizan dos parámetros (α y β) de los que el método toma el nombre.

28

Una aplicación para jugar al hex

Dado que no es objetivo de esta memoria ofrecer una descripción detallada de estos algoritmos, se recomienda acudir a la bibliografía especializada. En concreto es interesante consultar [9] y [10].

3.6.4 Una primera idea La función de cómputo más razonable conocida para el hex se basa en un método denominado H-Search, que nos permite generalizar ciertos patrones conocidos extendiéndolos al tablero completo. De esta forma, a través del análisis de entornos reducidos, podremos llegar a una valoración de un entorno general. Este algoritmo fue introducido por V. Anshelevich [1], el autor de la mayor parte de la documentación existente sobre este tema. La idea general de este método consiste en que una jugada será más o menos buena en función de que minimice la distancia de un jugador entre los bordes y procure maximizar esta distancia para el oponente. La dificultad reside en el hecho de que, aunque no exista una conexión sólida entre dos celdas, sí pueden formar una conexión implícita, que es igual de fuerte, por lo que tenemos que tener muy en cuenta a estas estructuras en nuestro diseño. La idea central consiste en que el programa sea capaz de detectar las conexiones y combinarlas para simplificarlas. Para poder estimar esta distancia debemos primero calcular la distancia virtual que separa los bordes de cada jugador. Diremos que se trata de una distancia virtual porque no se corresponde exactamente con la distancia visible en el tablero, sino que se deriva del estado subyacente del juego: así, puede existir una conexión de un jugador imposible de evitar. Aunque esta conexión no esté cerrada físicamente, sí lo está implícitamente, por lo que podemos considerar que existe una distancia nula entre los bordes. Podemos definir la distancia virtual como la distancia entre los bordes de un jugador teniendo en cuenta las conexiones virtuales establecidas. Así pues, para obtener un valor de esta distancia, primero debemos definir sobre una representación del tablero las conexiones que reflejen la verdadera situación del juego, creando ciertos enlaces adicionales. Para obtener esta representación son muy útiles las dos reglas AND y OR que veremos en la sección siguiente.

3.6.5 Reglas de deducción Estos enlaces se crean aplicando dos reglas de deducción de origen recursivo, la regla AND y la regla OR. Estas reglas permiten construir conexiones virtuales complejas partiendo de otras más sencillas, constituyendo un álgebra para las conexiones virtuales. Siempre desde el punto de vista de las negras, estas reglas se enuncian así:

La regla AND

Figura 14. Esquema de la regla AND

Sean los subjuegos (x, A, u) y (u, B, y) dos conexiones virtuales con un extremo común en u y otros dos diferentes x e y tales que x ≠ y. Asumimos que x ∉ B, y ∉ A, A∩B = ∅. Si u es negra, entonces el subjuego (x, A ∪ B, y) es una conexión virtual. Si u está vacía, entonces el subjuego (x, A ∪ u ∪ B, y) es un semiconexión virtual.

Conceptos teóricos

29

Dicho de otra manera más informal, dos conexiones virtuales con un extremo común equivalen a extender dicha conexión a otra que incluya las pertenecientes a ambas conexiones. Si este extremo común es una celda negra, ambas conexiones virtuales forman a su vez una conexión virtual con esta celda, por lo que es una conexión virtual estricta. En otro caso, el jugador blanco puede jugar sobre esta celda de unión e impedir la conexión, de modo que será simplemente una semiconexión virtual.

La regla OR

Figura 15. Esquema de la regla OR

Sean varios subjuegos (x, Ak, y), con k = 1, 2, 3, ... , n para n > 1) con extremos comunes y que sean semiconexiones virtuales. Si la intersección de todos los juegos es vacía, el subjuego (x, A, y), en el que A es la intersección de todos los Ak, es una conexión virtual. Dicho de otro modo, si entre dos casillas existen dos o más semiconexiones virtuales, entonces podemos afirmar que existe una conexión virtual entre ellas. Esto es así porque si el jugador blanco intercepta una semiconexión virtual, el negro podrá asegurar la que haya quedado libre. Combinando la regla OR y la regla AND podemos simplificar el juego, en un orden de jerarquía inversa, como se muestra en la figura:

Figura 16. Una deducción entre A y C

3.6.6 Descubrir conexiones con H-Search A pesar de la sencillez conceptual de las reglas AND y OR, la búsqueda de conexiones y de casillas que verifiquen dichas reglas no es ni mucho menos trivial. El algoritmo HSearch permite aplicar a las casillas y a las listas de conexiones una serie de operaciones que nos permitan deducir nuevas conexiones implícitas. Estas nuevas conexiones deberían quedar reflejadas en el grafo añadiendo nuevos enlaces entre las casillas. El algoritmo H-Search se basa en la equivalencia de unas posiciones complejas en otras más sencillas, o lo que es lo mismo: cómo se construyen conexiones complejas a través de otras más simples. Precisamente esta idea jerárquica es la que da el nombre al algoritmo H-Search, abreviatura de “Hierarchical Search”, búsqueda jerárquica en inglés. El algoritmo H-Search no es otra versión de la habitual búsqueda en árbol para juegos. La H-Search y la búsqueda en árbol utilizan diferentes representaciones del problema y,

30

Una aplicación para jugar al hex

como resultado, buscan en diferentes espacios. En particular, H-Search busca conexiones virtuales en el conjunto de subjuegos de una posición de hex. Esto nos lleva a otra pregunta: ¿es el conjunto de las reglas AND y OR completo? Es decir, ¿puede el algoritmo H-Search construir todas las conexiones virtuales comenzando desde las más sencillas? La respuesta es que no, y existen ejemplos de conexiones virtuales que no pueden ser construidas mediante este procedimiento. Hay que decir, eso sí, que las reglas AND y OR pueden ser generalizadas para convertirlas en reglas completas. Sin embargo, la búsqueda H-Search generalizada no ha sido probada con buenos resultados y su valor práctico no está todavía claro. El algoritmo de búsqueda H-Search está implementado en Hexy y en Six. Ambos programas son realmente fuertes y han ganado diversas competiciones. Sirviendo esto como anticipo, más adelante se ofrecerá un análisis pormenorizado de cómo se realiza este proceso.

3.6.7 Función de evaluación y circuitos eléctricos Una vez establecidas las relaciones (descubiertas a través de H-Search) y representadas sobre un grafo, que es en realidad un tablero simplificado, debe ejecutarse sobre éste un algoritmo que calcule la distancia global entre los bordes de un mismo color. Nos referiremos a estas resistencias como RN (para las negras) y RB (para las blancas). El cálculo de esta distancia equivale a resolver la resistencia eléctrica equivalente en un circuito obtenido a partir del grafo [13], sustituyendo las aristas por enlaces eléctricos con una resistencia variable (resistores). El valor de estas resistencias es el mismo que el de las aristas del grafo. Esta idea, de una gran belleza, fue implementada por primera vez por Shannon [14]. La idea, para el caso de las negras, se muestra así:

Figura 17. ¿Circuito o tablero?

El cálculo de los valores de las aristas se calcula en función del de los vértices. Así, cada vértice c toma una pareja de valores rb(c) y rn(c). Cada parámetro puede tomar tres valores distintos, a saber:

#1 si c está vacía % rb (c) = $0 si c está ocupada por las blancas %" si c está ocupada por las negras & #1 si c está vacía % rn (c) = $0 si c está ocupada por las negras %" si c está ocupada por las blancas & Para cada par de vértices vecinos (sean c1 y c2), la arista que los une tiene un valor:

!

!

rb (c1,c 2 ) = rb (c1 ) + rb (c 2 ) rn (c1,c 2 ) = rn (c1 ) + rn (c 2 )

Conceptos teóricos

31

Al modificarse el valor de un vértice (lo cual sólo puede suceder una vez a lo largo del juego) deben recalcularse los valores de las aristas que lo rodean. Con los datos de resistencias adecuados, debemos definir un grafo que represente un circuito eléctrico. Para ello, añadiremos un conductor entre dos celdas x e y si forman una conexión virtual, de la misma manera que si fueran celdas vecinas. Una vez establecida esta similitud, calcular la distancia global entre los bordes equivale a calcular la resistencia equivalente del circuito, resolviendo su matriz asociada por un método de iteraciones convencional. Esta distancia, contrapuesta a la del oponente, nos da un valor numérico de la calidad de la jugada, que se obtiene a través de la expresión:

"R % E = log$ N ' # RB &

!

Otra medida razonable es la longitud del camino más corto en el grafo que conecte los bordes, aunque utilizar la resistencia en el circuito equivalente da un cálculo más preciso, al tener en cuenta las intersecciones y los diferentes caminos que conecten los bordes. En el sistema construido, la función E es algo diferente, siguiendo la idea de un artículo anterior [11] de V. Anshelevich. En concreto, teniendo en cuenta que los valores mayores representan ventaja para las negras y los menores para las blancas:

E=

!

RB RN

De este modo, si la resistencia RN de las negras es nula, quiere decir que disponen de una CV entre sus bordes del tablero, con lo que en teoría habrán ganado la partida. El valor de esa jugada, entonces, será infinito. En el caso de las blancas, si disponen de una conexión ganadora el valor de RB será igual a cero, y el valor global de la jugada también cero. El valor de E para el sistema implementado varía en el intervalo [0, ∞) En otros juegos, al probar una jugada en la heurística se comprueba si esa jugada lleva a una condición de victoria mediante el test terminal. Así, si en una partida de ajedrez tenemos una jugada que nos lleva a jaque mate a nuestro favor, la ejecutaremos sin más. Sin embargo, en el hex esto no es del todo cierto, y en consecuencia la heurística no se apoya en el test terminal en ningún momento, pues apenas resulta útil. En su lugar, se considera que la jugada es ganadora simplemente con que conduzca a una conexión virtual ganadora. El resto del juego consistirá simplemente en concretar esta conexión y en no permitir que el adversario se interponga en ella.

3.7 Encontrando la mejor jugada A través de los procedimientos anteriores se obtiene un valor numérico de lo buena o mala que es una jugada. Una vez se han introducido estos conceptos, se verá cómo se relacionan entre sí para lograr devolver al sistema la mejor jugada disponible. Cabe destacar que no se pueden representar todas las situaciones del juego utilizando la jerarquía del juego y la aplicación de las reglas AND y OR, pues no se trata de un conjunto completo. Esto implica que la decisión de la siguiente jugada ha de estar basada en diversos datos adicionales que es necesario tener en cuenta. Así, el procedimiento general que nos conduce a la jugada que ejecutará el sistema es el siguiente: 1. Generar un árbol de de búsqueda minimax con las posibles jugadas y las respuestas a éstas hasta una profundidad variable. Aplicar, si procede, poda

32

Una aplicación para jugar al hex

alfa-beta a las ramas del árbol para agilizar el proceso. Para jugada que se desee evaluar, han de ejecutarse los pasos 2 y 3 para ambos jugadores. 2. Obtener las conexiones y semiconexiones virtuales equivalentes para cada tablero generado, mediante la aplicación del algoritmo H-Search. 3. Resolver el circuito eléctrico asociado a las conexiones descubiertas en el paso anterior y obtener el valor de la resistencia equivalente. La parte verdaderamente interesante es la que corresponde a los puntos 2 y 3, y por lo tanto vamos a detallarlas en la siguiente sección.

3.7.1 Calcular el valor de una jugada Para calcular el valor de una jugada determinada, ésta debe simularse sobre el tablero de juego. El sistema creará un objeto Simulación para cada jugada que se vaya a simular, y poder trazar de esta forma los cambios realizados en las estructuras comunes.

Aplicando el algoritmo H-Search Cuando se genera una simulación para analizar una jugada, se inserta en el tablero y se estudia si está rodeada de casillas de su mismo color. Esto es así porque varias celdas juntas del mismo jugador pueden simplificarse en el grafo interno por una misma, siempre que conservemos las conexiones. Se hereda de la jugada anterior una copia del tablero, sobre la que se ejecutan todos los cambios. Esta copia contiene las conexiones virtuales iniciales del tablero: es decir, entre las celdas adyacentes existe una conexión de camino nulo (inmediata). Se guarda un registro de algunos cambios para deshacerlos, ya que algunas estructuras son demasiado complejas como para copiarlas. Consideremos cualquier conjunto inicial de conexiones virtuales y semiconexiones virtuales como la primera generación de CV y SCV. En particular, este conjunto inicial puede consistir en las conexiones virtuales más sencillas, como lo son todos los pares de celdas vecinas del tablero. Aplicando la regla AND y la regla OR a los grupos apropiados de esta primera generación de CV y SCV, obtendremos una segunda generación. Entonces aplicaremos de nuevo las reglas de deducción AND y OR a la primera y a la segunda generación para construir la tercera generación de CV y SCV. Este proceso termina cuando no aparecen nuevas conexiones virtuales o se encuentra una conexión ganadora. En la práctica, y debido a la limitación de los recursos computacionales, H-Search sólo es capaz de descubrir conexiones virtuales ganadoras en los tableros pequeños o en los finales de partida. Sin embargo, en el resto de las situaciones se consiguen aproximaciones muy descriptivas de la situación del tablero y el balanceo de fuerzas.

Resolviendo el circuito eléctrico En principio, y de acuerdo a la documentación existente sobre el tema, con las conexiones obtenidas de la aplicación de H-Search, se debe obtener un circuito eléctrico que represente la situación del tablero. Después, debe generarse la matriz para este circuito y resolverla mediante un método de iteraciones o similar. Sin embargo, este método puede simplificarse en extremo haciendo las siguientes consideraciones: 1. Anteriormente se estableció que la resistencia de una celda ocupada por el oponente es r = ∞. Si tenemos una rama N entre el nodo A y B, y rA= ∞ o bien rB = ∞, entonces sabemos que rN = rA + rB = ∞. En un circuito eléctrico si la resistencia es infinita, la corriente que circula por esa rama es nula, luego

Conceptos teóricos

33

podemos descartar en nuestros cálculos todas las ramas adyacentes a nodos que representen celdas ocupadas por el contrario. 2. La abstracción del circuito eléctrico es útil por una cuestión intuitiva. Sin embargo, del circuito sólo nos interesa su matriz de resistencias, que es lo que nos sirve para resolver su ecuación. De acuerdo con esto, podemos omitir la construcción del circuito y elaborar directamente la matriz del mismo. Así pues, recorremos un conjunto de casillas donde estén insertadas las propias del jugador que se evalúa. Para cada casilla obtendremos el estado de sus vecinas, y según este estado cargaremos el índice correspondiente de la matriz con su valor. Una vez terminada la matriz, se resuelve utilizando el método del Pivote de Gauss. El valor obtenido es la resistencia equivalente del circuito, y el valor de la posición dada para el jugador estudiado.

34

Una aplicación para jugar al hex

3.7.2 Pseudocódigo A continuación se detalla el procedimiento anteriormente descrito en forma de pseudocódigo. Dicho procedimiento será descrito en profundidad en los anexos de diseño del sistema software construido. Clase Simulación Constructor (celda objetivo de la simulación Si hay vecinas del mismo color a la celda que se acaba de insertar fusionarlas en una sola almacenar las celdas eliminadas y las conexiones originales Procedimiento Calcular valor de la jugada : doble Resistencia blancas = Calcular la resistencia (blancas) Resistencia negras = Calcular la resistencia (negras) Devolver Resistencia blancas / Resistencia negras Procedimiento Aplicar Regla OR (conjuntos) Si estos nuevos conjuntos han formado SCV actualizar y crear una CV. Procedimiento Calcular la resistencia (color) Anotar los siguientes caminos en caducar G = Generar el conjunto G para color C = Generar una lista para las conexiones virtuales SC = Generar una lista para las semiconexiones virtuales Mientras se cree al menos una nueva CV: Tomar una casilla como pivote y otras dos como auxiliares. Siempre que exista una conexión recién descubierta: Si el pivote es de color: por la regla AND, existe una CV entre las dos auxiliares que implica al pivote Si no: por la regla AND, existe una SCV entre los dos auxiliares que implica al pivote. Si no existía esta SCV, entonces Aplicar Regla OR (conjuntos resultantes de aplicar la regla AND) Con las conexiones descubiertas, generar el circuito eléctrico equivalente Matriz = Generar una matriz que represente al circuito Resolver mediante pivote de Gauss (Matriz)

3.7.3 Conclusiones En esta sección se han introducido parte de los métodos precisos para la evaluación de jugadas en el hex. En resumen, la idea general consiste en que una jugada es buena para un jugador en la medida en que contribuya a minimizar la distancia entre sus bordes, mientras maximiza la distancia entre los bordes del oponente. Esta distancia se obtiene calculando la resistencia entre los bordes del tablero al simular un circuito eléctrico sobre las conexiones virtuales más completas que el algoritmo haya sido capaz de descubrir. Estas conexiones se descubren mediante la aplicación de las reglas AND y OR. Al principio de este apartado se advertía que el hex puede parecer un juego sencillo a primera vista, percepción ésta totalmente errónea. Con la implementación ocurre algo parecido: la aparente simplicidad de las estructuras pronto desborda las previsiones más optimistas, los tiempos empiezan a dispararse y el algoritmo capaz de resolver un tablero

Conceptos teóricos

35

pequeño en apenas un segundo se vuelve extremadamente lento al pasar a tableros mayores. El hex un juego exigente en cuanto a la implementación, pues las estructuras que requiere son complejas y las operaciones largas y muy repetitivas. Hasta la representación de tablero es potencialmente crítica al ser tan diferente al de los juegos tradicionales. Es éste un juego que empieza provocando incredulidad y termina obsesionando a quien se acerca a él. Por algo ha fascinado a algunas de las mentes más destacadas de nuestro tiempo. Shannon, Moore, John Nash... aunque procedentes de distintas disciplinas, todos ellos padecieron a lo largo de su vida la irrefrenable atracción por este juego, que tengo el honor de compartir con ellos.

Técnicas y herramientas

37

4 Técnicas y herramientas A continuación se resumen algunas decisiones relevantes de diseño con su correspondiente justificación.

4.1 Orientación a objetos Desde un primer momento se apostó por el modelo orientado a objetos, a pesar de la naturaleza fuertemente estructurada de este tipo de problemas. Existe cierta pérdida de eficiencia inherente al concepto de orientación a objetos, que motiva que haya que plantearse seriamente el paradigma que se va a utilizar en el desarrollo de aplicaciones como un juego, donde los tiempos son un factor importante. En la sección “Aspectos relevantes” se realizan valoraciones en profundidad sobre las metodologías utilizadas en el desarrollo.

4.2 Prototipado No existen experiencias anteriores en la implantación de juegos de hex sobre un lenguaje como Java y, por tanto, había dudas muy importantes sobre su viabilidad. En consecuencia, fue necesario desarrollar algunos prototipos previos para probar el rendimiento de los algoritmos de decisión. Principalmente se construyeron algunas versiones del algoritmo de búsqueda HSearch y de las estructuras que requiere el mismo para ejecutar algunas pruebas de rendimiento.

4.3 Algunas decisiones A continuación se enumeran algunas decisiones tomadas en base a diferentes alternativas, argumentando los motivos de las mismas:

4.3.1 Lenguaje de programación A la hora de elegir el lenguaje de programación se establecieron los siguientes requisitos: 1. Ser un lenguaje orientado a objetos puro. 2. Capacidad de desarrollar software multiplataforma sin hacer demasiados cambios, a ser posible ninguno. 3. Soportar diseño de interfaces gráficas de usuario de forma nativa. 4. Permitir una posible integración en Internet. 5. Existencia de herramientas de desarrollo libres para el lenguaje. 6. Ejecutables rápidos. Era importante mantener un rendimiento lo más elevado posible. 7. Soportar genericidad. Esto es necesario por las numerosas listas y estructuras complejas que mantendría el software.

38

Una aplicación para jugar al hex

Como alternativas, se evaluaron los lenguajes C++, C# y Java. En principio, C++ permite transferir los sistemas entre plataformas realizando compilaciones separadas, siempre que no se utilicen funciones específicas del sistema operativo. El problema surge en la interfaz gráfica, pues posiblemente habría que haber desarrollado diferentes versiones para cada plataforma. En el caso de C# existe el mismo problema, e incluso agudizado por la dependencia de una única plataforma. Finalmente, la posibilidad de utilizar tecnologías como los applet, los servlet o el Java Web Start inclinó la balanza hacia el lenguaje Java, al precio de sacrificar algo de rapidez.

4.3.2 Biblioteca para matemáticas El sistema requiere de determinadas funciones comunes, como resolución de sistemas de ecuaciones de cierta magnitud. Para realizar estas actividades, en principio se pensó en utilizar JScience, una biblioteca específica para aplicaciones científicas en Java. JScience es una solución muy interesante y muy completa, con soporte para genericidad, pero se estimó que no era muy razonable enlazar una librería tan grande para utilizar una mínima parte, por lo que se optó por la construcción manual de dichos módulos. JScience Sitio web de JScience. Última vez visitado: 5 de junio de 2007. http://jscience.org

4.3.3 ¿Applets o servlets? Esta cuestión surgió al poco tiempo de comenzar el proyecto, y sugería interesantes posibilidades, al permitir la ejecución del sistema a través de Internet. Gracias a la flexibilidad de Java, se decidió aplazar la decisión y comenzar a desarrollar una aplicación de escritorio que en el futuro pudiera ejecutarse como applet o como servlet. Finalmente este extremo se descartó, ya que varios accesos simultáneos a una aplicación con un consumo elevado de recursos podría ocasionar problemas en el servidor.

4.4 Herramientas de desarrollo A continuación se enumeran las herramientas utilizadas para el desarrollo del sistema.

4.4.1 Entorno de desarrollo La práctica totalidad del sistema ha sido desarrollada sobre el IDE NetBeans 5.0 y 5.5, desarrollado por Sun Microsystems sobre el sistema Mac OS X. Para algunas partes y adaptaciones iniciales se utilizó el IDE Xcode, desarrollado por Apple Inc. Para la mayor parte del desarrollo se prefirió utilizar NetBeans por la facilidad de diseñar interfaces gráficas en este entorno, aunque finalmente estas características no se utilizaron por razones de eficiencia.

4.4.2 Planificación Para la planificación se evaluaron las dos herramientas más populares para Mac OS: xTime y Merlin. Finalmente se prefirió Merlin por ofrecer más facilidad de uso y menos restricciones en su versión gratuita que xTime. Merlin resulta bastante más ágil para proyectos relativamente pequeños o con pocos agentes, y la experiencia con esta aplicación es muy satisfactoria.

Técnicas y herramientas

39

Mac Project planning software En este artículo (en inglés) se encontró información muy útil sobre aplicaciones para entornos Mac OS. Última vez visitado: 4 de junio de 2007.

ese tipo de

http://www.anyware.co.uk/2005/2006/03/23/mac-project-planning-software

4.4.3 Elicitación de requisitos Para la fase de requisitos se utilizó la herramienta REM, de la Universidad de Sevilla, que permite gestionar la información generada y presentarla adecuadamente con relativa sencillez.

4.4.4 Herramientas CASE Para el modelado se optó por la aplicación MagicDraw UML, de la cual hay disponible una versión gratuita para evaluación que se adapta a los requerimientos del proyecto. Se encuentra a esta alternativa muy superior al resto de herramientas CASE de que se disponen.

4.4.5 Diseño gráfico Para el diseño de los esbozos de la interfaz gráfica y para algunos diagramas se utilizó la aplicación OmniGraffle Professional, con una experiencia muy positiva. La mayoría de las imágenes de la aplicación final fueron generadas con Macromedia Freehand MX, con The Gimp y con Seashore.

Aspectos relevantes

41

5 Aspectos relevantes Esta sección es un resumen de los aspectos más interesantes del desarrollo del proyecto. La parte central del mismo es la construcción de un sistema, llamado Hexodus, que permite al usuario enfrentarse contra el ordenador en una partida de hex, basado en las ideas expuestas anteriormente. Dicha aplicación es el primer juego de hex multiplataforma, con posibilidad de ejecución paralela y construido haciendo uso de la tecnología Java.

5.1 Metodología Al comenzar la planificación de este proyecto se hacía frente a dos cuestiones: por un lado, la existencia previa de una biblioteca de clases para hex escrita en C++ como parte de un trabajo anterior [8]. Por otro lado, las características del desarrollo hacían deseable hacer un desarrollo rápido y algunas pruebas con el motor de la heurística antes de abordar el desarrollo en toda su dimensión.

5.1.1 Reutilización Una parte del código y de la documentación se recuperó de una biblioteca de clases diseñada como parte de la práctica de la asignatura de Programación Orientada a Objetos durante el curso 2005 – 2006 [8]. La biblioteca consiste en un paquete de clases escritas en C++ para soportar la implementación sencilla de programas para jugar al hex, aunque evidentemente no era capaz de jugar contra el usuario y sólo permitía el modo de dos jugadores. Sin embargo, el núcleo funcional que podía dar soporte al prototipado estaba ya construido. Los procesos y la documentación de esta biblioteca estaban asimismo disponibles, por lo que una parte del prototipo pudo desarrollarse muy rápidamente, mediante una revisión de los modelos del sistema. Fue preciso hacer un trabajo previo para traducir el código a Java y corregir cierto número de errores detectados.

5.1.2 Prototipado El objetivo del prototipado fue construir un módulo temprano que permitiera probar el funcionamiento y la adaptación de los algoritmos de detección de conexiones que figuran en la documentación. Esta implantación constituía un factor de riesgo importante en el desarrollo. Antes de comenzar el prototipado se expandieron los modelos de análisis y diseño de la parte reutilizada para dar cabida a las nuevas clases. Aplicando diferentes patrones se determinó qué parte del modelo ocuparía en módulo prototipado y se diseñó un borrador de los requisitos que debía cumplir. Este modelo estaría en el futuro en conexión con el resto del juego, por lo que se diseñaron las interfaces para realizar esta comunicación, con sus correspondientes modelos de interacción.

42

Una aplicación para jugar al hex

La elaboración del prototipo se prolongó durante un tiempo relativamente largo por la complejidad propia de los algoritmos, y así, una parte significativa del esfuerzo de desarrollo se ha invertido en este proceso.

5.1.3 Validación Al dar por validado el prototipo se procedió a trazar los cambios realizados sobre la estructuras para hacerlos coherentes con los modelos de desarrollo existentes. El resto del proceso, en concreto el diseño de la interfaz y la conexión de los diferentes módulos, se realizó de acuerdo a una adaptación del Proceso Unificado (PU), como se describe a continuación.

5.1.4 Una adaptación del Proceso Unificado El Proceso Unificado es un proceso de desarrollo iterativo e incremental compuesto de cuatro fases denominadas Inicio, Elaboración, Construcción y Transición, donde cada una de estas fases está a su vez dividida en una serie de iteraciones, que ofrecen como resultado un incremento del producto desarrollado que añade o mejora las funcionalidades del sistema en desarrollo. El Proceso Unificado se construye sobre cuatro pilares: 1. Dirigido por casos de uso, cuya aplicación se utiliza para descubrir las estructuras necesarias del sistema. 2. Iterativo e incremental. 3. Centrado en la arquitectura. 4. Focalizado en los riesgos El sistema desarrollado se ha construido de acuerdo con estos cuatro principios, dando una especial relevancia a los casos de uso como directores del proceso, refinando los sistemas individualmente en las iteraciones y añadiendo otros nuevos en los sucesivos incrementos. La presencia de un módulo reutilizado y la necesidad de realizar un prototipado, tal como se expuso anteriormente, influyeron decisivamente en los procedimientos llevados a término durante el desarrollo. La suma sinérgica de la orientación a objeto y del concepto del proceso iterativo e incremental fue vital en la gestión del prototipado evolutivo y favoreció de una forma muy destacable la reutilización de los módulos ya construidos y de sus artefactos asociados.

5.2 Modularidad La concepción básica del sistema se basa en dos grandes paquetes: uno dedicado a la gestión del juego y otro dedicado a encontrar las mejores jugadas cuando en el juego interviene el ordenador. Esto resulta interesante porque implica la existencia de dos sistemas completamente independientes que pueden considerarse dos bibliotecas de clases separadas: 1. El sistema base del juego con las reglas lógicas del mismo, que permite su extracción e integración en el desarrollo de otros juegos de hex. 2. El motor de inteligencia del programa, que puede utilizarse de forma independiente en otros sistemas, simplemente respetando las interfaces. La idea es poder utilizar estos subsistemas en otros contextos con poco esfuerzo de desarrollo, simplemente modificando la interfaz de usuario. Para reforzar este concepto de independencia, ambos módulos están empaquetados por separado.

Aspectos relevantes

43

Gracias a este diseño han podido desarrollarse utilidades relacionadas con el juego, como la aplicación auxiliar Hextress, que se describe en el siguiente apartado.

5.3 Procesamiento paralelo Los sistemas de procesamiento paralelo ofrecen posibilidades muy interesantes en el tratamiento de este tipo de problemas. En principio se descartó la construcción de un sistema multihilo para no complicar el diseño, aunque finalmente se optó por incluirlo en algunas pruebas, que arrojaron resultados muy positivos.

Figura 18. Repartiendo casillas entre procesadores para un tablero de 2x2

En este esquema, cada celda lleva asociada una simulación. Para cada simulación se crea un hilo que la procesa y devuelve el resultado de jugar en esa casilla al procedimiento principal, que elige la mejor opción. La máquina virtual de Java reparte automáticamente los hilos entre los procesadores disponibles del sistema, si éste estuviera equipado con más de uno. Esta mejora del rendimiento hizo que se incluyera el procesamiento paralelo como parte del sistema. Sin embargo, no se había previsto esta circunstancia cuando se diseñaron y se probaron los algoritmos de decisión, de modo que los cambios realizados para que el programa soportara varios hilos conllevan ciertas penalizaciones en el consumo de memoria y de procesador. En concreto, en el sistema actual, aplicando procesamiento paralelo: 1. Se pierde el primer nivel de la poda alfa-beta del árbol de jugadas. 2. Se multiplican las copias de objetos para mantener el proceso coherente. Esto deriva en que si el sistema se ejecuta en equipos con más de un procesador el rendimiento aumenta, pues el incremento de la rapidez en las operaciones compensa las dos penalizaciones anteriores. Sin embargo, si esta versión del algoritmo se ejecuta en una máquina de un único procesador, el rendimiento se degrada de una forma muy notable. Para solucionar este problema, se optó por el diseño de dos subsistemas: uno multihilo, dirigido a máquinas de varios procesadores, y otro multihilo dirigido a máquinas con un procesador. Las últimas versiones de la máquina virtual de Java compensan automáticamente el procesamiento paralelo entre los diferentes procesadores de la máquina. Esto tiene la ventaja de que los cambios para el multihilo no penalizan la ejecución cuando se utiliza un único procesador, y si se dispone de varios, se aprovecha al máximo el tiempo de CPU. El sistema detecta la configuración de la máquina y según ésta, decide si debe utilizar los procedimientos para uno o varios procesadores. Esto es posible gracias

44

Una aplicación para jugar al hex

a la implantación del patrón factoría en la construcción por polimorfismo de los objetos de la heurística. Para llegar a este diseño fue preciso realizar un pequeño estudio basado en el rendimiento de distintos sistemas para las dos versiones del algoritmo, como se describe a continuación.

5.3.1

Estudio práctico

Probar la aplicación con varios usuarios, en varios equipos y obtener resultados fiables de su ejecución es una tarea complicada en este caso, puesto que todavía muy pocas personas saben jugar al hex. Hacer pruebas de rendimiento es complejo por el gran número de partidas disponibles y por lo que varía el resultado en cada juego. Así, por ejemplo, el programa necesita menos tiempo para calcular su respuesta a una jugada débil que para responder a una jugada buena, de modo que los resultados de las ejecuciones son demasiado variables cuando interviene la mano humana. El diseño del sistema hace que sea muy sencillo crear nuevas aplicaciones y herramientas basadas en el juego, de modo que se optó por crear una aplicación específica para las pruebas. Esta aplicación, denominada Hextress, sólo dispone de una ventana donde muestra el resultado de un cierto número de pruebas previamente configuradas. Hextress sólo dispone de una ventana, donde muestra el número de procesadores detectados en el sistema y los resultados de las pruebas en segundos, ocultando los detalles de su comportamiento.

Figura 19. Hextress mostrando resultados

Las pruebas realizadas se identifican según la siguiente tabla: Prueba 1 2 3 4 5 6 7 8

Tablero 3 4 5 6

Algoritmo ejecutado Paralelo Secuencial Paralelo Secuencial Paralelo Secuencial Paralelo Secuencial

Estas pruebas consisten en dejar que el sistema juegue contra sí mismo para ciertos tableros, alternando la aplicación del algoritmo secuencial con la del paralelo. La idea es que el sistema ejecute las mismas operaciones en modo secuencial y en paralelo y las cronometre. Con los tiempos resultantes, se obtienen medidas fiables de los tiempos de procesado para cada opción, y se pueden comparar con más facilidad las diferentes opciones de ejecución.

Aspectos relevantes

45

De acuerdo a la tabla anterior, las pruebas típicas suponen la ejecución del modo demostración en tableros de dimensión de 3, 4, 5 y 6, alternando las versiones secuencial y paralela del algoritmo. Se pueden utilizar tableros mayores en las pruebas, pero los tiempos de procesamiento son elevados, y se pretendía que el programa se pudiera ejecutar rápidamente y que no comprometiera al usuario más de cinco minutos. Realizando las pruebas tanto en equipos con varios procesadores como en equipos con uno sólo podremos concluir qué versión del algoritmo es más eficiente en principio para cada configuración de hardware, y nos permitirá aportar pruebas y tiempos concretos que justifiquen el diseño del sistema. Se trata de responder a la pregunta ¿justifica el gasto adicional en operaciones y en consumo de memoria de la versión paralela del algoritmo aportando mejores tiempos de procesado? La respuesta, como se explica en la siguiente sección, es afirmativa.

Descripción de los equipos A continuación se detalla las configuraciones de los equipos que han participado en el estudio de casos, indicando el número de procesadores de cada uno de ellos. Los sistemas sombreados en gris tienen un único procesador, y sus resultados sirven para contrastar el resto. Id. 1 2 3 4 5 6 7 8 9 10

Nº 2 2 2 4 1 2 2 2 2 1

Procesador Modelo Frecuencia (GHz) Intel Core 2 Duo 2 Intel Core Duo 1,83 Intel Core 2 Duo 2,33 Intel Xeon 2,66 Intel Centrino 2 Intel Core Duo 2 Intel Core Duo 2 Intel Core Duo 1,83 Intel Core 2 Duo 1,86 AMD Athlon XP-M 1,2

Memoria (MB) 1024 1024 2048 2048 1024 1024 2048 1024 2048 256

Sistema operativo Mac OS 10.4.9 Mac OS 10.4.9 Mac OS 10.4.10 Mac OS 10.4.9 Windows XP SP 2 Mac OS 10.4.10 Mac OS 10.4.10 Windows XP SP 2 Windows XP SP 2 Windows XP SP 2

Resultados En el Apéndice A, al final de la memoria, se adjuntan las hojas de datos con los resultados de las experiencias llevadas a cabo debidamente clasificados. También pueden consultarse en formato de hoja de cálculo en el soporte digital que acompaña a esta memoria. A continuación se muestran una serie de gráficas que relacionan el tiempo de procesado de una partida completa con el tamaño del tablero de la partida en cuestión. Tiempos menores implican un mejor rendimiento del sistema. Para sistemas monoprocesador (equipados con un único microprocesador):

46

Una aplicación para jugar al hex

Figura 20. Gráfico de tiempos para sistemas de un único procesador

Se observan rendimientos ligeramente mejores para el algoritmo secuencial que el paralelo, cuyas diferencias tienden a aumentar conforme se incrementa el tamaño del tablero. La mayor parte de las pruebas se realizó con equipos de dos procesadores, o de dos núcleos, con los siguientes resultados (promedios):

Figura 21. Tabla de tiempos para sistemas de dos procesadores

Aspectos relevantes

47

Se observa una mejora muy significativa de los tiempos para tableros medianos cuando se utiliza el algoritmo paralelo. Por último, para los sisteºmas de cuatro procesadores, se obtuvieron los siguientes resultados:

Figura 22. Tabla de tiempos para sistemas de cuatro procesadores

5.3.2 Conclusiones Los datos recogidos en las diversas pruebas nos permiten extraer ciertas conclusiones interesantes. En primer lugar para tableros pequeños, de dimensión menor o igual a 4, se observa mejor rendimiento para los algoritmos secuenciales en todos los casos. Este margen de beneficio es muy amplio en dimensión 3 y algo más reducido para la dimensión. A partir de dimensión 5, sólo en los equipos monoprocesador se obtienen mejores rendimientos en la versión secuencial. Este mejor rendimiento del algoritmo secuencial para los tableros pequeños incluso en los equipos de varios procesadores se aprecia mejor en el siguiente diagrama, cuyo eje vertical utiliza la escala logarítmica para exagerar las diferencias:

48

Una aplicación para jugar al hex

Figura 23. Escala logarítimica para sistemas duales

Si el ordenador dispone de más de un procesador, en los tableros medios (de dimensión 5 y 6) se aprecian mejores rendimientos en los algoritmos paralelos. Por contra, si el ordenador sólo cuenta con un único procesador, los tableros medios siguen la progresión de los pequeños, y se observan mayores costes para el procesamiento paralelo que para el secuencial. Esto último es así porque la ejecución multihilo requiere más operaciones y más trabajo de la memoria, por lo que sólo compensa su uso cuando se dispone de más de un procesador que agilice la ejecución. Como síntesis, se puede concluir que el algoritmo paralelo es óptimo para entornos multiprocesadores, por lo que el gasto adicional de procesador y memoria se compensa gracias a la ejecución concurrente, mientras que el algoritmo secuencial es más eficiente para equipos basados en un único procesador. En un sentido más amplio, podemos considerar que construir algoritmos menos eficientes puede ser rentable si a cambio podemos utilizar las capacidades de procesamiento paralelo del sistema. Esta conclusión se ha utilizado como base a la implementación de las capacidades automáticas de procesamiento paralelo que se han comentado en esta sección: dependiendo de las características del sistema se aplica la versión secuencial o la concurrente mediante el uso de polimorfismo.

5.3.3 Otras posibilidades evaluadas Procesamiento superpuesto Cuando se evaluó la posibilidad de crear una versión de varios hilos se consideró también la posibilidad de comenzar a evaluar una jugada mientras se terminaban las operaciones con la anterior. Finalmente se descartó porque esta opción no resultaba práctica ni se adaptaba a las características del sistema.

Restricción del número de hilos

Aspectos relevantes

49

Para reducir la carga del sistema, se implementó una versión que mantenía sólo dos hilos en ejecución cada vez, dejando el resto en una cola. Aunque en principio esta opción podía parecer interesante, el gasto de esperar a cada par de procesos antes de lanzar otros dos, penalizaba mucho los tiempos de ejecución, por lo que se descartó.

5.4 Multiplataforma Uno de los objetivos planteados en las fases previas, tal y como se detalla en el Anexo 2, es que la aplicación pueda ejecutarse en los sistemas operativos más extendidos (Linux, Windows y Mac OS). La tecnología Java, utilizada en este proyecto, permite la ejecución de código sobre una máquina virtual. Esto permite generar un código intermedio que después es interpretado por “adaptadores” específicos de cada sistema sin que sea preciso realizar una compilación separada. Los componentes de la aplicación han sido desarrollados de manera que sea posible una integración adecuada en todos los sistemas. A continuación, se incluye una serie de capturas de pantalla que muestran a Hexodus ejecutándose sobre diferentes sistemas operativos o entornos gráficos. La aplicación funciona sobre Mac OS:

Figura 24. Hexodus sobre Mac OS X

También sobre Linux:

Figura 25. Hexodus sobre Linux con GNOME (Ubuntu)

50

Una aplicación para jugar al hex

Figura 26. Hexodus sobre Linux con KDE (Kubuntu)

Y sobre Microsoft Windows (2000 en adelante):

Figura 27. Hexodus sobre Windows 2000

5.5 Eficiencia de las estructuras A continuación se repasan algunas decisiones tomadas en torno a las estructuras de datos utilizadas, parte vital de este proyecto.

5.5.1 Mapas hash Java dispone de unas estructuras denominadas HashMap que vienen a ser una tabla basada en una clave hash. La ventaja de estas estructuras es que nos permiten relacionar objetos con arrays de otros objetos. Es decir, crear arrays cuyo índice es un objeto. El algoritmo H-Search se basa en crear caminos de celdas entre dos celdas, lo que en principio aconseja esta implementación. Sin embargo, para soportar el esquema, fue necesario crear una extensión del HashMap, adaptada a utilizar dos índices en vez de uno. La figura siguiente muestra una aproximación de esta idea.

Aspectos relevantes

51

Figura 28. Un esquema algo complicado

Sin embargo, el coste de las operaciones es muy elevado, pues el HashMap ocupa mucho espacio en memoria y los accesos son lentos para cierto número de elementos. Otra solución barajada fue la de representar los caminos entre dos celdas como un mapa de bits lógico de tres dimensiones. Sin embargo, esta opción tuvo que descartarse porque son necesarias ciertas operaciones adicionales en los contenedores de caminos, que a su vez precisan de otras operaciones y otros atributos especiales. Los HashMap necesitan disponer de un valor que pueda servir como identificador único de cada elemento que indexan, si se quiere que las operaciones sean rápidas. Así, es necesario dotar a cada objeto celda de un identificador. Sin embargo, si cada objeto tiene un identificador entero y único, puede utilizarse este identificador como índice en una tabla. Luego planteando una tabla de referencias a contenedores de caminos (llamados “rutas”), puede darse salida a la misma funcionalidad que el HashMap en tiempos inferiores al 40%, según las pruebas realizadas. ¿Cómo asignar un identificador a cada celda? Cada celda queda identificada en principio por la fila y la columna en el caso de las casillas, y por el punto cardinal que representa en el caso de los bordes. Así, pues para el caso de las celdas:

Indentificador = Fila " Dimensión + Columna Y en el caso de los bordes, se asignan a partir del último identificador ocupado por las casillas en orden creciente, según norte, sur, este y oeste.

5.5.2 Listas!y vectores Java dispone de un tipo de estructura denominada ArrayList, que es una lista abstracta donde pueden guardarse objetos en orden, recuperarlos y ejecutar operaciones con mucha rapidez. Los algoritmos son muy óptimos, aunque se pierde eficiencia en relación a los arrays tradicionales cuando se hacen operaciones como recorrer los índices. Así pues, en las partes donde se podía prescindir de estas estructuras con poca codificación adicional, así se ha hecho. En otras partes sencillamente no es viable por la cantidad de inserciones y eliminaciones de elementos. En algunos de estos casos se ha utilizado la función toArray(), que convierte la lista en un array convencional, que puede ser recorrido y accedido con mayor rapidez.

5.6 Simulación de jugadas El sistema utiliza un sistema algo novedoso al identificar cada jugada que debe probarse con una simulación. Esto permite llevar un control de las jugadas simuladas, el orden que ocupan en la partida, las estructuras a las que afectaron para deshacer los cambios... Este control permite reutilizar entre llamadas a simulaciones parte de las estructuras de la simulación anterior, deshaciendo los cambios convenientemente.

52

Una aplicación para jugar al hex

5.7 Simplificación de celdas Un conjunto de casillas vecinas ocupadas por el mismo jugador pueden considerarse como una sola. Desde el punto de vista del juego esto no es estrictamente cierto, pero sí desde el punto de vista de la heurística. Aunque parezca muy intuitivo, simplificar las casillas es una tarea ciertamente complicada, sobre todo porque se debe almacenar la traza de los cambios realizados para revertirlos al deshacer las simulaciones, siendo necesario observar multitud de excepciones y circunstancias diferentes al realizar estas operaciones. La idea principal es que, por ejemplo, dada esta situación:

Figura 29. Una posición en el tablero

Si representamos el tablero anterior como un grafo, tendríamos:

Figura 30. El equivalente en grafo

Y teniendo en cuenta que dos casillas vecinas pueden considerarse como una sola, esto equivaldría a:

Figura 31. Simplificando casillas

Existen situaciones donde se puede obtener un beneficio muy significativo de esta propiedad, sirva como ejemplo la figura siguiente:

Aspectos relevantes

53

Figura 32. Eliminando tres casillas

Simplificar las casillas reduce el número de celdas que deben evaluarse, y en consecuencia incrementa la eficiencia de los algoritmos. En la práctica, al ocupar una celda del tablero en la simulación de una jugada, se guarda una referencia a la celda que se ha eliminado, y se sustituye por la nueva. Acto seguido, se copian en la nueva las referencias de las vecinas de la celda sustituida, siempre que no existieran previamente. Cuando se deshace la simulación, se vuelve a incorporar la celda eliminada al tablero y se restablecen las conexiones.

5.8 Otros aspectos interesantes 5.8.1 Diferentes modos de juego El sistema permite ejecutar el juego en distintos modos. Así, para cada nueva partida se ofrecen las opciones: A. Jugar un humano contra el ordenador, pudiendo configurar qué jugador comenzará moviendo. B. Jugar un humano contra otro humano, deshabilitando la respuesta automática para este modo, y permitiendo a ambos contendientes enfrentarse con el programa ejerciendo de árbitro. C. Modo de demostración. Este modo permite observar al ordenador jugando contra sí mismo, lo cual resulta interesante a efectos de estudiar la calidad de la heurística. En los dos primeros modos, el sistema dispone de la opción de sugerir la mejor jugada según su criterio. El jugador que recibe la sugerencia puede aceptarla o bien ejecutar otro movimiento a su elección.

5.8.2 Sugerir jugadas El jugador en posesión del turno puede solicitar al sistema que sugiera cuál es, a su juicio, la mejor jugada. La jugada aparece señalada en el tablero y el usuario puede aceptarla o bien ejecutar otro movimiento a su elección.

5.8.3 Regla de intercambio El sistema permite, al crear una partida, activar o desactivar la regla de intercambio. Esto permite al jugador decidir si intercambia el primer movimiento, haya sido generado por el ordenador o por otro humano. Si no desea intercambiar el movimiento, el segundo jugador puede ejecutar cualquier otro sobre el mismo tablero.

54

Una aplicación para jugar al hex

5.8.4 Tablas de aperturas El primer movimiento es generado mediante una tabla estática que contiene los mejores movimientos, diferenciando el caso de que la regla de intercambio esté activada o no. Con las características actuales, el sistema juega siempre en la casilla central si la regla de intercambio está desactivada. En caso contrario, ejecuta una buena jugada menos tentadora para el contrario, para que éste no la intercambie pero al mismo tiempo pueda mantener cierta ventaja.

5.8.5 Nivel de juego configurable El sistema permite configurar el nivel de juego de la heurística al crear una nueva partida y durante el transcurso de las mismas. La selección del nivel se traduce internamente en una mayor o menor profundidad del árbol de búsqueda y detalle de las búsquedas. En la aplicación, el usuario puede configurar el nivel entre “modo normal” y “modo experto”. El modo normal trabaja con una altura en el árbol de búsqueda de 2 y el modo experto con 3. Estos valores aportan resultados muy buenos. El modo experto es útil para finales de partida y tableros pequeños donde existen ciertas sutilezas que el modo normal no llega a detectar.

5.8.6 Interfaz gráfica de usuario Se calcula que más de el 90% de las pruebas de prototipado y desarrollo se hicieron en modo texto, minimizando la dependencia de la interfaz gráfica. La implantación de la misma se ha realizado respetando la independencia de los módulos lo máximo posible, de manera que la lógica interna del programa sea independiente. Esto permitirá, llegado el caso, utilizar el motor de decisión sobre otra interfaz, o implantarlo en un servidor de juegos que cuente con su propio frontal gráfico. Se ha prestado especial atención en el diseño de los gráficos de la pantalla principal del juego para lograr un entorno atractivo, sencillo y lo más intuitivo posible.

5.9 Dificultades A lo largo de este desarrollo se han presentado multitud de problemas y dificultades. El resultado global es el resultado de un ejercicio continuo de aportar soluciones y de contrastar alternativas. La primera dificultad fue la falta de documentación sobre el juego. Esta situación ha ido corrigiéndose con el paso del tiempo, aunque queda mucho camino por recorrer. A la falta de referencias sobre el juego se une la falta de documentación explícita sobre implementación de programas capaces de jugar al hex. Mientras existen, por la propia tradición informática, infinidad de estudios sobre el ajedrez o las damas, que exponen con claridad cómo diseñar buenas heurísticas e incluso ejemplos, apenas hay disponible nada sobre el hex, más que los dos o tres artículos a los que nos hemos referido. En el estudio inicial se reflejó como crítica la gestión de la complejidad. Sin embargo, la división en módulos, el propio paradigma orientado a objetos y las herramientas de Java, han reducido este problema a lo anecdótico.

Conclusiones

55

6 Conclusiones Los métodos de búsqueda basados en árbol se utilizan ampliamente en juegos de computador y se tratan con detalle en la mayor parte de la bibliografía sobre Inteligencia Artificial (IA). Sin embargo, cabe señalar que estrictamente, estos métodos poco o nada tienen de IA, sino que son más bien métodos matemáticos deterministas que simulan una forma de proceder humana, aunque formalizada para el caso de las máquinas. Los avances más interesantes en el campo de los juegos vienen precisamente en la aplicación de técnicas de IA en la resolución del juego. Dichas técnicas se han implementado satisfactoriamente en el programa Blondie24, que utiliza una red de neuronas artificial para jugar a las damas, superando a los mejores jugadores humanos. De hecho hace tiempo que no se permite a los ordenadores competir contra humanos en los torneos de damas. El hex es un juego con una naturaleza muy ligada a las matemáticas, y una bella muestra de cómo estructuras sencillas pueden combinarse y dar lugar a sistemas complejos. La mayor dificultad cuando nos enfrentamos a la computación de este juego es, precisamente, la de eliminar su máscara superficial de entretenimiento y adentrarnos en su desbordante lógica subyacente. La idea que ha permitido que los ordenadores puedan jugar al hex se escapa del ámbito de los juegos y la propia inteligencia artificial para enraizarse sólidamente en las matemáticas. A diferencia de la mayoría de los juegos abstractos computables, los métodos que nos permiten elegir el mejor movimiento en el hex no se basan en las utilidades ni en los valores de las casillas, sino en el estudio de los grafos que describen el juego a nivel interno, en plena compenetración con las matemáticas. La ingeniería y su sentido más práctico se mezcla en la mágica idea de tratar las conexiones en estos grafos como circuitos eléctricos, y permite hacer posible el complicado sueño de jugar al hex contra una máquina. La idea clave es la unión. Desde la arquitectura del computador, las interfaces gráficas, la orientación a objetos o las estructuras de más bajo nivel de la ingeniería informática; hasta la teoría de juegos, los grafos o el álgebra de las ecuaciones en las matemáticas, pasando por la teoría de circuitos de la física… todas las disciplinas se unen y trabajan en sincronía en la compleja tarea de encontrar la mejor jugada en un juego que se aprende en unos segundos.

6.1 Líneas de trabajo futuras Como en la mayoría de este tipo de proyectos, tan abiertos a mejoras y cambios, es fácil que el sistema no llegue a terminarse nunca. Entre las ideas más interesantes para futuras versiones figuran: •

Continuar obteniendo datos sobre procesamiento paralelo que permitan su comparación con versiones posteriores.

56

Una aplicación para jugar al hex



La posibilidad de añadir un sistema de aprendizaje basado en redes de neuronas artificiales, lo que permitiría que el sistema evolucionara con el tiempo.



Estudiar la posibilidad de implantar un sistema de cálculo distribuido entre varios equipos.



Estudiar cómo pueden reducirse algunas estructuras a un nivel más bajo para mejorar el rendimiento.



Formalizar protocolos de comunicaciones con otros programas. Es posible que se intente liderar el esfuerzo de la comunidad de desarrolladores por llegar a un consenso en la definición de un protocolo que permita a las aplicaciones comunicarse y jugar entre ellas.

Respecto a cuestiones menores, más relacionadas con funciones del software, destacan las siguientes: •

Funciones de deshacer y rehacer movimientos, permitiendo avanzar y retroceder por el historial de jugadas. En el hex implantar esta característica no es en absoluto trivial, y de hecho la mayoría de los programas no la incluyen.



Más rapidez en la decisión de las jugadas. Sería necesario estudiar los algoritmos y ver cómo se pueden reutilizar ciertos cálculos.



Permitir guardar y abrir partidas. Sería interesante utilizar un formato XML para esta característica.



Prever que la interfaz se muestre en el idioma del sistema, y permitir generar ficheros de traducciones.

Reconocimientos

57

7 Reconocimientos Este proyecto comenzó a gestarse ya a finales del año 2005. Que este complicado viaje haya terminado en buen puerto es mérito colectivo de muchas personas. René Reynaga sugirió la idea de estudiar este juego en concreto, aportó su conocimiento del mismo e infinidad de conceptos interesantes de los que este proyecto se ha nutrido. Luis Alonso Romero proporcionó bibliografía y referencias muy valiosas sobre Inteligencia Artificial y resultó de gran ayuda a lo largo de este largo proyecto. Rafael García–Bermejo aportó ideas muy interesantes como la posibilidad de realizar un procesamiento con varios hilos y la optimización de algunas estructuras para acortar los tiempos de ejecución. Asimismo, atendió mis consultas con excelente disposición y enorme paciencia. Eduardo Alberto de la Peña se convirtió por unos días en un auténtico tutor virtual aportando un método muy interesante para calcular la resistencia de los circuitos eléctricos. Eladio Sanz y Marcelino Zazo colaboraron con una disposición excelente en el estudio de los algoritmos que permitieran generalizar la resolución de los circuitos eléctricos. Carlos Polo es el coautor de la primera versión de la biblioteca de clases para hex escrita en C++ [8] y que sirvió como base a la realización de este proyecto. Además, se prestó a discutir ciertos aspectos metodológicos y aportó sus valiosas opiniones. Marián Torrecilla obtuvo tiempo de donde no lo había para revisar y corregir una buena parte de la memoria aportando interesantes comentarios y anotaciones,

7.1 Beta testers Las siguientes personas colaboraron en el desarrollo de este sistema permitiendo, con una excelente disposición, que la aplicación Hextress se ejecutara en sus equipos y enviaron de vuelta los resultados de la ejecución, permitiendo su tabulación y su posterior análisis. Una buena parte de este trabajo no habría sido posible sin ellos: •

Inés López



Olga Molina



Carlos Polo



Pablo Ramos



Roberto Sánchez



Marián Torrecilla

Referencias

59

8 Referencias A continuación se muestra un listado de la documentación referida en el texto: [1]

Vadim V. Anshelevich. A hierarchical approach to computer Hex. Artificial Intelligence, 2001.

[2]

Edward A. Bender. Mathematical Methods in Artificial Intelligence. IEEE Computer Society Press, 1996.

[3]

S. Even; R. E. Tarjan. A Combinatorial Problem Which is Complete in Polynomial Space. Journal of the Association for Computing Machinery, 23, October 1976.

[4]

Stefan Reisch. Hex ist PSPACE-vollstandig. Acta Informatica, 15:167– 191, 1981.

[5]

Jack van Rijswijck. Computer Hex: Are Bees Better Than Fruitflies? Master’s thesis, University of Alberta, 2000.

[6]

Thomas Maarup. Hex: Everything You Always Wanted to Know About Hex But Were Afraid to Ask. University of Southern Denmark, 2005.

[7]

Morgenstern, Oskar; John von Neumann: The Theory of Games and Economic Behavior. Princeton University Pressm, 1947.

[8]

C. Polo; P. Torrecilla. Biblioteca de clases para hex, Universidad de Salamanca, 2006.

[9]

S. J. Russell; P. Norvig. Inteligencia artificial. Un enfoque moderno. Pearson Educación, 2004.

[10]

Nils J. Nilsson. Inteligencia artificial. Una nueva síntesis. McGraw Hill, 2001.

[11]

Vadim V. Anshelevich. The Game of Hex. An Automatic Theorem Proving Approach to Game Programming, Artificial Intelligence, 2000.

[12]

D. Gale. The game of Hex and the Brouwer fixed-point theorem. American Math. Número 86 (1979) 818–827

[13]

Paul A. Tipler. Física para la ciencia y la tecnología. Reverté, 2001.

[14]

C.E. Shannon. Computers and automata, Proceedings of Institute of Radio Engineers 41 (1953) 1234–1241.

8.1 Otra bibliografía Otras referencias bibliográficas consultadas durante la elaboración del proyecto: [15]

I. Jacobson, G. Booch, J. Rumbaugh. El proceso unificado de desarrollo de software. Adisson-Wesley, 2000.

[16]

I. Jacobson, G. Booch, J. Rumbaugh. El lenguaje unificado de modelado: manual de referencia. Adisson-Wesley, 2000.

[17]

A. Durán, B. Bernárdez. Metodología para la elicitación de Requisitos de Sistemas Software. Universidad de Sevilla, 2002.

[18]

A. Durán, B. Bernárdez. Metodología para el Análisis de Requisitos de Sistemas Software. Universidad de Secilla, 2001.

60

Una aplicación para jugar al hex

[19]

Judy Bishop. Java: fundamentos de programación. Addison-Wesley, 1999.

[20]

Bruce Eckel. Piensa en Java. Pearson Educación, 2002.

8.2 Recursos en Internet De todas las referencias consultadas, se muestran a continuación las más interesantes. Se ha creído conveniente mostrar en cada caso una breve descripción sobre el sitio referido. 

Hexwiki. Sitio tipo wiki con información muy detallada sobre el hex. http://hexwiki.org [Última vez visitado: 25 de junio de 2007].



Interactive Multipurpose Server. Sitio web con gran cantidad de documentación sobre matemáticas y recursos, como applets para resolución de sistemas de ecuaciones, sisemas de trazado automáticos… http://wims.unice.fr [Última vez visitado: 13 de marzo de 2007].



Wolfram MathWorld. Completo sitio web con multitud de recursos sobre matemáticas. http://mathworld.wolfram.com [Última vez visitado: 10 de abril de 2007].



Numerical recipes. Colección de libros online que abordan la computación de los diferentes métodos matemáticos. http://www.nrbook.com [Última vez visitado: 15 de mayo de 2007].



Métodos numéricos. Libro online con información sobre métodos numéricos. http://www.uv.es/~diaz/mn/fmn.html [Última vez visitado: 15 de marzo de 2007].



Curso de computación. Recopilación de datos teóricos sobre resolución de sistemas de ecuaciones lineales. http://www.uv.es/~diaz/mn/fmn.html [Última vez visitado: 10 de marzo de 2007].



Wikipedia. Enciclopedia libre on-line (Utilizada como fuente secundaria). http://wikipedia.org [Última vez visitado: 17 de junio de 2007].



Mac Project planning software. En este artículo (en inglés) se encontró información muy útil sobre ese tipo de aplicaciones para entornos Mac OS. http://www.anyware.co.uk/2005/2006/03/23/mac-project-planning-software [Última vez visitado: 4 de junio de 2007].



JSience. Librería científica para Java. http://jscience.org [Última vez visitado: 5 de junio de 2007]

Get in touch

Social

© Copyright 2013 - 2024 MYDOKUMENT.COM - All rights reserved.