def __init__(self, size): """Crea dos matrices de tamaño pasado por parámetro, una para estadísticas y otra para guardar el estado de las piedras. Creamos un set de piedras para ir guardando las piedras que estemos comprobando. También inicializa un kifu para guardar la partida y un el objetos igs que se encargará de conectarse con el servidor que subirá la partida. :Param size: tamaño del tablero :Type size: int """ self.size = size # El valor 0 es para ir sumando(hay piedra) o restando(no hay) # El valor 8 es el nº de veces a buscar antes de hacer la estadística self.goban = [[None] * size for i in range(size)] self.moves = [] self.statistical = [[10] * size for i in range(size)] self.stones = set() self.kifu = Kifu()
class Goban: """Clase tablero, contiene la matriz de estadíticas y funciones para rellenar el tablero. """ def __init__(self, size): """Crea dos matrices de tamaño pasado por parámetro, una para estadísticas y otra para guardar el estado de las piedras. Creamos un set de piedras para ir guardando las piedras que estemos comprobando. También inicializa un kifu para guardar la partida y un el objetos igs que se encargará de conectarse con el servidor que subirá la partida. :Param size: tamaño del tablero :Type size: int """ self.size = size # El valor 0 es para ir sumando(hay piedra) o restando(no hay) # El valor 8 es el nº de veces a buscar antes de hacer la estadística self.goban = [[None] * size for i in range(size)] self.moves = [] self.statistical = [[10] * size for i in range(size)] self.stones = set() self.kifu = Kifu() def invalid_move(self, move): return self.invalid_ko_move(move) def get_group(self, pos, color, group=set()): """ Funcion recursiva que busca, a partir de una posición, el grupo correspondiente a esa posición. :Param pos: Posición perteneciente al grupo en la buscaremos si sus vecinos pertenecen o no al grupo. :Type pos: tuple. :Param group: grupo de posiciones que se encuentran dentro del grupo. :Type group: set. """ # Out of range if pos[0] in (-1, GOBAN_SIZE) or pos[1] in (-1, GOBAN_SIZE): return group if self.goban[pos[0]][pos[1]] != color: return group group.add(pos) neighbour = ((0, -1), (0, 1), (1, 0), (-1, 0)) for n in neighbour: pos_neig = (pos[0] + n[0], pos[1] + n[1]) if not pos_neig in group: group = self.get_group(pos_neig, color, group) return group def get_liberties(self, pos, color): """ Función que comprueba las libertades que tiene el grupo al que pertenece la posición pasada por parámetro. :Param pos: Posición perteneciente al grupo en la buscaremos si sus vecinos pertenecen o no al grupo. :Type pos: tuple. :Param group: grupo de posiciones que se encuentran dentro del grupo. :Type group: set. """ liberties = set() group = self.get_group(pos, color) neighbour = ((0, -1), (0, 1), (1, 0), (-1, 0)) for pos in group: for n in neighbour: pos_neig = (pos[0] - n[0], pos[1] - n[1]) if not self.goban[pos_neig[0]][pos_neig[1]]: liberties.add(pos_neig) return liberties def is_last_liberty(self, pos, color): """ Comprobamos que la posición dada sea la última libertad del grupo. :Param pos: Posición perteneciente al grupo en la buscaremos si sus vecinos pertenecen o no al grupo. :Type pos: tuple. :Param group: grupo de posiciones que se encuentran dentro del grupo. :Type group: set. """ neighbour = ((0, -1), (0, 1), (1, 0), (-1, 0)) for n in neighbour: pos_neig = (pos[0] - n[0], pos[1] - n[1]) if len(self.get_liberties(pos_neig, color)) == 1: return True return False def is_move_kill(self, pos, color): """ Comprobamos que un movimiento capture un grupo. :Param pos: Posición perteneciente al grupo en la buscaremos si sus vecinos pertenecen o no al grupo. :Type pos: tuple. :Param group: grupo de posiciones que se encuentran dentro del grupo. :Type group: set. """ if color == BLACK: return self.is_last_liberty(pos, WHITE) elif color == WHITE: return self.is_last_liberty(pos, BLACK) def invalid_ko_move(self, move): """ Comprueba si el movimiento es un movimiento inválido de ko. :Param move: Movimiento a comprobar. :Type move: Move. """ if not self.moves: return False prev_move = self.moves[-1] around_pos = ((-1, 0), (1, 0), (0, -1), (0, 1)) if not (prev_move.x - move.x, prev_move.y - move.y) in around_pos: return False elif self.is_last_liberty(move.pt, move.color) and \ self.get_liberties(prev_move.pt, prev_move.color) == [move]: return True else: return False def add_move(self, move): """ Agregamos movimiento al tablero y a la lista de movimientos. Comprobando anteriormente si ese movimiento es válido. """ invalid = self.invalid_move(move) if not invalid: self.kifu.add_stone(move) self.goban[move.x][move.y] = move.color self.moves.append(move) return invalid def add_stones_to_statistical(self, stones): """Recorremos la lista de piedras pasadas por parámetros para hacer comprobaciones estadísticas en esas piedras, luego recorremos la lista de piedras guardada y la actualizamos. Actualiza kifu y el tablero donde guardamos el estado de las piedras cuando detecta estadísticamente que una piedra se ha puesto. :Param stones: lista de piedras :Type stones: list """ for st in stones: if self.goban[st.x][st.y] or st not in self.stones: continue self.statistical[st.x][st.y] -= 1 if self.statistical[st.x][st.y] <= 0: self.statistical[st.x][st.y] = 10 self.add_move(st) self.stones = set(stones) def print_st(self): string = "" for x in range(self.size): for y in range(self.size): string += '%s' % str(self.statistical[y][x]) string += " " + str(x + 1) + "\n" return string def __str__(self): string = "" for x in range(self.size): for y in range(self.size): if self.goban[y][x] == BLACK: string += " x " elif self.goban[y][x] == WHITE: string += " o " elif not self.goban[y][x]: string += " · " string += " " + str(x + 1) + "\n" return string