forked from tiyd-python-2015-05/mystery-word
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdemon_words.py
252 lines (178 loc) · 8.33 KB
/
demon_words.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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
import random
import os
import re
import interface
###############################################################################
def choose_best_list(word_list, current, guess):
"""Takes a list and two strings. Creates a dictionary where the keys are
maps of where the guessed letter could appear in a word and the values are
lists of the subset of words from the input list where the letter appears
in those locations (example: {"...e":["fate", "lone"], ".ee.":["need"]}).
It will return the value list of the greatest length, choosing randomly in
case of ties and choosing the option that does not contain the guess if
the dictionary only has two keys. It will also return the corresponding key
after it has merged the key with the current state of the game (e.g if the
game was currently restricted to words that met the condition ".n.rt" and
the choose_best_list function determined the best subset to be those meeting
the condition "i...." the function would return "in.rt").
Keyword Arguments:
word_list -- the current list of possible words
current -- the current state of the game (".n.rt" in the example above)
guess -- the user-input guess (a single letter string)
"""
families = choice_types(word_list, guess)
length = 0
largest_group = []
largest_group_name = ""
if len(families) == 2:
largest_group_name, largest_group = tie_breaker(families)
else:
for member in families:
if len(families[member]) > length:
length = len(families[member])
largest_group = families[member]
largest_group_name = member
largest_group_name = merge_words(current, largest_group_name,
filler=".", joiner="", capital=False)
return largest_group_name, largest_group
###############################################################################
def choice_types(word_list, guess):
"""Take a list and a string(intended to be a single letter). Copies the list
and iterates through it. For each word, it checks each letter and if that
letter does not match the input "guess" string that letter is replaced with
the string ".". Each word thus treated is appended to a new list, which the
function returns when complete.
Keyword Arguments:
word_list -- the current list of possible words
guess -- the user-input guess (a single letter string)
"""
families = {}
words = word_list[:]
search = r'[^' + guess + ']'
for word in words:
blanked_word = re.sub(search, ".", word)
families[blanked_word] = families.get(blanked_word, [])
families[blanked_word].append(word)
return families
###############################################################################
def tie_breaker(dictionary):
"""Takes a dictionary. For each key, checks the number of characters in that
key that are the string ".". Returns the key with the highest number along
with its corresponding value.
Keyword Arguments:
dictionary -- the dictionary that the function iterates through.
"""
number_blanks = 0
choice = ""
for key in dictionary:
count = 0
for letter in key:
if letter == ".":
count += 1
if count > number_blanks:
number_blanks = count
choice = key
return choice, dictionary[choice]
###############################################################################
def choose_length(a_list):
"""Takes a list of strings. Picks a number at random between 6 and 25 and
returns as a list the subset of strings from the input list that are that
length.
Keyword Arguments:
a_list -- the list of strings to be filtered.
"""
random_length = random.choice(range(6,25))
demon_list = [word for word in a_list if len(word) == random_length]
return demon_list, random_length
###############################################################################
def merge_words(word, guess, to_replace=".", filler="_", joiner=" ", capital=True):
"""Takes a maximum of 5 strings and 1 boolean (minimum 2 strings). The
first 2 strings ("word" and "guess") must be the same length. Essentially,
the function creates a new string list using the "filler" connected by the
"joiner". Each "filler" will be replaced by the character with the
corresponding index from both "word" and "guess" if the character at that
index in either of those strings is not equal to "to_replace". The "capital"
argument controls uppercasing of the resulting string.
Examples:
merge_words("t...", "..e.", "." , "." , "" ,False) == "t.e."
merge_words("t...", "..e.") == "T _ E _"
assert merge_words("t...", "....") == "T _ _ _"
Keyword Arguments:
word -- The first string to be used in the merge
guess -- The second string to be used in the merge
to_replace -- The character in "word" and "guess" to be found and replaced
by the "filler" argument. Default "."
filler -- The character to replace "to_replace" the resulting merged string.
Default "_"
joiner -- The character(s) to be used to join the resulting string
capital -- Boolean controlling if the merged string will be uppercased or not
(True for upper, False for not). Default True
"""
display_list = [filler for letter in word]
for index in range(len(display_list)):
if word[index] != to_replace:
display_list[index] = word[index]
elif guess[index] != to_replace:
display_list[index] = guess[index]
if capital:
return joiner.join(display_list).upper()
else:
return joiner.join(display_list)
###############################################################################
def demon_guess(word_list, length):
"""Takes a list and an int. Intitializes two empty lists, a variable equal
to the list, and a variable equal to a string == ("." * input int). Asks
the user to guess a letter in the string. If the guess is 'quit' the function
breaks. If the guess returns a new state of game different than the current
state of game, it adds that letter to a list of correct guesses and if
not it adds the letter to a list of incorrect guesses. The function will
continue to loop and ask for user input until either the list of
correct guesses contains all the letters in the current state of game
(including blanked out letters) or the list of incorrect guesses reaches a
length of 8, in which case the function will return a win or loss
message (dependent on outcome).
Keyword Arguments:
word_list -- the list of possibile words the function will use to try and
dodge the user's guess
length -- the length that all possible words are equal to (used to initialize
current state of game)
"""
words = word_list
incorrect_guesses = []
correct_guesses = []
current_state = ("." * length)
while interface.win_lose(current_state, correct_guesses, incorrect_guesses, words):
guess = interface.guess_input((correct_guesses + incorrect_guesses))
# ^handles user input
if guess != 'quit':
new_states = choose_best_list(words, current_state, guess)
if new_states[0] != current_state:
correct_guesses.append(guess)
current_state = new_states[0]
else:
incorrect_guesses.append(guess)
else:
os.system('clear')
print("Goodbye! Thanks for Playing!")
break
words = new_states[1]
os.system('clear')
print(
" (_(\n ('')\n_ \"\ )>,_ .-->\n_>--w/((_ >,_.'\n ///\n \"`\"")
interface.guess_display(
current_state, correct_guesses, incorrect_guesses)
###############################################################################
def main():
"""Initializes "demon" mode and controls game flow."""
words, length = choose_length(
interface.import_words('/usr/share/dict/words'))
interface.word_length(words[0])
demon_guess(words, length)
if interface.play_again():
return interface.main()
else:
os.system('clear')
return
###############################################################################
if __name__ == '__main__':
main()