Confused by Code

Word Finder

A few months ago I decided to test my programming skills. I wanted to write a program that could find all the words in a set of random letters. Or as a less honorable man might say, cheat at Scrabble.

The first thing I needed was a dictionary. I managed to find a free HTML version of Webster's dictionary, but there was a catch. It was from 1913. That wasn't a major problem, but some modern words were missing. Apparently "zoo" was not yet considered a word.

Once I had the dictionary, I wrote a short script that uses the lxml module to put all the words into a list. Then I wrote the main program, which uses the itertools module to find all of the possible combinations that can be made with a set of letters. The program checks if each combination is in the list of words from the dictionary, and if it is, it is added to the list of results.

While the program could hypothetically work with any number of letters, it takes a long time to run when more than 10 letters are entered. The program takes about a minute and a half for 11 letters, and about 19 minutes for 12 letters. The time it takes increases sharply because the number of combinations is a factorial a.k.a. n!. I suspect mathematicians chose to represent factorials with exclamation points because they were shocked by the sheer size of the numbers. For example, 10! equals 3,628,800. 11! is 39,916,800.

I wrote several drafts of my program. The first was just a command line program. The second had a GUI I built with Tkinter. The third is the one below, which is made for the web. In the process of trying to maintain the function of the program while changing its form, I learned a bit about good object-oriented programming. I created two classes. The class that does all the work is exactly the same in each version of the program, but there's a different class for each user interface. In fact, the WordMachine class could potentially be used in a different program all together - it's completely self contained. Modular, in geek-speak.

Go ahead and give it a try. I limited the input to 10 letters. You can see the complete code below.

              
from itertools import *
from eds_words3 import all_words
import cgi

# all_words is a set containing every word in the dictionary.

class WordMachine(object):

    def __init__(self, letters):
        self.actual_words = []
        self.letters = letters
        self.spots = len(self.letters) + 1

    def word_maker(self, combos):
        for item in combos:
            word = "".join(list(item)).lower()
            if word in all_words and not word in self.actual_words:
                self.actual_words.append(word)

    def combo_maker(self):
        for i in range(1, self.spots):
            combos = permutations(self.letters, i)
            self.word_maker(combos)

    def print_results(self):
        print "Actual words you can make with your letters:"
        for word in self.actual_words:
            print word
        print "\nEnjoy your words!"


class Interface(object):

    def __init__(self):
        self.form = cgi.FieldStorage()
    
    def return_results(self):        
        letters = self.form.getvalue('letters')
        mywords = WordMachine(letters)
        mywords.combo_maker()
        print "Content-Type: text/html\n\n"

        print ('<!DOCTYPE html>\n...') # page template
        for word in mywords.actual_words:
            print "<li>%s</li>" % word
        print ('</ul>\n...') # rest of page template

ui = Interface()
ui.return_results()