Quantcast
Channel: Simplifying a Scrabble Word Finder - Python - Code Review Stack Exchange
Viewing all articles
Browse latest Browse all 8

Answer by 200_success for Simplifying a Scrabble Word Finder - Python

$
0
0

You have some generally good ideas, especially for a beginner. However, I echo @JoshDawson's critique that your functions are not well designed to make the code readable.

Code organization

  • Function names should be verb-like. If you have calc_score(), then for consistency, you should have check_rack(), make_dict(), and print_list().

  • look_up() needs to be renamed: it doesn't actually look anything up.

  • Names like something_check() or check_something() tend to be ambiguous. What is being checked? What happens if the check fails — does it raise an exception? Your rack_check() is even more unconventional: it acts as a filter on the global variable word_master!

    Furthermore, the rack_check() function should be broken down. In Python, filtering is easy: you can use the filter() builtin function, or better yet, use a list comprehension with an if clause. Therefore, what you really want is a function can_form_word(rack_letters, word) that returns True or False, which you can then apply to the master word list.

  • As you noted, it's weird that the entire challenge is solved within a function called list_print(). That function certainly does a lot more than printing a list, as its name would suggest! A pretty good convention is just to call the main function of your program main(). Furthermore, it is customary to call that function using if __name__ == '__main__': main() at the bottom of the code.

  • It is a bad idea to bury a raw_input("Letters: ") prompt inside your dict_maker() function. Avoid doing I/O and computations in the same function, since such mixing hinders readability and code reuse. In this case, the main() function should prompt for the input.

Implementation details

  • Take advantage of list comprehensions and generator expressions to make your code concise. In general, whenever you see a pattern like

    accumulator = init_value
    for val in some_list:
        accumulator += val
    return accumulator
    

    … then that is a candidate to be replaced by a one-liner.

  • Always call open() in the context of a with block, for tidier and more reliable code.

Suggested solution

I would define three very short helper functions. Then, the entire challenge can be solved in a few lines of code within main(). The master vocabulary list is a local variable within main(), instead of being a global variable that is initialized somewhere in the middle of the code and mysteriously incorporated into rack_check().

Instead of a dict, I've opted to use a list of (score, word) tuples, which is slightly easier to sort.

LETTER_POINTS = {...}

def calc_score(word):
    """Sum the point values of the letters of the word."""
    return sum(LETTER_POINTS[letter] for letter in word)

def read_word_list(filename):
    """Read a list of words, one word per line of the file."""
    with open(filename) as f:
        return [line.rstrip('\n') for line in f]

def can_form_word(rack_letters, word):
    """
    Determine whether a word can be formed only using the letters on the rack.
    """
    return all(word.count(c) <= rack_letters.count(c) for c in word)

def main():
    """
    For some letter tiles specified by the user, print the words in the
    SOWPOD dictionary that can be formed, in descending order of their
    Scrabble scores.
    """
    vocabulary = read_word_list('sowpods.txt')
    rack_letters = raw_input("Letters: ").lower()
    words = [word for word in vocabulary if can_form_word(rack_letters, word)]
    word_scores = [(calc_score(word), word) for word in words]
    for points, word in sorted(word_scores, reverse=True):
        print('{0}-{1}'.format(word, points))

if __name__ == '__main__':
    main()

Viewing all articles
Browse latest Browse all 8

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>