Existem muitos aspectos a considerar no problema, mas se esquecermos os detalhes podemos propor uma primeira versão do nosso programa:
def tic_tac_toe_0(n):
# inicializa tabuleiro
# mostra tabuleiro
# define jogador
while True : # não há vencedor ou empate
if True: # humano
# pede jogada
# actualiza tabuleiro
pass
else:
# define jogada
# actualiza tabuleiro
pass
# mostra tabuleiro
# Vencedor ou empate --> termina
# comuta jogador
# mensagem final
Como se pode ver o jogo começa essencialmente por construir um tabuleiro e definir quem é o jogador que começa. Depois entramos num ciclo em que os jogadores jogam alternadamente enquanto não houver vencedor ou o jogo estar empatado. Podemos avançar um pouco começando por definir as partes que envolvem criar um tabuleiro e mostrar o tabuleiro.
def tic_tac_toe_1(n):
# inicializa tabuleiro
tabuleiro = inicializa_tabuleiro(n)
# mostra tabuleiro
mostra_tabuleiro(tabuleiro)
# define jogador
while True : # não há vencedor ou empate
if True: # humano
# pede jogada
# actualiza tabuleiro
pass
else:
# define jogada
# actualiza tabuleiro
pass
# mostra tabuleiro
mostra_tabuleiro(tabuleiro)
# Vencedor ou empate --> termina
# comuta jogador
# mensagem final
Agora precisamos definir as novas funções. Para isso, é necessário decidir a representação para o tabuleiro. Optamos por uma lista de listas. Assim podemos facilmente modificar o seu conteúdo. Cada célula do tabuleiro, um elemento da lista de listas, poderá ter um de três valores: 0, quando está vazia, ‘X’ ou ‘O’ para a marca de cada jogador.Começando pela inicialização do tabuleiro, podemos optar pela solução trivial:
def inicializa_tabuleiro_0(n):
tabuleiro = []
for i in range(n):
# linha i
linha_i = []
for j in range(n):
linha_i += [0]
tabuleiro += [linha_i]
return tabuleiro
Dois ciclos. O externo trata das linhas, o interno das colunas para cada linha. Uma alternativa:
def inicializa_tabuleiro(n):
tabuleiro = []
for i in range(n):
# linha i
tabuleiro += [[0] * n]
return tabuleiro
Quem já conhece as listas por compreensão poderia optar por:
def inicializa_tabuleiro(n):
return [[0] * n for i in range(n)]
Agora mostrar o tabuleiro. A solução banal:
def mostra_tabuleiro(tabuleiro):
print(tabuleiro)
Esta solução esquece a natureza bi-dimensional do tabuleiro. Mas isso pode-se resolver, ainda de modo simples:
def mostra_tabuleiro(tabuleiro):
for linha in tabuleiro:
for valor in linha:
print(valor,' ',end='')
print()
E agora? Que fazer? Vamos definir o jogador:
def define_jogador():
import random
return random.choice(‘XO')
Notar onde é feita a importação do módulo. Isso tem consequências. Sabe quais são, certo? Com este modo de proceder, avançamos para uma versão mais detalhada:
def tic_tac_toe_2(n):
# inicializa tabuleiro
tabuleiro = inicializa_tabuleiro(n)
# mostra tabuleiro
mostra_tabuleiro(tabuleiro)
# define jogador
jogador = define_jogador()
while True : # não há vencedor ou empate
if jogador == 'X': # humano
# pede jogada
# actualiza tabuleiro
pass
else:
# define jogada
# actualiza tabuleiro
pass
# mostra tabuleiro
mostra_tabuleiro(tabuleiro)
# Vencedor ou empate --> termina
# comuta jogador
if jogador == 'X':
return 'O'
else:
return 'X'
# mensagem final
Está na hora de resolver o problema do conceito de jogada. Vamos admitir que jogar é simplesmente indicar as coordenadas da célula onde se pretende jogar. Existe uma diferença entre ser o humano ou ser o computador. No primeiro caso o programa dever pedir ao humano essas coordenadas:
def pede_jogada(tabuleiro):
jogada = eval(input('A sua jogada (x,y)?: '))
while not possivel(tabuleiro,jogada):
print('Jogada ilegal...')
jogada = eval(input('A sua jogada (x,y)?: '))
return jogada
def possivel(tabuleiro,jogada):
return tabuleiro[jogada[0]][jogada[1]] == 0
Só estamos a proteger o programa contra tentavas de jogar numa célula já ocupada…. No caso do computador a jogar podemos definir definir diferentes estratégias. apresentamos duas: numa, o computador procura por uma certa ordem a primeira posição disponível; na outra, o computador escolhe aleatoriamente uma de entre as disponíveis.
def joga_computador(tabuleiro):
"""Escolha por ordem"""
for i in range(len(tabuleiro)):
for j in range(len(tabuleiro[0])):
if tabuleiro[i][j] == 0:
return i,j
def joga_computador_b(tabuleiro):
import random
"""Escolha aleatória"""
livres = []
for i in range(len(tabuleiro)):
for j in range(len(tabuleiro[0])):
if tabuleiro[i][j] == 0:
livres += [(i,j)]
return random.choice(livres)
Daqui resulta uma nova aproximação.
def tic_tac_toe_3(n):
# inicializa tabuleiro
tabuleiro = inicializa_tabuleiro(n)
# mostra tabuleiro
mostra_tabuleiro(tabuleiro)
# define jogador X (humano) ou O (computador)
jogador = define_jogador()
while True: # não há vencedor ou empate
if jogador == 'X': # humano
print('Joga Humano')
# pede jogada
jogada = pede_jogada(tabuleiro)
# actualiza tabuleiro
else:
print('Joga computador')
# define jogada
jogada = joga_computador_b(tabuleiro)
# actualiza tabuleiro
# mostra tabuleiro
mostra_tabuleiro(tabuleiro)
# Vencedor ou empate --> termina
# comuta jogador
jogador = comuta_jogador(jogador)
# mensagem final
Com o conceito de jogada definido podemos passar à actualização do tabuleiro. Como temos que colocar marcas diferentes, a função vai ter três parâmetros: o tabuleiro, a jogada (a posição) e a marca:
def actualiza_tabuleiro(tabuleiro, jogada, jogador):
if jogador == 'X':
tabuleiro[jogada[0]][jogada[1]] = 'X'
else:
tabuleiro[jogada[0]][jogada[1]] = 'O'
return tabuleiro
E aproximamo-nos do final:
def tic_tac_toe_4(n):
# inicializa tabuleiro
tabuleiro = inicializa_tabuleiro(n)
# mostra tabuleiro
mostra_tabuleiro(tabuleiro)
# define jogador X (humano) ou O (computador)
jogador = define_jogador()
while True: # não há vencedor ou empate
if jogador == 'X': # humano
print('Joga Humano')
# pede jogada
jogada = pede_jogada(tabuleiro)
# actualiza tabuleiro
tabuleiro = actualiza_tabuleiro(tabuleiro,jogada,jogador)
else:
print('Joga computador')
# define jogada
jogada = joga_computador_b(tabuleiro)
# actualiza tabuleiro
tabuleiro = actualiza_tabuleiro(tabuleiro,jogada,jogador)
# mostra tabuleiro
mostra_tabuleiro(tabuleiro)
# Vencedor ou empate --> termina
# comuta jogador
jogador = comuta_jogador(jogador)
# mensagem final
Vamos tratar agora de uma parte mais difícil: Determinar se há vencedor ou se o jogo está empatado. Comecemos pela primeira questão. Haverá vencedor se um jogador tiver N marcas consecutivas, numa linha ou numa coluna ou numa diagonal. Vamos então separar estas três verificações, parando mal uma delas nos permita tirar conclusões. Então:
def vencedor(tabuleiro,jogador):
return vence_linhas(tabuleiro,jogador) or vence_colunas(tabuleiro, jogador) or vence_diagonais(tabuleiro, jogador)
def vence_linhas(tabuleiro,jogador):
for i in range(len(tabuleiro)):
vence = True
for j in range(len(tabuleiro[0])):
vence = vence and (tabuleiro[i][j] == jogador)
if vence:
return True
return False
def vence_colunas(tabuleiro,jogador):
for i in range(len(tabuleiro[0])):
vence = True
for j in range(len(tabuleiro)):
vence = vence and (tabuleiro[j][i] == jogador)
if vence:
return True
return False
def vence_diagonais(tabuleiro,jogador):
vence_1 = True
vence_2 = True
# diagonal principal
for i in range(len(tabuleiro)):
vence_1 = vence_1 and (tabuleiro[i][i] == jogador)
vence_2 = vence_2 and (tabuleiro[i][len(tabuleiro)-1-i] == jogador)
return vence_1 or vence_2
A verificaão de empate é mais simples:
def empate(tabuleiro):
for i in range(len(tabuleiro)):
for j in range(len(tabuleiro[0])):
if tabuleiro[i][j] == 0:
return False
return True
Tome atenção sobretudo ao caso das diagonais e ao modo como calculamos as respectivas posições. Para concluir, só nos falta a mensagem final. Vamos colocá-la no programa e mostar a versão final.
def tic_tac_toe(n):
# inicializa tabuleiro
tabuleiro = inicializa_tabuleiro(n)
# mostra tabuleiro
mostra_tabuleiro(tabuleiro)
# define jogador X (humano) ou O (computador)
jogador = define_jogador()
while True: # não há vencedor ou empate
if jogador == 'X': # humano
print('Joga Humano')
# pede jogada
jogada = pede_jogada(tabuleiro)
# actualiza tabuleiro
tabuleiro = actualiza_tabuleiro(tabuleiro,jogada,jogador)
else:
print('Joga computador')
# define jogada
jogada = joga_computador_b(tabuleiro)
# actualiza tabuleiro
tabuleiro = actualiza_tabuleiro(tabuleiro,jogada,jogador)
# mostra tabuleiro
mostra_tabuleiro(tabuleiro)
# Vencedor ou empate --> termina
if vencedor(tabuleiro,jogador) or empate(tabuleiro):
break
# comuta jogador
jogador = comuta_jogador(jogador)
# mensagem final
print('Finito...')
if empate(tabuleiro):
print('Fica para o próximo jogo!')
elif jogador == 'X':
print('Parabéns humanóide!')
else:
print('Parabéns Computador')
Agora já só falta … jogar. Ou quase. Se quiser melhorar o desempenho do computador, tem que pensar numa estratégia diferente O programa seguinte mostra uma hipótese. Não quer completar??
def joga_computador_c(tabuleiro):
if pode_ganhar_computador(tabuleiro):
# joga para aí
pass
elif pode_ganhar_humano(tabuleiro):
# bloqueia
pass
elif livre_centro():
# joga centro
pass
elif livre_canto():
# joga_canto
pass
else:
# joga
pass
Moral da história: devagar se vai ao longe ou a complexidade domina-se dividindo um problema em sub-problemas mais simples.

Sem comentários:
Enviar um comentário