step 4
TirSystem/github-action: Sync GitHub mirror metadata / sync-metadata (push) Successful in 4s

Keeping track of the player's lives
This commit is contained in:
2026-10-04 12:41:43 +08:00
parent ce87f23314
commit a76df46b79
6 changed files with 91 additions and 38 deletions
+4 -2
View File
@@ -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
+4
View File
@@ -3,6 +3,8 @@
@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.
@@ -26,3 +28,5 @@ WORD_LIST = [
## 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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"