O código mais "bonito" que já escrevi f'#{self.ego}'
Inspirado pelo projeto AlphaGo, um projeto que aprende a jogar Go sozinho usando machine learning, decidi criar um projeto semelhante, e o jogo escolhido foi… O jogo da velha!
Pois é…
O código
Antes de tudo eu precisava de uma grande quantidade de partidas para treinar a rede neural. Para isso, criei um pequeno script que gera jogas aleatórias com seus respectivos passos e resultado, eis o tal código:
def run():
board = np.zeros((3, 3), dtype=np.int)
players = np.array([1, 2])
np.random.shuffle(players)
player, next_player = players[:]
while True:
sys.stdout.write('\n')
sys.stdout.write(str(player))
sys.stdout.write(''.join(map(str, board.flatten())))
slots = (board == 0).flatten()
available_slots = np.where(slots == True)[0]
if available_slots.size == 0:
sys.stdout.write('3')
break
mask = board == player
out = mask.all(0).any() | mask.all(1).any()
out |= np.diag(mask).all() | np.diag(mask[:, ::-1]).all()
if out:
sys.stdout.write(str(player))
break
sys.stdout.write('0')
picked = np.random.choice(available_slots)
board[picked / 3][picked % 3] = player
next_player, player = player, next_player
Passo a passo
board = np.zeros((3, 3), dtype=np.int) # Cria um "tabuleiro" 3x3 inicializado com zero.
players = np.array([1, 2]) # Cria os jogadores.
np.random.shuffle(players) # Embaralha os jogadores.
player, next_player = players[:] # Após embaralhar, separamos os jogadores já sorteados.
sys.stdout.write(str(player)) # Imprime quem está jogando.
sys.stdout.write(''.join(map(str, board.flatten()))) # Imprime o estado atual do tabuleiro.
# Filtra pelas posições livres do tabuleiro.
slots = (board == 0).flatten()
available_slots = np.where(slots == True)[0]
# Caso não haja nenhuma posição livre, declara empate e termina o `loop`.
if available_slots.size == 0:
sys.stdout.write('3')
break
# Esse trecho é um pouco mais complexo, e graças ao numpy
# a verificação do jogador vitoriosos é mais simplificada.
# Cria uma máscara em relação o jogador atual e o tabuleiro.
mask = board == player
# Verifica a condição de vitória na horizontal e/ou vertical.
out = mask.all(0).any() | mask.all(1).any()
# Verifica a condição de vitória na diagonal.
out |= np.diag(mask).all() | np.diag(mask[:, ::-1]).all()
# Caso haja uma condição que satisfaça a vitória, imprime o jogador vitorioso.
if out:
sys.stdout.write(str(player))
break
# Ninguém ganhou, o jogo continua.
sys.stdout.write('0')
# Escolhe uma posição do tabuleiro que esteja livre.
picked = np.random.choice(available_slots)
# Atribui o número do jogador ao lugar do tabuleiro sorteado.
board[picked / 3][picked % 3] = player
# Troca quem joga na próxima interação.
next_player, player = player, next_player
A saída
O script escreve na saída stdout
o resultado de cada interação, o formato de saída é este:
PTTTTTTTTTR
P
- Representa o jogador, sendo 1 ou 2.T
- Indica o estado de cada posição do tabuleiro, 0 caso esteja livre; 1 ou 2* caso esteja preenchido.R
- Indica o resultado, 0 caso ainda não haja uma vitória, 3 caso seja um empate ou 1 ou 2 caso um dos jogadores tenha vencido.
A partida
$ python generator.py
10000000000
20020000000
10021000000
20021200000
10021200100
20221200100
10221201100
20221201120
11221201121
No caso acima, o jogador 1 iniciou a partida e acabou sendo o vitorioso. Vejamos outra partida:
$ python generator.py
20000000000
10000100000
20000120000
10000121000
20002121000
11002121000
21202121000
11202121100
21222121100
11222121113
Nessa partida o jogador 2 começou e terminou em empate.
Próximos passos
Não sei bem ao certo se irei usar deep reinforcement learning (deep Q-learning) #recomendo ou algum outro método para este projeto, fiquem atentos aos próximos capítulos.