Hangman Python

Programando um Jogo em Python

Giovanni Angelo
Dados de Cientista

--

Como utilizar Programação Orientada a Objetos para desenvolvimento de Jogos em Python.

Mais um capítulo da minha jornada para me tornar um Cientista de Dados. No meu planejamento dessa transição de carreira decidi começar estudando uma das skills mais solicitadas: O Python. E hoje vou mostrar um pouco do avanço que tenho feito.

Já postei aqui meu primeiro contato com a linguagem e como apliquei os principais conceitos. Pra conferir só acessar o post Meu Primeiro Projeto em Python. Lá eu conto como apliquei tudo que sabia num único projeto. Agora vou contar como apliquei os conceitos de Programação Orientada a Objetos (POO) para fazer um jogo. Vamos lá!

Ideia do Projeto

Como mencionei no post citado, tenho seguido a ordem de estudos proposta no curso Python para Análise de Dados da Data Science Academy. Após a conclusão do capítulo sobre POO somos desafiados a aplicar os conceitos na construção de um jogo. O Hangman! (Ou para nós brasileiros: O Jogo da Forca)

No curso recebemos o esqueleto do projeto para que implementemos a lógica do jogo. O arquivo que recebemos estava neste formato:

# Hangman Game (Jogo da Forca) 
# Programação Orientada a Objetos
# Import
import random
# Board (tabuleiro)
board = ['''
>>>>>>>>>>Hangman<<<<<<<<<<+---+
| |
|
|
|
|
=========''', '''
+---+
| |
O |
|
|
|
=========''', '''
+---+
| |
O |
| |
|
|
=========''', '''
+---+
| |
O |
/| |
|
|
=========''', '''
+---+
| |
O |
/|\ |
|
|
=========''', '''
+---+
| |
O |
/|\ |
/ |
|
=========''', '''
+---+
| |
O |
/|\ |
/ \ |
|
=========''']
# Classe
class Hangman:
# Método Construtor
def __init__(self, word):


# Método para adivinhar a letra
def guess(self, letter):


# Método para verificar se o jogo terminou
def hangman_over(self):


# Método para verificar se o jogador venceu
def hangman_won(self):
# Método para não mostrar a letra no board
def hide_word(self):


# Método para checar o status do game e imprimir o board na tela
def print_game_status(self):
# Função para ler uma palavra de forma aleatória do banco de palavras
def rand_word():
with open("palavras.txt", "rt") as f:
bank = f.readlines()
return bank[random.randint(0,len(bank))].strip()
# Função Main - Execução do Programa
def main():
# Objeto
game = Hangman(rand_word())
# Enquanto o jogo não tiver terminado, print do status, solicita uma letra e faz a leitura do caracter# Verifica o status do jogo
game.print_game_status()
# De acordo com o status, imprime mensagem na tela para o usuário
if game.hangman_won():
print ('\nParabéns! Você venceu!!')
else:
print ('\nGame over! Você perdeu.')
print ('A palavra era ' + game.word)

print ('\nFoi bom jogar com você! Agora vá estudar!\n')
# Executa o programa
if __name__ == "__main__":
main()

O código acima contém a seguinte estrutura: Imports e definição da variável board, definição da Classe Hangman e dos métodos que compõem o jogo, definição da função main e execução do programa. Foi dado o desafio de implementar os seguintes métodos:

  • def guess(self, letter): — Método para adivinhar a letra
  • def hangman_over(self): — Método para verificar se o jogo terminou
  • def hangman_won(self): — Método para verificar se o jogador venceu
  • def hide_word(self): — Método para não mostrar a letra no board
  • def print_game_status(self): — Método para checar o status do game e imprimir o board na tela
  • def main(): — Método com a execução do jogo

Hora de Codar!

Caso você não conheça o jogo da forca, ele funciona da seguinte forma, uma palavra é escolhida aleatoriamente e o objetivo é adivinhar qual é a palavra apenas “chutando” as letras do alfabeto antes que o boneco morra na forca. Para cada letra errada uma parte do boneco é desenhada e caso o boneco fique completo antes de você descobrir a palavra, você perde.

Sabendo dessas regras e tendo visto a execução do jogo completo no curso, chegou a hora de começar a implementar os métodos e fazer o jogo aplicando os conceitos de POO em python. Agora vou detalhar cada método e como ele foi implementado.

Printando o status do Jogo

O método print_game_status(self): serve para checar o status do game e imprimir o board na tela.

# Método para checar o status do game e imprimir o board na tela
def print_game_status(self):
print(board[self.erros])
print("palavra: " + str(self.hidden))
print("\nLetras certas: " + self.lc)
print("\nLetras erradas: " + self.le)

Este método basicamente imprime o tabuleiro na tela. Mostrando a situação do boneco na forca, a palavra com as letras descobertas visíveis e com o restante oculto e a lista de letras certas e letras erradas. Todas as variáveis utilizadas foram criadas como atributos da Classe hangman.

# Método Construtor
def __init__(self, word):
self.word = word
self.erros = 0
self.lc = ""
self.le = ""
self.hidden = ""
self.temp_hidden = [] #explico essa daqui a pouco

Ocultando a palavra da vez

O programa pega de forma randômica através do método rand_word() uma das palavras presente no arquivo palavras.txt que foi preenchido anteriormente.

# Função para ler uma palavra de forma aleatória do banco de palavras
def rand_word():
with open("palavras.txt", "rt") as f:
bank = f.readlines()
return bank[random.randint(0, len(bank))-1].strip()

Com a palavra escolhida precisamos ocultar a palavra do usuário mas manter o número de caracteres disponíveis como dica da palavra. Para isso implementados o método hide_word().

# Método para não mostrar a letra no board
def hide_word(self):
for x in self.word:
self.temp_hidden.append("_")
self.hidden += "_" #Faço isso para a primeira impressão do status do jogo

Neste método eu percorro a variável word recebida na criação do objeto e substituo cada caractere por “_”, tanto na lista temp_hidden quanto na string hidden. A primeira é uma variável dinâmica que vai ser comparada com a variável word em toda iteração, a segunda é a que guarda apenas o resultado final e que é apresentada no print_game_status().

Adivinhando a letra

Este aqui foi o método mais complexo, boa parte da lógica de toda execução do jogo ficou aqui no guess().

# Método para adivinhar a letra
def guess(self, letter):
for x in range(len(self.word)):
if letter == self.word[x]:
self.temp_hidden[x] = self.word[x]
self.hidden = ""
for i in self.temp_hidden:
self.hidden += i
if letter in self.word:
self.lc += letter+" "
else:
self.le += letter+" "
self.erros += 1

Este método recebe a letra digitada pelo usuário e confere se a palavra escolhida contém a letra ou não. Primeiramente eu crio um loop com a quantidade de caracteres da palavra, e verifico se a letra informada está na palavra, se estiver substituo o “_” pela letra correta.

Depois reseto a string hidden para vazio, percorro todas as letras da lista temp_hidden e monto a string de hidden com o resultado já descoberto. Por fim, atualizo as listas de letras certas e letras erradas. Caso seja errada, incremento o número de erros para que o board atualize com mais uma parte desenhada.

Fim de Jogo

Para verificar se o jogo terminou e se o jogador perdeu ou ganhou implementamos os métodos hangman_over() e hangman_won().

# Método para verificar se o jogo terminou
def hangman_over(self):
if self.erros == 6:
return True
# Método para verificar se o jogador venceu
def hangman_won(self):
if self.hidden == self.word:
return True

O método hangman_over() verifica a quantidade de erros, se for igual a 6 (limite de partes do boneco) o jogador perdeu e hangman_over() retorna True.

Já o método hangman_won() verifica a string hidden, se for igual a palavra escolhida o hangman_won() retorna True.

Executando o programa

Com os métodos individualmente implementados chegou a hora de juntar tudo e montar a lógica da execução do jogo e como esses métodos interagirão entre si.

Implementando a função main()

def main():
# Objeto
game = Hangman(rand_word())
game.hide_word()
# Enquanto o jogo não tiver terminado, print do status, solicita uma letra e faz a leitura do caracter while game.erros < 6:
# Verifica o status do jogo
game.print_game_status()
letra = input("\nDigite uma letra: ")
game.guess(letra)
# De acordo com o status, imprime mensagem na tela para o usuário
if game.hangman_won():
print('\nParabéns! Você venceu!!')
print('A palavra era ' + game.word)
break
if game.hangman_over():
print('\nGame over! Você perdeu.')
print('A palavra era ' + game.word)
print('\nFoi bom jogar com você! Agora vá estudar!\n')

Bom, o primeiro passo aqui é instanciar um objeto da Classe Hangman para que possamos manipulá-lo em seguida. Fazemos isso através do construtor da classe passando o rand_word() como parâmetro para escolha da palavra.

Em seguida, enquanto o número de erros não tiver chegado ao limite o jogador informa uma letra. Passo a letra informada para o método guess() que verifica a letra e atualiza as listas e a quantidade de erros.

Se após a letra informada o método hangman_won() retornar True, significa que o jogador venceu, logo o loop é quebrado e as mensagens de vitória são impressas na tela. Se por outro lado o método hangman_over() retornar True significa que o jogador perdeu e as mensagens de derrota são impressas na tela.

Conclusão

Assim como meu primeiro projeto, meu objetivo com este desafio era testar meus conhecimentos de POO, por isso concluí o projeto sabendo de suas limitações, como por exemplo:

Problemas detectados

  • Não tratamento da entrada — Se o jogador inserir mais de uma letra ou um número o programa não está preparado para lidar com isso
  • Letra repetida — Se o jogador informar uma letra já informada anteriormente o programa não considera como uma entrada inválida e verifica tudo novamente.
  • Performance comprometida — Apesar de ser um jogo simples algumas das soluções encontradas não são as melhores escolhas a título de performance e existem soluções mais rápidas e até “elegantes”.

Ainda assim valeu a pena?

Com certeza! Gosto bastante dessa sensação de desafio de “quebrar a cabeça” programando e testando o código várias vezes e ir vendo o resultado tomando forma. Mesmo com os problemas mencionados acima fiquei muito satisfeito com o resultado e decidi compartilhá-lo aqui.

Penso que um dos “segredos” de qualquer processo evolutivo é aprender com os próprios erros (modelos de ML não supervisionados que o digam). Por isso achei importante expor as minhas limitações de agora e poder comparar no futuro em como elas foram superadas.

Se você quiser baixar o código completo pode acessar o meu perfil no Github bem aqui. Se quiser verificar a solução proposta pelo instrutor do curso pode consultar o código aqui.

Bônus

Se a Programação Orientada a Objetos aplicada a construção de jogos te interessou, dá uma olhada nesse link pra conhecer diversos projetos de construção de games em #python.

https://copyassignment.com/category/game-in-python/

Se quiser ficar por dentro de outros artigos sobre Python e Ciência de Dados, segue o dados-de-cientista pra crescermos juntos. E para acompanhar minha atividade profissional esse é o meu linkedin.

Grande abraço e até a próxima! 😉

--

--

Giovanni Angelo
Dados de Cientista

Cientista de Dados em formação | Coordenador de Sistemas | Administrador de Dados | Carioca