I'm not the first person to wonder about those semicolons, but I might be the first to explain what they do!
They are used to teminate a line early, so that you can put two logical lines on the same physical one, such as in:
a = 1; b = 2
This is normally frowned upon (this in particular should be written a, b = 1, 2) and use where not needed is a very dangerous sight to the untrained Pythonista. Use with care.
From the the prompt function:
The parameter's name should be something meaningful like question.
Writiting (Y/n) with the Y in capitals has the standard meaning of "defaults to yes". If this is not wanted you should use (y/n).
Stylistically, you should either use a tuple or a set for if ans in ["y", "n"].
I would do:
def prompt(question):
while True:
ans = raw_input(question + " (Y/n) ") or "y"
if ans.lower() in ("y", "n"):
return ans == "y"
For loadWords:
The name breaks convention; it should probably be load_words.
It should not use the global directly, or it should use it as a default parameter.
You do too much work inside the with after you finish reading it. It's a good habit to
minimize the are under contexts, whether they be trys or withs.
Don't read everything and then split it; utilize files being iterable.
What's up with the positioning of that comment?
You don't need the "r" parameter to open; it's the default.
I would do:
def loadWords(filename="english.txt"):
with open(filename) as f:
return [line.strip() for line in f if line]
and ditch the global.
For play
The function is too long; a standard function should be a little smaller than this. It's OK to have longer functions, but they need to be justified (for example, implementing a non-trivial algorithm).
The function does not have a single responsibility; it does both output and dealing with minor concerns like if l.isalpha().
"[DEBUG] word is %r" % (word) strangely has word in brackets. You should remove the brackets; they do nothing. Sometimes it makes sense to wrap it in a tuple ((word,)), but this is not what you did. IMHO it's even better to just print "[DEBUG] word is", repr(word).
You should probably avoid using an overarching while guesses > 0 loop; a while True with appropriate exits would be clearer since it would not imply that there is only one standard exit method.
toGuess should be to_guess, or IMHO just unguessed.
You can use a set comprehension for unguessed: {l for l in word.upper() if l.isalpha()}.
You're not checking for the length of the guess.
letters should be named something like guessed.
"{}".format is IMHO better than the old-style %s formatting.
I would thus chose something like:
import random
# If set to true, displays chosen word before each game.
# For debugging and cheating only :)
DEBUG = False
GUESSES = 10
def loadWords(filename="english.txt"):
with open(filename) as file:
return [line.strip() for line in file if line]
def prompt(question):
while True:
ans = raw_input(question + " (Y/n) ") or "y"
if ans.lower() in {"y", "n"}:
return ans == "y"
def get_guess(guessed):
while True:
guess = raw_input("Choose a letter: ").upper()
if not guess.isalpha() or len(guess) != 1:
print "That's not a letter!"
continue
if guess in guessed:
print "You have already chosen that letter!"
continue
return guess
def alert_turn(word, guesses_left, guessed, unguessed):
print "=" * 78
print "You have {} guess{} left.".format(guesses_left, "" if guesses_left == 1 else "es")
print "Word: ", " ".join("_" if c in unguessed else c for c in word.upper())
print "Letters:", " ".join(sorted(guessed))
def alert_correct():
print "Correct!"
def alert_incorrect():
print "Sorry, not in the word."
def alert_won(word, guesses_left):
print "You win with {} guess{} left!".format(guesses_left, "" if guesses_left == 1 else "es")
print "The word is", repr(word)
def alert_lost(word):
print "=" * 78
print "Sorry, you lose."
print "The word was:", repr(word)
def play(word):
word = word.lower()
guessed = set()
unguessed = {l for l in word.upper() if l.isalpha()}
guesses_left = GUESSES
if DEBUG:
print "[DEBUG] word is", repr(word)
while True:
alert_turn(word, guesses_left, guessed, unguessed)
guess = get_guess(guessed)
guessed.add(guess)
if guess in unguessed:
alert_correct()
unguessed.remove(guess)
if not unguessed:
alert_won(word, guesses_left)
return True
else:
alert_incorrect()
guesses_left -= 1
if not guesses_left:
alert_lost(word)
return False
if __name__ == "__main__":
words = loadWords()
random.shuffle(words)
while True:
# Remove a random word so it's not chosen again
play(words.pop())
if not prompt("Would you like to play again?"):
break