-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhangman.py
128 lines (101 loc) · 3.8 KB
/
hangman.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
"""
Rewrite of hangman_game.py
"""
import random
from utils import input_with_timeout, TimeoutExpired
def get_fruits() -> list[str]:
"""
Returns a list of fruit names.
"""
with open("./fruits.txt", encoding="utf-8") as fruits:
content = fruits.read().split()
return content
class Hangman:
"""
Base game class.
"""
def __init__(self) -> None:
self.fruit = random.choice(get_fruits())
# positions_to_show is a list of indices of string self.fruit
# which are going to be showed as hint, while rest of the
# indices will be filled with underscores
positions_to_show = self.decide_positions_to_show(self.fruit)
# running a list comprehension which basically
# includes indices of self.fruit which are in positions_to_show
# and replaces rest of the characters with _
self.fruit_with_blanks = [
self.fruit[i] if i in positions_to_show else "_"
for i in range(0, len(self.fruit))
]
# determining the first _, i.e., the first position to guess
self.position = self.fruit_with_blanks.index("_")
self.chances = len(self.fruit)
@staticmethod
def decide_positions_to_show(fruit: str) -> list[int]:
"""
Returns a list of indices to show.
"""
length = len(fruit)
number_of_positions = round(0.5 * length)
if number_of_positions == length:
number_of_positions -= 1
indices = []
for _ in range(number_of_positions):
# length-1 cuz randint is inclusive
idx = random.randint(0, length - 1)
# while loop to ensure non duplicate index
while idx in indices:
idx = random.randint(0, length - 1)
indices.append(idx)
return indices
def guess_next_char(self):
"""
Interface for letting user guess next empty character.
"""
print(" ".join(self.fruit_with_blanks))
try:
guess = input_with_timeout(
f"Guess next({self.position + 1}th) character: ", 15
)
except TimeoutExpired:
print("\nTimeout!")
self.chances -= 1
print(f"Remaining chances: {self.chances}")
print("\n------------------------------\n")
return
if guess.lower() == self.fruit[self.position]:
print("Correct guess!")
# updating self.fruit_with_blanks
self.fruit_with_blanks[self.position] = self.fruit[self.position]
try:
self.position = self.fruit_with_blanks.index("_")
# passing except here because when game is finished,
# there are no `_` present if self.fruit_with_blanks
# and hence index method returns a value error
except ValueError:
pass
else:
print("Incorrect!")
self.chances -= 1
print(f"Remaining chances: {self.chances}")
print("\n------------------------------\n")
def play(self):
"""
Main game loop.
"""
print("Welcome to the Hangman game.")
print("You'll be alloted only 15s to guess.")
print(f"Total number of chances to guess the fruit name: {self.chances}.")
input("Press enter to start the game.")
print("\n-----------------------------------------\n")
# run until there is an `_` present and chances are left
while "_" in self.fruit_with_blanks and self.chances > 0:
self.guess_next_char()
if self.chances > 0:
print("You won the game!")
else:
print("You lost the game!")
print(f"The fruit name was `{self.fruit}`.")
if __name__ == "__main__":
game = Hangman()
game.play()