Intro to CS: Problem Set 2


I was able to complete this problem set in just under an hour. It was good practice. Will show my code then the result of running the unit tests.

My solution:

import random
import string

# -----------------------------------
# HELPER CODE
# -----------------------------------

WORDLIST_FILENAME = "words.txt"
VOWELS = 'aeiou'

def load_words():
    """
    returns: list, a list of valid words. Words are strings of lowercase letters.

    Depending on the size of the word list, this function may
    take a while to finish.
    """
    print("Loading word list from file...")
    # inFile: file
    inFile = open(WORDLIST_FILENAME, 'r')
    # line: string
    line = inFile.readline()
    # wordlist: list of strings
    wordlist = line.split()
    print(" ", len(wordlist), "words loaded.")
    return wordlist

def choose_word(wordlist):
    """
    wordlist (list): list of words (strings)

    returns: a word from wordlist at random
    """
    return random.choice(wordlist)

# -----------------------------------
# END OF HELPER CODE
# -----------------------------------


# Load the list of words to be accessed from anywhere in the program
wordlist = load_words()

def has_player_won(secret_word, letters_guessed):
    """
    secret_word: string, the lowercase word the user is guessing
    letters_guessed: list (of lowercase letters), the letters that have been
        guessed so far

    returns: boolean, True if all the letters of secret_word are in letters_guessed,
        False otherwise
    """
    for c in secret_word:
        if c not in letters_guessed:
            return False
    return True


def get_word_progress(secret_word, letters_guessed):
    """
    secret_word: string, the lowercase word the user is guessing
    letters_guessed: list (of lowercase letters), the letters that have been
        guessed so far

    returns: string, comprised of letters and asterisks (*) that represents
        which letters in secret_word have not been guessed so far
    """
    s = ''
    for c in secret_word:
        if c in letters_guessed:
            s += c
        else:
            s += '*'
    return s


def get_available_letters(letters_guessed):
    """
    letters_guessed: list (of lowercase letters), the letters that have been
        guessed so far

    returns: string, comprised of letters that represents which
      letters have not yet been guessed. The letters should be returned in
      alphabetical order
    """
    s = ''
    for c in string.ascii_lowercase:
        if c not in letters_guessed:
            s += c
    return s

def get_new_revealed_letter(secret_word, letters_guessed):
    """
    Parameters
    ----------
    secret_word : string
        the lowercase word the user is guessing.
    letters_guessed : list
        list of lowercase letters, the letters that have been guessed so far.

    Returns
    -------
    string - the newly revealed character.
    """
    choose_from = []
    for c in secret_word:
        if c not in letters_guessed:
            choose_from.append(c)
    new = random.randint(0, len(choose_from)-1)
    revealed_letter = choose_from[new]
    return revealed_letter

def get_unique_letters_count(s):
    """
    Parameters
    ----------
    s : string
        string to get unique count from.

    Returns
    -------
    int - the number of unique characters in s.
    """
    seen = []
    count = 0
    for c in s:
        if c not in seen:
            count += 1
            seen.append(c)
    return count


def hangman(secret_word, with_help):
    """
    secret_word: string, the secret word to guess.
    with_help: boolean, this enables help functionality if true.

    Starts up an interactive game of Hangman.

    * At the start of the game, let the user know how many
      letters the secret_word contains and how many guesses they start with.

    * The user should start with 10 guesses.

    * Before each round, you should display to the user how many guesses
      they have left and the letters that the user has not yet guessed.

    * Ask the user to supply one guess per round. Remember to make
      sure that the user puts in a single letter (or help character '!'
      for with_help functionality)

    * If the user inputs an incorrect consonant, then the user loses ONE guess,
      while if the user inputs an incorrect vowel (a, e, i, o, u),
      then the user loses TWO guesses.

    * The user should receive feedback immediately after each guess
      about whether their guess appears in the computer's word.

    * After each guess, you should display to the user the
      partially guessed word so far.

    -----------------------------------
    with_help functionality
    -----------------------------------
    * If the guess is the symbol !, you should reveal to the user one of the
      letters missing from the word at the cost of 3 guesses. If the user does
      not have 3 guesses remaining, print a warning message. Otherwise, add
      this letter to their guessed word and continue playing normally.

    Follows the other limitations detailed in the problem write-up.
    """
    print('Welcome to Hangman!')
    print('I am thinking of a word that is', len(secret_word), 'letters long.')
    guesses = 10
    letters_guessed = []
    while True:
        print('--------------')
        if guesses <= 0:
            print('Sorry, you ran out of guesses. The word was', secret_word + '.')
            return
        if has_player_won(secret_word, letters_guessed):
            print('Congratulations, you won!')
            total_score = guesses + (4 * get_unique_letters_count(secret_word)) + (3 * len(secret_word))
            print('Your total score for this game is:', total_score)
            return

        print('You have', guesses, 'guesses left.')
        print('Available letters:', get_available_letters(letters_guessed))
        guess = input('Please guess a letter: ')
        word_progress = get_word_progress(secret_word, letters_guessed)

        if with_help and guess == '!':
            if guesses >= 3:
                revealed_letter = get_new_revealed_letter(secret_word, letters_guessed)
                letters_guessed.append(revealed_letter)
                print('Letter revealed:', revealed_letter)
                print(get_word_progress(secret_word, letters_guessed))
                guesses -= 3
                continue
            else:
                print('Oops! Not enough guesses left:', word_progress)
                continue

        if not guess.isalpha() or len(guess) > 1:
            print('Oops! That is not a valid letter. Please input a letter from the alphabet:', word_progress)
            continue

        if guess in letters_guessed:
            print('Oops! You already guessed that letter:', word_progress)
            continue

        letters_guessed.append(guess.lower())
        new_word_progress = get_word_progress(secret_word, letters_guessed)

        if guess in secret_word:
            print('Good guess:', new_word_progress)
        else:
            print('Oops! That letter is not in my word:', new_word_progress)
            if guess in VOWELS:
                guesses -= 2
            else:
                guesses -= 1



# When you've completed your hangman function, scroll down to the bottom
# of the file and uncomment the lines to test

if __name__ == "__main__":
    # To test your game, uncomment the following three lines.
    secret_word = 'abcdef'
    with_help = False
    hangman(secret_word, with_help)

    # secret_word = choose_word(wordlist)
    # with_help = False
    # hangman(secret_word, with_help)

    # After you complete with_help functionality, change with_help to True
    # and try entering "!" as a guess!

    ###############

    # SUBMISSION INSTRUCTIONS
    # -----------------------
    # It doesn't matter if the lines above are commented in or not
    # when you submit your pset. However, please run ps2_student_tester.py
    # one more time before submitting to make sure all the tests pass.

Test results:

Running unit tests
test_get_available_letters (__main__.TestPS2) ... ok
test_get_available_letters_empty_list (__main__.TestPS2) ... ok
test_get_available_letters_empty_string (__main__.TestPS2) ... ok
test_get_word_progress (__main__.TestPS2) ... ok
test_get_word_progress_empty_list (__main__.TestPS2) ... ok
test_get_word_progress_empty_string (__main__.TestPS2) ... ok
test_get_word_progress_repeated_letters (__main__.TestPS2) ... ok
test_has_player_won (__main__.TestPS2) ... ok
test_has_player_won_empty_list (__main__.TestPS2) ... ok
test_has_player_won_empty_string (__main__.TestPS2) ... ok
test_has_player_won_repeated_letters (__main__.TestPS2) ... ok
test_play_game_short (__main__.TestPS2) ... ok
test_play_game_short_fail (__main__.TestPS2) ... ok
test_play_game_wildcard (__main__.TestPS2) ... ok

----------------------------------------------------------------------
Ran 14 tests in 0.000s

OK


Problem Set 2 Unit Test Results:
All correct!
Points for these tests: 5/5
 (Please note that this is not your final pset score, additional test cases will be run on submissions)