Representacion del Estado del Tablero
Antes de disenar una IA de Go, primero debemos resolver un problema fundamental: ¿Como hacer que la computadora "entienda" el tablero?
Este problema parece simple, pero involucra estructuras de datos, algoritmos y diseno de entrada para redes neuronales en multiples niveles. Este articulo comenzara desde la matriz bidimensional mas basica, introduciendo gradualmente varios metodos de representacion del tablero utilizados en IA de Go, y finalmente explicara como AlphaGo codifica el tablero en un formato que las redes neuronales pueden procesar.
Representacion con Matriz Bidimensional: La Forma Mas Intuitiva
Concepto Basico
El tablero de Go es una cuadricula de 19×19, y la forma mas intuitiva de representarlo es usando una matriz bidimensional:
import numpy as np
# Constantes del tablero
BOARD_SIZE = 19
EMPTY = 0
BLACK = 1
WHITE = 2
# Inicializar tablero vacio
board = np.zeros((BOARD_SIZE, BOARD_SIZE), dtype=np.int8)
# Colocar piedra: negra en D4 (3, 3)
board[3, 3] = BLACK
# Colocar piedra: blanca en Q16 (15, 3)
board[15, 3] = WHITE
Sistema de Coordenadas
El Go usa dos sistemas de coordenadas comunes:
1. Coordenadas Numericas (para programacion)
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
0 . . . . . . . . . . . . . . . . . . .
1 . . . . . . . . . . . . . . . . . . .
2 . . . . . . . . . . . . . . . . . . .
3 . . . ● . . . . . + . . . . . ○ . . .
...
2. Coordenadas Letra-Numero (para registros de partidas)
A B C D E F G H J K L M N O P Q R S T
19 . . . . . . . . . . . . . . . . . . .
18 . . . . . . . . . . . . . . . . . . .
17 . . . . . . . . . . . . . . . . . . .
16 . . . ● . . . . . + . . . . . ○ . . .
...
Nota: Las coordenadas de letras omiten "I" (para evitar confusion con el numero 1).
Funciones de Conversion de Coordenadas
def coord_to_index(coord):
"""
Convertir coordenadas de partida a indices de matriz
Ejemplo: 'D4' -> (3, 15)
"""
letters = 'ABCDEFGHJKLMNOPQRST' # Omite I
col = letters.index(coord[0].upper())
row = BOARD_SIZE - int(coord[1:])
return row, col
def index_to_coord(row, col):
"""
Convertir indices de matriz a coordenadas de partida
Ejemplo: (3, 15) -> 'Q16'
"""
letters = 'ABCDEFGHJKLMNOPQRST'
return f"{letters[col]}{BOARD_SIZE - row}"
Analisis de Complejidad Espacial
Complejidad espacial de la representacion con matriz bidimensional:
| Representacion | Tamano por celda | Espacio total |
|---|---|---|
int8 | 1 byte | 361 bytes |
int32 | 4 bytes | 1.4 KB |
float64 | 8 bytes | 2.9 KB |
Para las computadoras modernas, esta memoria no es un problema. Sin embargo, cuando se necesita almacenar muchos estados del tablero (como en el arbol de busqueda MCTS), usar int8 puede reducir significativamente el uso de memoria.
Operaciones Basicas
class Board:
def __init__(self, size=19):
self.size = size
self.board = np.zeros((size, size), dtype=np.int8)
self.current_player = BLACK
self.ko_point = None # Punto de ko prohibido
self.move_history = []
def is_valid_move(self, row, col):
"""Verificar si un movimiento es legal"""
# 1. La posicion esta dentro del tablero
if not (0 <= row < self.size and 0 <= col < self.size):
return False
# 2. La posicion esta vacia
if self.board[row, col] != EMPTY:
return False
# 3. No es un punto de ko prohibido
if self.ko_point == (row, col):
return False
# 4. No es suicidio (a menos que pueda capturar)
# ... necesita verificacion mas compleja
return True
def place_stone(self, row, col):
"""Colocar piedra"""
if not self.is_valid_move(row, col):
return False
self.board[row, col] = self.current_player
self.move_history.append((row, col))
# Procesar capturas
captures = self._remove_captured_stones(row, col)
# Actualizar punto de ko
self._update_ko(row, col, captures)
# Cambiar jugador
self.current_player = WHITE if self.current_player == BLACK else BLACK
return True
Zobrist Hashing: Identificacion Rapida de Posiciones Duplicadas
Contexto del Problema
En la IA de Go, frecuentemente necesitamos determinar "si dos posiciones son iguales":
- Regla de ko: No se puede devolver la posicion al estado del movimiento anterior
- Triple ko: Manejo de reglas especiales
- Tabla de transposicion: Recordar posiciones ya buscadas
- Cache de red neuronal: Evitar calculos repetidos
Si comparamos celda por celda cada vez, necesitamos tiempo O(361). ¿Hay un metodo mas rapido?
Principio de Zobrist Hashing
Zobrist Hashing (tambien llamado Zobrist Keys) es un metodo de hash ingenioso, propuesto por Albert Zobrist en 1970. Su idea central es:
- Pregenerar un numero aleatorio para cada combinacion de "posicion × color"
- El valor hash del tablero = XOR de los numeros aleatorios de todas las posiciones con piedras
- Colocar/capturar piedras solo requiere una operacion XOR para actualizar el valor hash
Tabla de Numeros Aleatorios
import numpy as np
# Generar tabla de numeros aleatorios
# Usar semilla fija para reproducibilidad
np.random.seed(42)
# Un numero aleatorio para cada posicion, cada color
# zobrist[color][row][col]
zobrist_table = np.random.randint(
0, 2**64,
size=(3, BOARD_SIZE, BOARD_SIZE), # 3: EMPTY, BLACK, WHITE
dtype=np.uint64
)
# Numero aleatorio para quien juega
zobrist_player = np.random.randint(0, 2**64, dtype=np.uint64)
Propiedades de la Operacion XOR
La operacion XOR tiene varias propiedades importantes que la hacen muy adecuada para esta aplicacion:
- Reflexividad: A ⊕ A = 0 (el mismo numero XOR consigo mismo es 0)
- Reversibilidad: A ⊕ B ⊕ B = A (XOR dos veces es igual a nada)
- Conmutatividad: A ⊕ B = B ⊕ A
- Asociatividad: (A ⊕ B) ⊕ C = A ⊕ (B ⊕ C)
Esto significa:
- Colocar piedra:
hash ^= zobrist[color][row][col] - Capturar piedra:
hash ^= zobrist[color][row][col](¡la misma operacion!)
Implementacion
class ZobristBoard:
def __init__(self, size=19):
self.size = size
self.board = np.zeros((size, size), dtype=np.int8)
self.current_player = BLACK
self.hash = np.uint64(0)
# Inicializar tabla de numeros aleatorios
self._init_zobrist_table()
def _init_zobrist_table(self):
np.random.seed(12345)
self.zobrist = np.random.randint(
0, 2**64,
size=(3, self.size, self.size),
dtype=np.uint64
)
self.zobrist_player = np.random.randint(0, 2**64, dtype=np.uint64)
def place_stone(self, row, col, color):
"""Colocar piedra y actualizar valor hash"""
# Remover estado anterior (si hay piedra)
old_color = self.board[row, col]
if old_color != EMPTY:
self.hash ^= self.zobrist[old_color, row, col]
# Agregar nueva piedra
self.board[row, col] = color
self.hash ^= self.zobrist[color, row, col]
def remove_stone(self, row, col):
"""Capturar piedra y actualizar valor hash"""
color = self.board[row, col]
if color != EMPTY:
self.hash ^= self.zobrist[color, row, col]
self.board[row, col] = EMPTY
def switch_player(self):
"""Cambiar jugador y actualizar valor hash"""
self.hash ^= self.zobrist_player
self.current_player = WHITE if self.current_player == BLACK else BLACK
def get_hash(self):
"""Obtener valor hash de la posicion actual"""
return self.hash
Ventaja de la Actualizacion Incremental
Usando Zobrist Hashing, actualizar el valor hash solo requiere tiempo O(1):
| Operacion | Comparacion tradicional | Zobrist |
|---|---|---|
| Comparar dos posiciones | O(361) | O(1) |
| Actualizar despues de colocar | O(361) | O(1) |
| Actualizar despues de capturar | O(361) | O(k), k es el numero de capturas |
Probabilidad de Colision
Usando hash de 64 bits, la probabilidad de colision es extremadamente baja:
P(colision) ≈ n² / 2^65
Donde n es el numero de posiciones diferentes. Incluso con mil millones de posiciones, la probabilidad de colision es solo aproximadamente 10^-3.
En la practica, muchos programas de Go usan Zobrist Hashing de 64 bits sin verificar colisiones, porque la probabilidad de colision es lo suficientemente baja como para ignorarla.
Representacion de Cadenas (Union-Find): Problema de Conectividad
Concepto de Cadena
En Go, una cadena (String o Chain) se refiere a un grupo de piedras del mismo color conectadas. Las "libertades" de la cadena determinan su vida o muerte.
En la figura anterior, cuatro piedras negras forman una cadena, y el numero de libertades de esta cadena determina su vida o muerte.
¿Por Que Se Necesita Gestion Eficiente de Cadenas?
Cada movimiento puede:
- Crear una nueva cadena
- Fusionar multiples cadenas
- Reducir las libertades de cadenas enemigas
- Capturar cadenas sin libertades
Estas operaciones necesitan ejecutarse eficientemente, porque en MCTS, pueden necesitarse decenas de miles de simulaciones de movimientos por segundo.
Estructura de Datos Union-Find
Union-Find (tambien llamado Disjoint Set Union, DSU) es una estructura de datos clasica para manejar problemas de "conectividad":
class UnionFind:
def __init__(self, size):
# parent[i] apunta al padre de i
# Si parent[i] = i, entonces i es raiz
self.parent = list(range(size))
self.rank = [0] * size
def find(self, x):
"""
Encontrar el nodo raiz del conjunto al que pertenece x (con compresion de camino)
"""
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x]) # Compresion de camino
return self.parent[x]
def union(self, x, y):
"""
Fusionar los conjuntos de x e y (con union por rango)
"""
root_x = self.find(x)
root_y = self.find(y)
if root_x == root_y:
return # Ya estan en el mismo conjunto
# Union por rango: conectar el arbol mas pequeno al mas grande
if self.rank[root_x] < self.rank[root_y]:
self.parent[root_x] = root_y
elif self.rank[root_x] > self.rank[root_y]:
self.parent[root_y] = root_x
else:
self.parent[root_y] = root_x
self.rank[root_x] += 1
Complejidad Temporal
La complejidad temporal de Union-Find se expresa usando la funcion de Ackermann inversa α(n):
| Operacion | Complejidad temporal |
|---|---|
| find | O(α(n)) ≈ O(1) |
| union | O(α(n)) ≈ O(1) |
α(n) crece extremadamente lento, para cualquier n practico, α(n) ≤ 4. Por lo tanto, puede considerarse tiempo constante.
Aplicacion de Union-Find en Go
class GoStringManager:
def __init__(self, size=19):
self.size = size
num_points = size * size
# Estructura Union-Find
self.parent = list(range(num_points))
self.rank = [0] * num_points
# Libertades de cada cadena (indexado por raiz)
self.liberties = [set() for _ in range(num_points)]
# Piedras de cada cadena (indexado por raiz)
self.stones = [set() for _ in range(num_points)]
def _index(self, row, col):
return row * self.size + col
def _neighbors(self, row, col):
"""Obtener los cuatro vecinos"""
neighbors = []
if row > 0: neighbors.append((row - 1, col))
if row < self.size - 1: neighbors.append((row + 1, col))
if col > 0: neighbors.append((row, col - 1))
if col < self.size - 1: neighbors.append((row, col + 1))
return neighbors
def find(self, idx):
if self.parent[idx] != idx:
self.parent[idx] = self.find(self.parent[idx])
return self.parent[idx]
def add_stone(self, row, col, color, board):
"""
Agregar una piedra, manejar conexiones y capturas
"""
idx = self._index(row, col)
# Crear nueva cadena
self.stones[idx] = {idx}
self.liberties[idx] = set()
# Calcular libertades de esta piedra
for nr, nc in self._neighbors(row, col):
nidx = self._index(nr, nc)
if board[nr, nc] == 0: # Punto vacio es libertad
self.liberties[idx].add(nidx)
# Fusionar con vecinos del mismo color
for nr, nc in self._neighbors(row, col):
nidx = self._index(nr, nc)
if board[nr, nc] == color:
self._merge_strings(idx, nidx)
# Reducir libertades de cadenas enemigas
opponent = 3 - color # BLACK=1, WHITE=2
captured = []
for nr, nc in self._neighbors(row, col):
nidx = self._index(nr, nc)
if board[nr, nc] == opponent:
root = self.find(nidx)
self.liberties[root].discard(idx)
if len(self.liberties[root]) == 0:
captured.append(root)
return captured
def _merge_strings(self, idx1, idx2):
"""Fusionar dos cadenas"""
root1 = self.find(idx1)
root2 = self.find(idx2)
if root1 == root2:
return
# Union por rango
if self.rank[root1] < self.rank[root2]:
root1, root2 = root2, root1
self.parent[root2] = root1
if self.rank[root1] == self.rank[root2]:
self.rank[root1] += 1
# Fusionar conjuntos de libertades y piedras
self.liberties[root1] |= self.liberties[root2]
self.stones[root1] |= self.stones[root2]
# Remover su propia posicion de libertades (uno no es su propia libertad)
for stone_idx in self.stones[root2]:
self.liberties[root1].discard(stone_idx)
Calculo de Libertades
Calcular el numero de libertades de una cadena es una de las operaciones mas comunes en Go:
def count_liberties(self, row, col, board):
"""Calcular el numero de libertades de la cadena a la que pertenece una piedra"""
idx = self._index(row, col)
root = self.find(idx)
return len(self.liberties[root])
Usando Union-Find, esta operacion es O(1) (asumiendo que find es O(1)).
Evolucion de la Codificacion de Caracteristicas
Desde caracteristicas manuales tradicionales hasta las caracteristicas simplificadas de AlphaGo Zero, la codificacion de caracteristicas de la IA de Go ha experimentado una evolucion significativa.
Caracteristicas Manuales de GNU Go
Los primeros programas de Go (como GNU Go) usaban muchas caracteristicas disenadas manualmente:
def extract_features_gnugo(board, point):
"""
Extraccion de caracteristicas estilo GNU Go
"""
features = {}
# 1. Patrones de forma (formas locales predefinidas)
features['pattern'] = match_pattern(board, point)
# 2. Caracteristicas tacticas
features['can_capture'] = check_capture(board, point)
features['can_connect'] = check_connect(board, point)
features['creates_eye'] = check_eye_creation(board, point)
# 3. Caracteristicas locales
features['liberties_after'] = count_liberties_after_move(board, point)
features['enemy_liberties'] = count_enemy_liberties(board, point)
# 4. Caracteristicas globales
features['distance_to_edge'] = min(
point[0], point[1],
18 - point[0], 18 - point[1]
)
# ... mas caracteristicas
return features
Problemas con este enfoque:
- Requiere mucho conocimiento de expertos humanos
- Las caracteristicas pueden estar incompletas
- Dificil descubrir nuevos patrones
Los 48 Planos de Caracteristicas de AlphaGo
AlphaGo (version Nature 2016) uso 48 planos de caracteristicas como entrada de la red neuronal:
def encode_alphago_features(board, player, history):
"""
48 planos de caracteristicas de AlphaGo 2016
Returns:
Tensor de (19, 19, 48)
"""
features = np.zeros((19, 19, 48), dtype=np.float32)
# Plano 1: Posiciones de piedras negras
features[:, :, 0] = (board == BLACK)
# Plano 2: Posiciones de piedras blancas
features[:, :, 1] = (board == WHITE)
# Plano 3: Puntos vacios
features[:, :, 2] = (board == EMPTY)
# Plano 4: Todo unos (termino de sesgo)
features[:, :, 3] = 1
# Planos 5-12: Codificacion del numero de libertades (un plano para 1-8 libertades)
for i, num_libs in enumerate(range(1, 9)):
for r in range(19):
for c in range(19):
if board[r, c] != EMPTY:
libs = count_liberties(board, r, c)
if libs == num_libs:
features[r, c, 4 + i] = 1
# Planos 13-20: Libertades del oponente despues de captura
# ...
# Planos 21-28: Nuestras libertades despues de jugar
# ...
# Planos 29-36: Historial de los ultimos 8 movimientos (un plano por movimiento)
for i, (r, c) in enumerate(history[-8:]):
features[r, c, 28 + i] = 1
# Planos 37-44: Juicio de escalera
# ...
# Planos 45-48: Relacionados con simetria
# ...
# Plano 49: A quien le toca jugar (todo 1 para negro, todo 0 para blanco)
if player == BLACK:
features[:, :, 47] = 1
return features
Clasificacion de los 48 Planos
| Categoria | Numero de planos | Descripcion |
|---|---|---|
| Posiciones de piedras | 3 | Piedras negras, piedras blancas, puntos vacios |
| Constante | 1 | Todo unos |
| Numero de libertades | 8 | 1-8 libertades |
| Libertades despues de captura | 8 | Libertades asumiendo que jugamos |
| Libertades despues de jugar | 8 | Libertades asumiendo que jugamos |
| Historial | 8 | Ultimos 8 movimientos |
| Escalera | 8 | Analisis complejo de escalera |
| Otros | 4 | Turno, simetria, etc. |
Los 17 Planos de Caracteristicas de AlphaGo Zero
AlphaGo Zero (2017) simplifico drasticamente la codificacion de caracteristicas, usando solo 17 planos:
def encode_alphago_zero_features(board_history, player):
"""
17 planos de caracteristicas de AlphaGo Zero
Args:
board_history: Ultimas 8 posiciones (incluyendo la actual)
player: Jugador actual
Returns:
Tensor de (19, 19, 17)
"""
features = np.zeros((19, 19, 17), dtype=np.float32)
# Planos 1-8: Historial de piedras negras (ultimos 8 pasos)
for i, board in enumerate(board_history[-8:]):
features[:, :, i] = (board == BLACK)
# Planos 9-16: Historial de piedras blancas (ultimos 8 pasos)
for i, board in enumerate(board_history[-8:]):
features[:, :, 8 + i] = (board == WHITE)
# Plano 17: A quien le toca jugar (todo 1 para negro, todo 0 para blanco)
if player == BLACK:
features[:, :, 16] = 1
return features
La Simplicidad de los 17 Planos
| Categoria | Numero de planos | Descripcion |
|---|---|---|
| Historial de piedras negras | 8 | Posiciones de piedras negras en los ultimos 8 pasos |
| Historial de piedras blancas | 8 | Posiciones de piedras blancas en los ultimos 8 pasos |
| Jugador actual | 1 | Todo unos o todo ceros |
¿Por que puede ser tan simple?
Idea clave: Dejar que la red neuronal aprenda las caracteristicas por si misma.
AlphaGo Zero demostro que:
- No se necesita calcular manualmente el numero de libertades (la red puede inferirlo de las posiciones de las piedras)
- No se necesita analisis de escalera (la red puede aprender a reconocer escaleras)
- No se necesita prediccion compleja de capturas (la red puede entender las consecuencias de los movimientos)
Esto refleja la ventaja central del aprendizaje profundo: aprendizaje de extremo a extremo.
Visualizacion de Planos de Caracteristicas
Veamos como lucen realmente estos planos de caracteristicas:
Tablero original: Plano de negras: Plano de blancas:
. . . . . . 0 0 0 0 0 0 0 0 0 0 0 0
. ● ○ . . . 0 1 0 0 0 0 0 0 1 0 0 0
. . ● ○ . . -> 0 0 1 0 0 0 + 0 0 0 1 0 0
. . . ● . . 0 0 0 1 0 0 0 0 0 0 0 0
. . . . . . 0 0 0 0 0 0 0 0 0 0 0 0
Cada plano es una matriz binaria (0 o 1), la red neuronal puede extraer patrones de estos planos a traves de operaciones de convolucion.
Procesamiento de Simetria: 8 Transformaciones
Simetrias del Go
El tablero de Go tiene 8 simetrias:
- 4 rotaciones: 0°, 90°, 180°, 270°
- 4 reflexiones con rotacion: Reflexion horizontal + 4 rotaciones
Esto forma un grupo diedrico D4.
Representacion Matematica
Sea (x, y) una coordenada en el tablero (centro en (9, 9)), las 8 transformaciones pueden expresarse como:
| Transformacion | Formula |
|---|---|
| Identidad | (x, y) |
| Rotacion 90° | (-y, x) |
| Rotacion 180° | (-x, -y) |
| Rotacion 270° | (y, -x) |
| Reflexion horizontal | (-x, y) |
| Reflexion vertical | (x, -y) |
| Reflexion diagonal | (y, x) |
| Reflexion anti-diagonal | (-y, -x) |
Implementacion del Codigo
def get_symmetries(board):
"""
Obtener las 8 transformaciones simetricas del tablero
Returns:
Lista de 8 tableros
"""
symmetries = []
# Original
symmetries.append(board.copy())
# Rotacion 90°
symmetries.append(np.rot90(board, k=1))
# Rotacion 180°
symmetries.append(np.rot90(board, k=2))
# Rotacion 270°
symmetries.append(np.rot90(board, k=3))
# Reflexion horizontal
symmetries.append(np.fliplr(board))
# Reflexion vertical
symmetries.append(np.flipud(board))
# Reflexion diagonal
symmetries.append(board.T)
# Reflexion anti-diagonal
symmetries.append(np.rot90(board.T, k=2))
return symmetries
def apply_symmetry(board, sym_index):
"""
Aplicar la transformacion de simetria sym_index
"""
return get_symmetries(board)[sym_index]
def inverse_symmetry(move, sym_index, board_size=19):
"""
Convertir coordenadas de movimiento transformadas de vuelta a coordenadas originales
"""
row, col = move
if sym_index == 0: # Identidad
return row, col
elif sym_index == 1: # Rotacion 90°
return col, board_size - 1 - row
elif sym_index == 2: # Rotacion 180°
return board_size - 1 - row, board_size - 1 - col
elif sym_index == 3: # Rotacion 270°
return board_size - 1 - col, row
elif sym_index == 4: # Reflexion horizontal
return row, board_size - 1 - col
elif sym_index == 5: # Reflexion vertical
return board_size - 1 - row, col
elif sym_index == 6: # Reflexion diagonal
return col, row
elif sym_index == 7: # Reflexion anti-diagonal
return board_size - 1 - col, board_size - 1 - row
Aumento de Datos
Al entrenar redes neuronales, se puede usar la simetria para aumento de datos:
def augment_training_data(board, policy, value):
"""
Expandir una muestra de entrenamiento a 8
"""
augmented = []
for sym_index in range(8):
# Transformar tablero
aug_board = apply_symmetry(board, sym_index)
# Transformar politica (vector de 361 dimensiones)
policy_2d = policy.reshape(19, 19)
aug_policy_2d = apply_symmetry(policy_2d, sym_index)
aug_policy = aug_policy_2d.flatten()
# El valor no cambia
aug_value = value
augmented.append((aug_board, aug_policy, aug_value))
return augmented
Esto aumenta los datos de entrenamiento 8 veces, y es completamente gratis (no necesita recopilar nuevos datos).
Uso de Simetria en Inferencia
AlphaGo tambien usa simetria en partidas reales:
def predict_with_symmetry(model, board):
"""
Usar simetria para mejorar la prediccion
"""
policies = []
values = []
for sym_index in range(8):
# Transformar entrada
aug_board = apply_symmetry(board, sym_index)
# Prediccion de red neuronal
policy, value = model.predict(aug_board)
# Transformar politica de vuelta al sistema de coordenadas original
policy_2d = policy.reshape(19, 19)
original_policy = inverse_symmetry_2d(policy_2d, sym_index)
policies.append(original_policy.flatten())
values.append(value)
# Promediar todas las predicciones
avg_policy = np.mean(policies, axis=0)
avg_value = np.mean(values)
return avg_policy, avg_value
Este enfoque puede mejorar ligeramente la precision y estabilidad de las predicciones.
Resumen de Metodos de Representacion del Tablero
| Metodo | Uso | Complejidad | Caracteristicas |
|---|---|---|---|
| Matriz bidimensional | Almacenamiento basico | Espacio O(361) | Simple e intuitivo |
| Zobrist Hashing | Identificacion de posiciones | Consulta O(1) | Hash eficiente |
| Union-Find | Gestion de cadenas | Operacion O(α(n)) | Maneja conectividad |
| Planos de caracteristicas | Entrada de red neuronal | O(361×num_planos) | Codifica informacion rica |
| Transformaciones de simetria | Aumento de datos | 8× volumen de datos | Ampliacion gratuita |
Estas tecnicas trabajan juntas para formar la infraestructura de la IA de Go moderna.
Correspondencia con Animaciones
Conceptos centrales cubiertos en este articulo y numeros de animacion:
| Numero | Concepto | Correspondencia Fisica/Matematica |
|---|---|---|
| 🎬 A1 | Representacion con matriz bidimensional | Matrices, datos dispersos |
| 🎬 A2 | Zobrist Hashing | Funciones hash, operacion XOR |
| 🎬 A8 | Codificacion de planos de caracteristicas | Tensores, entrada multicanal |
| 🎬 A5 | Procesamiento de simetria | Teoria de grupos, grupo diedrico |
Lecturas Adicionales
- Articulo anterior: Los Limites de los Metodos Tradicionales - De Minimax a MCTS
- Articulo siguiente: Explicacion Detallada de la Policy Network - Como las redes neuronales predicen movimientos
- Ejercicio practico: Ejecuta tu primera IA de Go en 30 minutos - Experiencia practica
Referencias
- Zobrist, A. L. (1970). "A new hashing method with application for game playing." ICCA journal.
- Tarjan, R. E. (1975). "Efficiency of a Good But Not Linear Set Union Algorithm." Journal of the ACM, 22(2), 215-225.
- Silver, D., et al. (2016). "Mastering the game of Go with deep neural networks and tree search." Nature, 529, 484-489. - 48 planos de caracteristicas de AlphaGo
- Silver, D., et al. (2017). "Mastering the game of Go without human knowledge." Nature, 550, 354-359. - 17 planos de caracteristicas de AlphaGo Zero