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
|
1. Picking a random word and checking answers
|
||||||
2. Replacing blanks with guesses
|
2. Replacing blanks with guesses
|
||||||
3. Checking if the player has won *(this step)*
|
3. Checking if the player has won
|
||||||
4. Keeping track of the player's lives
|
4. Keeping track of the player's lives *(this step)*
|
||||||
5. Improving the user experience
|
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.
|
**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 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
|
## 📋 Requirements
|
||||||
|
|
||||||
- Python 3.13 or newer
|
- Python 3.13 or newer
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
@brief Constants shared across the Hangman game.
|
@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.
|
## Character shown for a letter that has not been guessed yet.
|
||||||
BLANK = "_"
|
BLANK = "_"
|
||||||
## Words the computer picks from.
|
## Words the computer picks from.
|
||||||
@@ -26,3 +28,5 @@ WORD_LIST = [
|
|||||||
|
|
||||||
## Message shown when the player wins.
|
## Message shown when the player wins.
|
||||||
MSG_WIN = "You win!"
|
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
|
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:
|
def choose_word(words: list[str] | None = None) -> str:
|
||||||
@@ -32,25 +32,31 @@ class Game:
|
|||||||
@brief State of one Hangman 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.
|
@brief Start a new game.
|
||||||
@param word The secret word.
|
@param word The secret word.
|
||||||
|
@param lives Wrong guesses allowed before losing.
|
||||||
"""
|
"""
|
||||||
## The secret word.
|
## The secret word.
|
||||||
self.word = word.lower()
|
self.word = word.lower()
|
||||||
|
## Lives left.
|
||||||
|
self.lives = lives
|
||||||
## Letters guessed so far.
|
## Letters guessed so far.
|
||||||
self.guesses: set[str] = set()
|
self.guesses: set[str] = set()
|
||||||
|
|
||||||
def guess(self, letter: str) -> bool:
|
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.
|
@param letter A single letter.
|
||||||
@return True if the letter is in the word.
|
@return True if the letter is in the word.
|
||||||
"""
|
"""
|
||||||
letter = letter.lower()
|
letter = letter.lower()
|
||||||
self.guesses.add(letter)
|
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:
|
def display(self) -> str:
|
||||||
"""!
|
"""!
|
||||||
@@ -65,3 +71,17 @@ class Game:
|
|||||||
@brief True when every letter of the word has been guessed.
|
@brief True when every letter of the word has been guessed.
|
||||||
"""
|
"""
|
||||||
return all(c in self.guesses for c in self.word)
|
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.
|
@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
|
from hangman.game import Game, choose_word
|
||||||
|
|
||||||
|
|
||||||
@@ -17,18 +17,18 @@ def say(message: str = "") -> None:
|
|||||||
|
|
||||||
def play(game: Game) -> 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.
|
@param game The game to play.
|
||||||
"""
|
"""
|
||||||
say(game.display())
|
say(game.display())
|
||||||
while not game.won:
|
while not game.over:
|
||||||
letter = input("Guess a letter: ").strip()
|
letter = input("Guess a letter: ").strip()
|
||||||
if game.guess(letter):
|
if game.guess(letter):
|
||||||
say(f"Right, '{letter.lower()}' is in the word.")
|
say(f"Right, '{letter.lower()}' is in the word.")
|
||||||
else:
|
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(game.display())
|
||||||
say(MSG_WIN)
|
say(MSG_WIN if game.won else MSG_LOSE + game.word)
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
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
|
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 = Game("hi")
|
||||||
g.guess("x")
|
g.guess("x")
|
||||||
assert not g.won
|
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"])
|
out = run(monkeypatch, capsys, "hi", ["z", "h", "i"])
|
||||||
assert out.splitlines() == [
|
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.",
|
"Right, 'h' is in the word.",
|
||||||
"h _",
|
"h _",
|
||||||
@@ -26,3 +26,9 @@ def test_play_until_won(monkeypatch, capsys):
|
|||||||
def test_repeated_letter_fills_all_blanks(monkeypatch, capsys):
|
def test_repeated_letter_fills_all_blanks(monkeypatch, capsys):
|
||||||
out = run(monkeypatch, capsys, "aa", ["a"])
|
out = run(monkeypatch, capsys, "aa", ["a"])
|
||||||
assert out.splitlines()[-2:] == ["a a", "You win!"]
|
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