Dagor es un framework de Python que permite programar diversos tipos de jugadores que compiten entre sí para ganar un juego combinacional.
La palabra Dagor significa “batalla” en el idioma sindarin (conocido también como élfico gris), creado por el escritor británico J. R. R. Tolkien, autor del “Señor de los anillos”. |
En teoría de juegos, un juego combinacional tiene las siguientes características:
-
Siempre hay dos jugadores que toman turnos para tirar de manera alternada.
-
No hay elementos aleatorios como dados o cartas barajadas.
-
Ambos jugadores tienen información perfecta (no hay información oculta).
-
El juego es finito — debe terminar eventualmente.
-
Usualmente el último jugador en tirar gana.
Muchas de las ideas de este framework fueron tomadas del sitio Gamesman elaborado por Dan Garcia de UC Berkeley.
1. Descarga
El módulo de dagor
se puede descargar de la siguiente liga:
-
dagor.zip versión 1.0.3, 3 de noviembre, 2022.
Solo se requiere descomprimir el ZIP y copiar el archivo dagor.py
al directorio de trabajo.
2. D10 (destino 10)
2.1. Reglas
Piezas y tablero: Este juego se juega en un tablero de 1 por 10. La posición inicial es un tablero vacío.
Para tirar: Los jugadores se alternan colocando una o dos piezas en las localidades desocupadas de más a la izquierda del tablero. En este juego no hay distinción respecto a las piezas de cada jugador. Nos referiremos al primer jugador en tirar como izquierda, y al otro jugador como derecha.
Para ganar: El primer jugador en colocar la décima pieza en el tablero gana.
2.2. Ejemplo
Tablero inicial: _ _ _ _ _ _ _ _ _ _ | | | | | | | | | | | | | | | | | | | | | | |_|_|_|_|_|_|_|_|_|_| 1 2 3 4 5 6 7 8 9 10 Izquierda tira 2 _ _ _ _ _ _ _ _ _ _ | | | | | | | | | | | |*|*| | | | | | | | | |_|_|_|_|_|_|_|_|_|_| 1 2 3 4 5 6 7 8 9 10 Derecha tira 2 _ _ _ _ _ _ _ _ _ _ | | | | | | | | | | | |*|*|*|*| | | | | | | |_|_|_|_|_|_|_|_|_|_| 1 2 3 4 5 6 7 8 9 10 Izquierda tira 2 _ _ _ _ _ _ _ _ _ _ | | | | | | | | | | | |*|*|*|*|*|*| | | | | |_|_|_|_|_|_|_|_|_|_| 1 2 3 4 5 6 7 8 9 10 Derecha tira 1 _ _ _ _ _ _ _ _ _ _ | | | | | | | | | | | |*|*|*|*|*|*|*| | | | |_|_|_|_|_|_|_|_|_|_| 1 2 3 4 5 6 7 8 9 10 Izquierda tira 2 _ _ _ _ _ _ _ _ _ _ | | | | | | | | | | | |*|*|*|*|*|*|*|*|*| | |_|_|_|_|_|_|_|_|_|_| 1 2 3 4 5 6 7 8 9 10 Derecha tira 1 _ _ _ _ _ _ _ _ _ _ | | | | | | | | | | | |*|*|*|*|*|*|*|*|*|*| |_|_|_|_|_|_|_|_|_|_| 1 2 3 4 5 6 7 8 9 10 Derecha resulta ganador
2.3. Utilizando Dagor
El siguiente programa permite a un jugador humano jugar contra un jugador estratégico autónomo:
from dagor import JuegoD10, JugadorD10Interactivo, JugadorD10Estrategico (1)
jugador1 = JugadorD10Interactivo('Humano') (2)
jugador2 = JugadorD10Estrategico('Máquina') (3)
juego = JuegoD10(jugador1, jugador2) (4)
juego.inicia() (5)
1 | Importamos del módulo dagor las clases que estaremos utilizando en nuestro programa. |
2 | Creamos un jugador de D10 interactivo. |
3 | Creamos un jugador de D10 estratégico autónomo. |
4 | Creamos un juego de D10 y le enviamos los dos jugadores previamente creados. |
5 | Comenzamos el juego. |
Para correrlo, desde la terminal teclear:
$ python ejemplo1.py
2.4. Clases disponibles
Clase | Descripción |
---|---|
|
Clase que representa un juego de D10 (Destino 10). |
|
Toda clase que represente un jugador del juego D10 debe heredar de esta clase. |
|
Jugador de D10 que tira de manera aleatoria. |
|
Jugador de D10 que tira con una estrategia. |
|
Jugador de D10 controlado a partir de una interfaz de usuario en modo texto. |
2.5. Programando un nuevo jugador
Para escribir el código de un nuevo jugador solo se requiere:
-
Definir una clase que herede del tipo de jugador deseado (
JugadorD10
en nuestro caso). -
Proporcionar la implementación de los siguientes dos métodos abstractos:
-
heuristica(self, posicion)
: Función heurística que puede utilizar un jugador para rankear la posición enviada como argumento. -
tira(self, posicion)
: Invocada automáticamente por el objeto controlador del juego para determinar el tiro de este jugador. Debe devolver un tiro válido a partir de la posición enviada como argumento.
-
El siguiente código muestra un jugador de D10 cuya estrategia es tirar un 1 siempre, excepto si existe un tiro ganador con un 2:
from dagor import JuegoD10, JugadorD10, JugadorD10Aleatorio
class JugadorD10SiempreTiraUno(JugadorD10): (1)
def heuristica(self, posicion):
return self.triunfo(posicion) == self.simbolo (2)
def tira(self, posicion):
posibles = self.posiciones_siguientes(posicion) (3)
for p in posibles: (4)
if self.heuristica(p):
return p
return posibles[0] (5)
jugador1 = JugadorD10SiempreTiraUno('OnlyOne')
jugador2 = JugadorD10Aleatorio('RandomBoy')
juego = JuegoD10(jugador1, jugador2)
juego.inicia()
1 | Esta es la manera correcta en Python de definir una clase JugadorD10SiempreTiraUno que hereda de otra JugadorD10 . |
2 | Esta función heurística devuelve True si posicion resulta en un tiro ganador para este jugador. De otra forma regresa False . |
3 | Obtenemos todas todas las posiciones posibles que puede haber en un juego posteriores a posicion . |
4 | Busca si en esas posibles posiciones existe un tiro ganador. |
5 | De lo contrario, tira siempre la primera posición, la cual siempre es el tiro con un 1. |
2.6. Sobre posicion
La documentación en línea indica la estructura interna que tiene el parámetro posicion
para los métodos definidos en el ejemplo anterior. Para consultarla, hay que correr el interprete de Python. Desde la línea de comando teclear:
python
En el shell de Python, debemos hacer lo siguiente:
>>> from dagor import JugadorD10 (1)
>>> help(JugadorD10) (2)
1 | Importamos la clase de la cual heredamos en el ejemplo de arriba. |
2 | Solicitamos la ayuda en línea. |
La salida debe ser algo así:
Help on class JugadorD10 in module dagor: class JugadorD10(Jugador) | Toda clase que represente un jugador del juego D10 | debe heredar de esta clase. | | La posición que maneja un juego de D10 es una tupla | de la siguiente forma: | | (J, S) | | En donde: | | J: turno actual (nombre del jugador que se indicó al | momento de crearlo) | S: suma hasta el momento (0 al 10) | | Por ejemplo: | | ('Alfa', 5) | | Esta posición indica que el jugador actual es Alfa y que | la suma actual es 5.
2.7. Opciones de inicia
El método inicia
de la clase Juego
(y sus subclases, como JuegoD10
) comienza un encuentro entre los dos jugadores provistos al momento en el que se creó el juego. El encuentro tendrá un cierto número de juegos determinado por el valor del parámetro opcional veces
. Cada jugador tira primero de manera alternada en todos los juegos del encuentro. Si el parámetro opcional delta_max
tiene un valor mayor a cero arroja la excepción de TiemploLimiteExcedido
si el tiempo que tarda el tiro de algún jugador excede delta_max
segundos. Al final despliega un resumen del encuentro si veces
es mayor a 1.
En el siguiente ejemplo se inicia un encuentro de 10 juegos en donde el tiro de cada jugador debe durar 2 segundos o menos.
juego.inicia(veces=10, delta_max=2)
2.8. Propiedades y funciones de un jugador
Cualquier instancia de las subclases de la clase Jugador
(incluyendo JugadorD10
y sus subclases) tienen los siguientes métodos y propiedades:
Nombre | Descripción |
---|---|
|
Constructor que inicializa un jugador con |
|
Método que convierte este jugador a una cadena de caracteres. El formato usado es: |
|
Método que devuelve todas todas las posiciones posibles que puede haber en un juego posteriores a |
|
Método que devuelve el símbolo del jugador que resulta ganador a partir de |
|
Propiedad con el nombre de este jugador. |
|
Propiedad con el símbolo (o nombre si el símbolo no existe) de este jugador. |
|
Propiedad con la referencia al oponente de este jugador. Útil para obtener el nombre y símbolo del jugador contrario, por ejemplo: |
3. SuperGato
3.1. Reglas
Piezas y tablero: Este juego se juega en un tablero rectangular de n renglones por m columnas, donde 3 ≤ n ≤ 10, 3 ≤ m ≤ 10.
Para tirar: De manera similar al juego de gato, los jugadores alternan su turno colocando su símbolo (X
o O
) en cualquier localidad vacía del tablero.
Para ganar: El jugador que logre colocar su símbolo en tres localidades consecutivas (horizontal o verticalmente) gana. Si nadie ha ganado y el tablero está completamente lleno, entonces es un empate.
3.2. Clases disponibles
Clase | Descripción |
---|---|
|
Clase que representa un juego de SuperGato. Al crear una instancia de esta clase, es necesario indicar el número de renglones y columnas del tablero como tercer y cuarto argumento. |
|
Toda clase que represente un jugador del juego SuperGato debe heredar de esta clase. |
|
Jugador de SuperGato que tira de manera aleatoria. |
|
Jugador de SuperGato que tira con una estrategia. |
|
Jugador de SuperGato controlado a partir de una interfaz de usuario en modo texto. |
4. Orugas
4.1. Reglas
Piezas y tablero: Este juego se juega en un tablero rectangular de n renglones por m columnas, donde 4 ≤ n ≤ 10, 4 ≤ m ≤ 10. Inicialmente cada jugador ocupa una localidad del tablero (la cabeza de la oruga), determinada de manera aleatoria.
Para tirar: Cada jugador controla una oruga que crece rápidamente a partir de su cabeza. Durante el turno de un jugador, éste debe seleccionar una localidad contigua vacía en la que pueda crecer la cabeza de su oruga. La oruga puede crecer hacia la localidad de arriba, abajo, izquierda o derecha, mas no en diagonal. Es posible crecer saliéndose de una orilla del tablero y llegar a su correspondiente localidad opuesta.
Para ganar: Un jugador gana cuando su oponente ya no tenga una localidad donde crecer.
En el tablero, el primer jugador usa el símbolo B
(blanco) como cabeza y b
para el resto del cuerpo. El segundo jugador usa el símbolo N
(negro) como cabeza y n
para el resto del cuerpo.
4.2. Clases disponibles
Clase | Descripción |
---|---|
|
Clase que representa un juego de Orugas. Al crear una instancia de esta clase, es necesario indicar el número de renglones y columnas del tablero como tercer y cuarto argumento. |
|
Toda clase que represente un jugador del juego Orugas debe heredar de esta clase. |
|
Jugador de Orugas que tira de manera aleatoria. |
|
Jugador de Orugas controlado a partir de una interfaz de usuario en modo texto. |