Jan 30th, 2024
Graduer en tant qu'ingénieur mécanique ne m'a presque pas fourni les connaissances de base nécessaires en programmation (à l'exception des mathématiques et un peu de Matlab). Au cours de mes années de codage professionnel, j'ai accumulé d'énormes connaissances en informatique, mais de temps en temps, je ressens encore qu'il y a beaucoup de place pour s'améliorer.
C'est pourquoi j'ai décidé de suivre le cours CS50 de Harvard. Il s'agit du premier cours CS50 de Harvard que je me suis fixé pour terminer.
Chaque cours a un projet final, et ma mise en œuvre de celui-ci sera présentée dans ces publications de blog liées à CS50.
Le jeu suit les règles standards du jeu du pendu:
Le jeu a deux modes :
Selon les règles standard du jeu du pendu, une partie du pendu est dessinée pour chaque essai infructueux. Si vous atteignez dix tentatives infructueuses, un dessin complet sera réalisé.
Le Pendu - Écran de démarrage
Après avoir terminé le jeu (gagné ou perdu), vous pouvez commencer une autre partie ou quitter le programme.
Le Pendu - Partie perdue
Le Pendu - Partie en cours
Le Pendu - Partie gagnée
Pour jouer au jeu, clonez le projet, installez Python, déplacez-vous dans le dossier racine du dépôt, installez les dépendances avec
pip install -r requirements.txt
et exécutez
python project.py
dans le terminal.
project.py
- comme indiqué dans les directives, le code se compose d'une fonction principale et de trois fonctions au même niveau, nécessaires pour les tests. Cependant, pour des raisons de conservation de l'état, j'ai initialisé et conservé l'ensemble de mon jeu à l'intérieur d'une classe Game, qui se trouve dans le même fichier. Je voulais éviter l'utilisation de variables globales.
Les trois fonctions requises au même niveau d'indentation que la fonction principale implémentent des appels aux méthodes de la classe Game afin que les exigences finales du projet soient remplies. Mais je tiens à souligner que toutes les méthodes de la classe ont également été testées de manière approfondie.
test_project.py
- contient tous les tests du projet
requirements.txt
- contient toutes les dépendances du package et leurs versions utilisées dans le projet
data/words.py
- contient deux listes de mots anglais de cinq et six lettres utilisées lors du choix des modes de jeu
components/separators.py
- contient des fonctions utilisées pour dessiner le jeu du pendu dans l'interface en ligne de commande (CLI)
components/stages.py
- contient les étapes de chacun des dessins des tentatives incorrectes pour le jeu du pendu. Les étapes ne suivent délibérément pas le principe DRY, afin qu'elles soient plus faciles à entretenir et à examiner. Si cela était une application plus importante, où la performance serait critique, ces étapes ainsi que les dessins de l'écran de démarrage pourraient être optimisés.
Comme mentionné précédemment, la classe
Game
encapsule l'ensemble de la logique du jeu.
Voici la répartition des principales méthodes et de leurs propriétés:
def __init__(self):
self.separator_length = 40
self.game_screen = 0
self.game_mode = "0"
self.guessed_letters = []
self.used_letters = []
self.word = ""
self.failures = 0
self.already_used_letter = ""
project.py - Game class
@property
def game_screen(self):
return self._game_screen
@game_screen.setter
def game_screen(self, n):
self._game_screen = n
@property
def game_mode(self):
return self._game_mode
@game_mode.setter
def game_mode(self, n):
self._game_mode = n
@property
def guessed_letters(self):
return self._guessed_letters
@guessed_letters.setter
def guessed_letters(self, new_values):
if len(new_values) == 2 and 0 <= new_values[1] < len(self.guessed_letters):
self._guessed_letters[new_values[1]] = new_values[0]
elif len(new_values) == 1:
self._guessed_letters = new_values[0]
else:
self._guessed_letters = []
@property
def used_letters(self):
return self._used_letters
@used_letters.setter
def used_letters(self, new_values):
if len(new_values) == 2 and new_values[1]:
self._used_letters.append(new_values[0])
elif len(new_values) == 1:
self._used_letters = new_values[0]
else:
self._used_letters = []
@property
def word(self):
return self._word
@word.setter
def word(self, n):
if n == "1":
self._word = random.choice(words_five_letters).upper()
elif n == "2":
self._word = random.choice(words_six_letters).upper()
else:
self._word = n # for testing purposes
@property
def failures(self):
return self._failures
@failures.setter
def failures(self, n):
self._failures = n
@property
def already_used_letter(self):
return self._already_used_letter
@already_used_letter.setter
def already_used_letter(self, n):
self._already_used_letter = n
project.py - Game class
def run_game(self):
self.clear_terminal()
while self.game_screen <= 1:
if self.game_screen == 0:
self.start_game()
while self.game_mode != "1" and self.game_mode != "2":
user_input = input("Mode: ")
self.game_mode = user_input
self.word = user_input
self.guessed_letters = [[" " for _ in range(len(self.word))]]
self.game_screen = 1
elif self.game_screen == 1:
self.clear_terminal()
self.main_game()
if self.is_game_finished():
user_input = input("Decision: ").upper()
else:
user_input = input("Guess a letter: ").upper()
self.already_used_letter = ""
if len(user_input) == 1 and user_input.isalpha():
###
## check if hit, miss, or repeat guess
# hit
if user_input in self.word and user_input not in self.used_letters:
indexes_of_hit = [index for index, char in enumerate(self.word) if char == user_input]
for index in indexes_of_hit:
self.guessed_letters = [user_input, index]
# repeat
elif user_input in self.used_letters:
self.already_used_letter = user_input
# miss
else:
self.failures = self.failures + 1
###
# add to used letters list
if user_input not in self.used_letters:
self.used_letters = [user_input, True]
elif user_input == "YES":
self.restart()
elif user_input == "NO":
self.quit()
break
project.py - Game class
def start_game(self):
draw_separator(self.separator_length)
draw_separator(self.separator_length)
self.empty_space()
self.empty_space()
self.empty_space()
center_text(text="HANGMAN", length=self.separator_length, draw_border=True, double_border=True)
self.empty_space()
self.empty_space()
self.empty_space()
draw_separator(self.separator_length)
draw_separator(self.separator_length)
self.empty_space()
center_text(text="Enter mode to start the game", length=self.separator_length, draw_border=True)
center_text(text="[1] - Normal mode", length=self.separator_length, draw_border=True)
center_text(text="[2] - Hard mode", length=self.separator_length, draw_border=True)
self.empty_space()
draw_separator(self.separator_length)
print("2023 - Alan Jereb".rjust(self.separator_length))
center_text(text="", length=self.separator_length)
project.py - Game class
def main_game(self):
draw_separator(self.separator_length)
self.empty_space()
stage_functions = [stage.zero, stage.one, stage.two, stage.three, stage.four, stage.five, stage.six,
stage.seven, stage.eight, stage.nine, stage.ten]
if 0 <= self.failures <= 10:
stage_functions[self.failures](self.separator_length)
self.empty_space()
self.empty_space()
if self.user_has_won():
center_text(text="You win!", length=self.separator_length, draw_border=True, double_border=True)
elif self.user_has_failed():
center_text(text="You lose!", length=self.separator_length, draw_border=True, double_border=True)
self.empty_space()
if not self.user_has_failed():
center_text(text=(" ".join(self.guessed_letters)), length=self.separator_length, draw_border=True, double_border=True)
center_text(text=("__ " * len(self.word)), length=self.separator_length, draw_border=True, double_border=True)
else:
center_text(text=self.word, length=self.separator_length, draw_border=True, double_border=True)
self.empty_space()
draw_separator(self.separator_length)
if self.is_game_finished():
center_text(text="To play another game type: yes", length=self.separator_length, draw_border=True)
center_text(text="To quit the game type: no", length=self.separator_length, draw_border=True)
else:
center_text(text="Used letters:", length=self.separator_length, draw_border=True)
center_text(text=", ".join(self.used_letters[0:9]), length=self.separator_length, draw_border=True)
center_text(text=", ".join(self.used_letters[9:]), length=self.separator_length, draw_border=True)
draw_separator(self.separator_length)
if len(self.already_used_letter):
print("You have already used letter", self.already_used_letter ,"!")
else:
center_text(text="", length=self.separator_length)
center_text(text="", length=self.separator_length)
project.py - Game class
def clear_terminal(self):
if platform == "Windows":
os.system("cls")
else:
os.system("clear")
project.py - Game class
def restart(self):
self.clear_terminal()
self.game_screen = 0
self.used_letters = []
self.failures = 0
self.game_mode = "0"
project.py - Game class
Terminer le cours a été un petit projet amusant. J'ai appris suffisamment sur les bases de Python pour pouvoir construire par-dessus. Je comprends pourquoi Python est si populaire de nos jours, mais en même temps, j'ai quelques réserves à son égard. Je trouve sa syntaxe un peu difficile à lire (fonctions significativement plus longues) et j'aimerais voir Python utiliser plus d'accolades et moins d'indentation. Le manque de sécurité de type dans Python est également quelque chose à quoi je ne peux pas m'habituer.