Skip to main content
added 1 character in body
Source Link
Veedrac
  • 9.8k
  • 23
  • 38

I would thus chosechoose something like:

I would thus chose something like:

I would thus choose something like:

Source Link
Veedrac
  • 9.8k
  • 23
  • 38

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