Keeping track of the player's lives
This commit is contained in:
@@ -18,8 +18,8 @@ Day 7 of Udemy's *100 Days of Code™: The Complete Python Pro Bootcamp*. The ga
|
||||
|
||||
1. Picking a random word and checking answers
|
||||
2. Replacing blanks with guesses
|
||||
3. Checking if the player has won *(this step)*
|
||||
4. Keeping track of the player's lives
|
||||
3. Checking if the player has won
|
||||
4. Keeping track of the player's lives *(this step)*
|
||||
5. Improving the user experience
|
||||
|
||||
**Step 1:** the program picks a random word, asks for one letter and tells you whether the letter is in the word.
|
||||
@@ -28,6 +28,8 @@ Day 7 of Udemy's *100 Days of Code™: The Complete Python Pro Bootcamp*. The ga
|
||||
|
||||
**Step 3:** the game keeps asking for letters until every blank is filled, then prints "You win!". (There are no lives yet, so wrong guesses cost nothing.)
|
||||
|
||||
**Step 4:** you start with 6 lives. Every wrong guess costs one and shows the lives left. At 0 lives the game ends with "You lose" and reveals the word.
|
||||
|
||||
## 📋 Requirements
|
||||
|
||||
- Python 3.13 or newer
|
||||
|
||||
+29
-25
@@ -1,28 +1,32 @@
|
||||
"""!
|
||||
@file constants.py
|
||||
@brief Constants shared across the Hangman game.
|
||||
"""
|
||||
|
||||
## Character shown for a letter that has not been guessed yet.
|
||||
BLANK = "_"
|
||||
## Words the computer picks from.
|
||||
WORD_LIST = [
|
||||
"aardvark",
|
||||
"baboon",
|
||||
"camel",
|
||||
"dolphin",
|
||||
"elephant",
|
||||
"flamingo",
|
||||
"giraffe",
|
||||
"hedgehog",
|
||||
"iguana",
|
||||
"kangaroo",
|
||||
"leopard",
|
||||
"octopus",
|
||||
"penguin",
|
||||
"squirrel",
|
||||
"tortoise",
|
||||
]
|
||||
"""!
|
||||
@file constants.py
|
||||
@brief Constants shared across the Hangman game.
|
||||
"""
|
||||
|
||||
## Number of wrong guesses the player may make before losing.
|
||||
MAX_LIVES = 6
|
||||
## Character shown for a letter that has not been guessed yet.
|
||||
BLANK = "_"
|
||||
## Words the computer picks from.
|
||||
WORD_LIST = [
|
||||
"aardvark",
|
||||
"baboon",
|
||||
"camel",
|
||||
"dolphin",
|
||||
"elephant",
|
||||
"flamingo",
|
||||
"giraffe",
|
||||
"hedgehog",
|
||||
"iguana",
|
||||
"kangaroo",
|
||||
"leopard",
|
||||
"octopus",
|
||||
"penguin",
|
||||
"squirrel",
|
||||
"tortoise",
|
||||
]
|
||||
|
||||
## Message shown when the player wins.
|
||||
MSG_WIN = "You win!"
|
||||
## Message shown when the player loses (the word is appended).
|
||||
MSG_LOSE = "You lose. The word was: "
|
||||
|
||||
+24
-4
@@ -5,7 +5,7 @@
|
||||
|
||||
import random
|
||||
|
||||
from hangman.constants import BLANK, WORD_LIST
|
||||
from hangman.constants import BLANK, MAX_LIVES, WORD_LIST
|
||||
|
||||
|
||||
def choose_word(words: list[str] | None = None) -> str:
|
||||
@@ -32,25 +32,31 @@ class Game:
|
||||
@brief State of one Hangman game.
|
||||
"""
|
||||
|
||||
def __init__(self, word: str) -> None:
|
||||
def __init__(self, word: str, lives: int = MAX_LIVES) -> None:
|
||||
"""!
|
||||
@brief Start a new game.
|
||||
@param word The secret word.
|
||||
@param lives Wrong guesses allowed before losing.
|
||||
"""
|
||||
## The secret word.
|
||||
self.word = word.lower()
|
||||
## Lives left.
|
||||
self.lives = lives
|
||||
## Letters guessed so far.
|
||||
self.guesses: set[str] = set()
|
||||
|
||||
def guess(self, letter: str) -> bool:
|
||||
"""!
|
||||
@brief Process a guess and reveal the letter if it is in the word.
|
||||
@brief Process a guess; a wrong guess costs a life.
|
||||
@param letter A single letter.
|
||||
@return True if the letter is in the word.
|
||||
"""
|
||||
letter = letter.lower()
|
||||
self.guesses.add(letter)
|
||||
return is_in_word(self.word, letter)
|
||||
correct = is_in_word(self.word, letter)
|
||||
if not correct:
|
||||
self.lives -= 1
|
||||
return correct
|
||||
|
||||
def display(self) -> str:
|
||||
"""!
|
||||
@@ -65,3 +71,17 @@ class Game:
|
||||
@brief True when every letter of the word has been guessed.
|
||||
"""
|
||||
return all(c in self.guesses for c in self.word)
|
||||
|
||||
@property
|
||||
def lost(self) -> bool:
|
||||
"""!
|
||||
@brief True when no lives are left.
|
||||
"""
|
||||
return self.lives <= 0
|
||||
|
||||
@property
|
||||
def over(self) -> bool:
|
||||
"""!
|
||||
@brief True when the game has been won or lost.
|
||||
"""
|
||||
return self.won or self.lost
|
||||
|
||||
+5
-5
@@ -3,7 +3,7 @@
|
||||
@brief Command-line interface for Hangman.
|
||||
"""
|
||||
|
||||
from hangman.constants import MSG_WIN
|
||||
from hangman.constants import MSG_LOSE, MSG_WIN
|
||||
from hangman.game import Game, choose_word
|
||||
|
||||
|
||||
@@ -17,18 +17,18 @@ def say(message: str = "") -> None:
|
||||
|
||||
def play(game: Game) -> None:
|
||||
"""!
|
||||
@brief Ask for letters until every blank is filled.
|
||||
@brief Ask for letters until the word is guessed or the lives run out.
|
||||
@param game The game to play.
|
||||
"""
|
||||
say(game.display())
|
||||
while not game.won:
|
||||
while not game.over:
|
||||
letter = input("Guess a letter: ").strip()
|
||||
if game.guess(letter):
|
||||
say(f"Right, '{letter.lower()}' is in the word.")
|
||||
else:
|
||||
say(f"Wrong, '{letter.lower()}' is not in the word.")
|
||||
say(f"Wrong, '{letter.lower()}' is not in the word. Lives left: {game.lives}")
|
||||
say(game.display())
|
||||
say(MSG_WIN)
|
||||
say(MSG_WIN if game.won else MSG_LOSE + game.word)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
+22
-1
@@ -1,4 +1,4 @@
|
||||
from hangman.constants import WORD_LIST
|
||||
from hangman.constants import MAX_LIVES, WORD_LIST
|
||||
from hangman.game import Game, choose_word, is_in_word
|
||||
|
||||
|
||||
@@ -45,3 +45,24 @@ def test_wrong_guesses_do_not_win():
|
||||
g = Game("hi")
|
||||
g.guess("x")
|
||||
assert not g.won
|
||||
|
||||
|
||||
def test_wrong_guess_loses_life():
|
||||
g = Game("cat")
|
||||
assert g.lives == MAX_LIVES
|
||||
g.guess("z")
|
||||
assert g.lives == MAX_LIVES - 1
|
||||
|
||||
|
||||
def test_correct_guess_keeps_lives():
|
||||
g = Game("cat")
|
||||
g.guess("c")
|
||||
assert g.lives == MAX_LIVES
|
||||
|
||||
|
||||
def test_lost_when_out_of_lives():
|
||||
g = Game("hi", lives=2)
|
||||
g.guess("x")
|
||||
assert not g.lost
|
||||
g.guess("y")
|
||||
assert g.lost and g.over and not g.won
|
||||
|
||||
+7
-1
@@ -13,7 +13,7 @@ def test_play_until_won(monkeypatch, capsys):
|
||||
out = run(monkeypatch, capsys, "hi", ["z", "h", "i"])
|
||||
assert out.splitlines() == [
|
||||
"_ _",
|
||||
"Wrong, 'z' is not in the word.",
|
||||
"Wrong, 'z' is not in the word. Lives left: 5",
|
||||
"_ _",
|
||||
"Right, 'h' is in the word.",
|
||||
"h _",
|
||||
@@ -26,3 +26,9 @@ def test_play_until_won(monkeypatch, capsys):
|
||||
def test_repeated_letter_fills_all_blanks(monkeypatch, capsys):
|
||||
out = run(monkeypatch, capsys, "aa", ["a"])
|
||||
assert out.splitlines()[-2:] == ["a a", "You win!"]
|
||||
|
||||
|
||||
def test_play_until_lost(monkeypatch, capsys):
|
||||
out = run(monkeypatch, capsys, "a", list("bcdefg"))
|
||||
assert "Lives left: 0" in out
|
||||
assert out.splitlines()[-1] == "You lose. The word was: a"
|
||||
|
||||
Reference in New Issue
Block a user