Juego de gato¶
El juego del gato consta en un tablero de $3\times 3$ donde cada jugador marca con un signo distintivo ($o$ y $x$) una casilla. El jugador que pueda completar un renglón, una columna o una diagonal con sólo los signos que le corresponden ganará el juego. Para simular esto, tenemos que determinar una función que nos diga cuando las variables (casillas del tablero) son iguales, para identificar cuando se ha completado un juego ganador.
import numpy as np
from itertools import combinations
np.random.seed(12345)
def allsim(variables):
"""Revisa si son similares todas las variables"""
for x_i,x_j in combinations(variables, 2):
if x_i != x_j:
return False
return True
Nuestro simulador para jugar gato consistirá en un tablero de $3\times 3$ (una matriz) en donde cada jugador colocará un símbolo, 'x' o 'o', para intentar formar una fila, columna o diagonal con estos símbolos. Ganará el jugador que primero logre realizar esto. Definimos aquí las siguientes funciones:
- to_move: Determina quién es jugador al que le corresponde la jugada.
- change_player: Cambia de jugador cuando se ha terminado una jugada.
- actions: Regresa las coordenadas de las acciones posibles del estado actual del tablero; es decir, aquellas casillas que no han sido marcadas.
- result: Regresa el resultado de la acción (marcar una casilla) dado el estado actual del tablero.
- is_terminal: Revisa si el estado actual del tablero es un estado final, es decir, si alguno de los dos jugadores ha ganado. Si es así, regresa True.
- utility: Es la función de utilidad definida como: $$U(s) = \begin{cases} 1 & \text{si jugador 1 gana} \\ 0 & \text{si hay empate} \\ -1 & \text{si jugador 2 gana} \end{cases}$$
class Gato():
"""Clase para simular el juego de gato"""
def __init__(self):
#Tablero de 3x3
self.board = np.zeros((3,3))
#Guarda el jugador previo
self.previous_player = None
#Guarda el jugador actual
self.player = 1
def __str__(self):
"""Imprime tablero"""
board = ''
for row_i in self.board:
row = '|'
for val in row_i:
if val == 0:
row += ' |'
elif val == 1:
row += 'o|'
else:
row += 'x|'
board += row + '\n'
return board
def to_move(self):
"""Quién moverá en el turno"""
return self.player
def change_player(self):
"""Cambia el turno de cada jugador"""
if self.player == 1:
self.player = 2
elif self.player == 2:
self.player = 1
def actions(self, state):
"""La posibles acciones que se pueden realizar"""
#En dónde hay espacios para marcar
return np.stack(np.where(state == 0)).T
def result(self, state, action):
"""Resultado de la acción de cada jugador"""
if self.player == 1:
state[action[0],action[1]] = 1
return state
elif self.player == 2:
state[action[0],action[1]] = -1
return state
def is_terminal(self, state):
"""Determina si un estado es final o no
El estado es final si un renglón, columna o diagonal tienen los mismos símbolos"""
rows, columns = False, False
for i in range(3):
rows = rows or allsim(list(state[i])+[1]) or allsim(list(state[i])+[-1])
columns = columns or allsim(list(state.T[i])+[1]) or allsim(list(state.T[i])+[-1])
diag1 = allsim([state[0,0],state[1,1],state[2,2],1]) or allsim([state[0,0],state[1,1],state[2,2],-1])
diag2 = allsim([state[0,2],state[1,1],state[2,0],1]) or allsim([state[0,2],state[1,1],state[2,0],-1])
draw = list(self.actions(state))==[]
return rows or columns or diag1 or diag2
def utility(self, state, player=1):
"""Función de utilidad de cada jugador"""
if player == 1:
w = 1
else:
w = -1
if list(self.actions(state))==[]:
return 0
elif allsim([state[0,0],state[1,1],state[2,2]]):
return w*state[1,1]
elif allsim([state[0,2],state[1,1],state[2,0]]):
return w*state[1,1]
else:
for i in range(3):
if allsim(state[i]):
return w*state[i,1]
elif allsim(state.T[i]):
return w*state[1,i]
Podemos crear nuestro juego de gato que comenzará con un tablero con todas las casillas vacías. Asimismo, las acciones permitidas son marcar cualquiera de las casillas del tablero.
game = Gato()
print(game)
print('Coordenadas de posibles movimientos:\n{}'.format(game.actions(game.board)))
| | | | | | | | | | | | Coordenadas de posibles movimientos: [[0 0] [0 1] [0 2] [1 0] [1 1] [1 2] [2 0] [2 1] [2 2]]
El jugador 1 puede marcar cualquier casilla con el símbolo 'o'.
print('Jugador actual: {}'.format(game.to_move()))
#Realiza jugada
game.board = game.result(game.board, [1,1])
game.change_player()
print(game)
Jugador actual: 1 | | | | | |o| | | | | |
El jugador 2 también puede marcar una de las casillas no marcadas todavía con el símbolo 'x'.
print('Jugador actual: {}'.format(game.to_move()))
#Realiza jugada
game.board = game.result(game.board, [0,2])
game.change_player()
print(game)
Jugador actual: 2 | | |x| | |o| | | | | |
Finalmente, podemos generar un juego aleatorio:
for i in range(7):
print('Jugador actual: {}'.format(game.to_move()))
#Selecciona acción aleatoriamente
actions = game.actions(game.board)
a = actions[np.random.choice(range(len(actions)))]
#Realiza jugada
game.board = game.result(game.board, a)
game.change_player()
print(game)
Jugador actual: 1 | | |x| |o|o| | | | | | Jugador actual: 2 | | |x| |o|o| | | | |x| Jugador actual: 1 | |o|x| |o|o| | | | |x| Jugador actual: 2 |x|o|x| |o|o| | | | |x| Jugador actual: 1 |x|o|x| |o|o| | |o| |x| Jugador actual: 2 |x|o|x| |o|o|x| |o| |x| Jugador actual: 1 |x|o|x| |o|o|x| |o|o|x|