Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
*.pyc
*.pyc
myvenv/
.venv/
99 changes: 39 additions & 60 deletions juegos_simplificado.py
Original file line number Diff line number Diff line change
@@ -1,68 +1,70 @@
"""
Modulo para las clases básicas para realizar un jkuego de forma muy simplificada
Modulo para las clases básicas para realizar un juego de forma muy simplificada

Vamos a usar una orientación funcional en este modulo

"""

from random import shuffle



class ModeloJuegoZT2:
"""
Clase abstracta para juegos de suma cero, por turnos, dos jugadores.

Se asumen que los jugadores son 1 y -1

"""

def inicializa(self):
"""
Inicializa el estado inicial del juego y el jugador
que comienza (típicamente el primero)

devuelve: (s0, j) donde s0 es el estado inicial y j el jugador

"""
raise NotImplementedError("Hay que desarrollar este método, pues")

def jugadas_legales(self, s, j):
"""
Devuelve una lista con las jugadas legales para el jugador j
en el estado s

"""
raise NotImplementedError("Hay que desarrollar este método, pues")
raise NotImplementedError("Hay que desarrollar este método, pues")

def transicion(self, s, a, j):
"""
Devuelve el estado que resulta de realizar la jugada a en el estado s
para el jugador j

"""
raise NotImplementedError("Hay que desarrollar este método, pues")

def terminal(self, s):
"""
Devuelve True si es terminal el estado actual,

"""
raise NotImplementedError("Hay que desarrollar este método, pues")

def ganancia(self, s):
"""
Devuelve la ganancia para el jugador 1 en el estado terminal s

"""
raise NotImplementedError("Hay que desarrollar este método, pues")


def juega_dos_jugadores(juego, jugador1, jugador2):
"""
Juega un juego de dos jugadores

juego: instancia de ModeloJuegoZT
jugador1: función que recibe el estado y devuelve la jugada
jugador2: función que recibe el estado y devuelve la jugada

"""
s, j = juego.inicializa()
while not juego.terminal(s):
Expand All @@ -75,52 +77,39 @@ def juega_dos_jugadores(juego, jugador1, jugador2):
def minimax(juego, estado, jugador):
"""
Devuelve la mejor jugada para el jugador en el estado

"""
j = jugador

def max_val(estado, jugador):
if juego.terminal(estado):
return j * juego.ganancia(estado)
v = -1e10
for a in juego.jugadas_legales(estado, jugador):
v = max(
v,
min_val(
juego.transicion(estado, a, jugador),
-jugador
)
)
v = max(v, min_val(juego.transicion(estado, a, jugador), -jugador))
return v

def min_val(estado, jugador):
if juego.terminal(estado):
return j * juego.ganancia(estado)
v = 1e10
for a in juego.jugadas_legales(estado, jugador):
v = min(
v,
max_val(
juego.transicion(estado, a, jugador),
-jugador
)
)
v = min(v, max_val(juego.transicion(estado, a, jugador), -jugador))
return v

return max(
juego.jugadas_legales(estado, jugador),
key=lambda a: min_val(
juego.transicion(estado, a, jugador),
-jugador
)
)

key=lambda a: min_val(juego.transicion(estado, a, jugador), -jugador),
)


def alpha_beta(juego, estado, jugador, ordena=None):
"""
Devuelve la mejor jugada para el jugador en el estado

"""
j = jugador

def max_val(estado, jugador, alpha, beta):
if juego.terminal(estado):
return j * juego.ganancia(estado)
Expand All @@ -132,18 +121,13 @@ def max_val(estado, jugador, alpha, beta):
shuffle(jugadas)
for a in jugadas:
v = max(
v,
min_val(
juego.transicion(estado, a, jugador),
-jugador,
alpha, beta
)
v, min_val(juego.transicion(estado, a, jugador), -jugador, alpha, beta)
)
if v >= beta:
return v
alpha = max(alpha, v)
return v

def min_val(estado, jugador, alpha, beta):
if juego.terminal(estado):
return j * juego.ganancia(estado)
Expand All @@ -155,18 +139,13 @@ def min_val(estado, jugador, alpha, beta):
shuffle(jugadas)
for a in jugadas:
v = min(
v,
max_val(
juego.transicion(estado, a, jugador),
-jugador,
alpha, beta
)
v, max_val(juego.transicion(estado, a, jugador), -jugador, alpha, beta)
)
if v <= alpha:
return v
beta = min(beta, v)
return v

jugadas = list(juego.jugadas_legales(estado, jugador))
if ordena:
jugadas = ordena(jugadas)
Expand All @@ -175,7 +154,7 @@ def min_val(estado, jugador, alpha, beta):
return max(
jugadas,
key=lambda a: min_val(
juego.transicion(estado, a, jugador),
-jugador,
-1e10, 1e10
))
juego.transicion(estado, a, jugador), -jugador, -1e10, 1e10
),
)

111 changes: 73 additions & 38 deletions minimax.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,26 @@
5- Tablas de transposicion
6- Trazabilidad
"""

from random import shuffle
from time import time


def negamax(
juego, estado, jugador,
alpha=-1e10, beta=1e10, ordena=None,
d=None, evalua=None,
transp={}, traza=[]
):
juego,
estado,
jugador,
alpha=-1e10,
beta=1e10,
ordena=None,
d=None,
evalua=None,
transp={},
traza=[],
):
"""
Devuelve la mejor jugada para el jugador en el estado

Parametros
----------
juego (ModeloJuegoZT): Modelo del juego
Expand All @@ -29,39 +37,41 @@ def negamax(
beta (float): Limite superior
ordena (function:) Funcion de ordenamiento
si None, ordena aleatoriamente
d (int): Profundidad.
d (int): Profundidad.
Si None, busca hasta el final
evalua: function de evaluación
Siempre evalua para el jugador 1
transp (dict): Tabla de transposición
traza (list): Trazabilidad

Regresa
-------
tuple: (lista mejores jugadas, valor)

"""
if d != None and evalua == None:
if d is not None and evalua is None:
raise ValueError("Se necesita evalua si d no es None")
if type(ordena) != type(None) and type(ordena) != type(lambda x: x):
if type(ordena) is not type(None) and type(ordena) is not type(lambda x: x):
raise ValueError("ordena debe ser una función")
if type(evalua) != type(None) and type(evalua) != type(lambda x: x):
if type(evalua) is not type(None) and type(evalua) is not type(lambda x: x):
raise ValueError("evalua debe ser una función")
if type(transp) != dict:
if type(transp) is not dict:
raise ValueError("transp debe ser un diccionario")
if type(traza) != list:
if type(traza) is not list:
raise ValueError("traza debe ser una lista")

hash_estado = estado.tobytes()

if juego.terminal(estado):
return [], jugador * juego.ganancia(estado)
if d == 0:
return [], jugador * evalua(estado)
if d != None and estado in transp and transp[estado][1] >= d:
return [], transp[estado][0]
if d is not None and hash_estado in transp and transp[hash_estado][1] >= d:
return [], transp[hash_estado][0]

v = -1e10
jugadas = list(juego.jugadas_legales(estado, jugador))
if ordena != None:
if ordena is not None:
jugadas = ordena(jugadas, jugador)
else:
shuffle(jugadas)
Expand All @@ -71,9 +81,16 @@ def negamax(
jugadas = [a_pref] + [a for a in jugadas if a != a_pref]
for a in jugadas:
traza_actual, v2 = negamax(
juego, juego.transicion(estado, a, jugador), -jugador,
-beta, -alpha, ordena, d if d == None else d - 1,
evalua, transp, traza
juego,
juego.transicion(estado, a, jugador),
-jugador,
-beta,
-alpha,
ordena,
d if d is None else d - 1,
evalua,
transp,
traza,
)
v2 = -v2
if v2 > v:
Expand All @@ -84,40 +101,58 @@ def negamax(
break
if v > alpha:
alpha = v
transp[estado] = (v, d)
return [mejor] + mejores, v
transp[hash_estado] = (v, d)
return [mejor] + mejores, v


def jugador_negamax(
juego, estado, jugador, ordena=None, d=None, evalua=None
):
def jugador_negamax(juego, estado, jugador, ordena=None, d=None, evalua=None):
"""
Funcion burrito para el negamax

"""
traza, _ = negamax(
juego=juego, estado=estado, jugador=jugador,
alpha=-1e10, beta=1e10, ordena=ordena, d=d,
evalua=evalua, transp={}, traza=[])
juego=juego,
estado=estado,
jugador=jugador,
alpha=-1e10,
beta=1e10,
ordena=ordena,
d=d,
evalua=evalua,
transp={},
traza=[],
)
return traza[0]


def minimax_iterativo(
juego, estado, jugador, tiempo=10,
ordena=None, d=None, evalua=None,
):
juego,
estado,
jugador,
tiempo=10,
ordena=None,
d=None,
evalua=None,
):
"""
Devuelve la mejor jugada para el jugador en el estado
acotando a un periodo de tiempo

"""
t0 = time()
d, traza = 2, []
while time() - t0 < tiempo/2:
while time() - t0 < tiempo / 2:
traza, v = negamax(
juego=juego, estado=estado, jugador=jugador,
alpha=-1e10, beta=1e10, ordena=ordena, d=d, evalua=evalua,
transp={}, traza=traza
juego=juego,
estado=estado,
jugador=jugador,
alpha=-1e10,
beta=1e10,
ordena=ordena,
d=d,
evalua=evalua,
transp={},
traza=traza,
)
d += 1
return traza[0]
Loading