Ruby is my timewaster

August 27th, 2007

A few weeks ago, someone on a message board I frequent asked for some good timewasters while their boss was out of town. Someone came up with Make-A-Word, a sort of half-assed Boggle. The only goal seems to be to use the given letters to make as many words as possible in two minutes, with more points given for longer words. Unlike Boggle, the letters don’t have to be next to each other to make a valid word, which I didn’t figure out until I’d gotten horrible scores a few dozen times.

Once I figured it out, my score jumped up a bit, from the 5th percentile to the 30th. Not good enough. Like any nerd worth his salt, I had to figure out a way to beat the system. I turned to my real favorite timewaster: coding. Having fallen in love with Ruby a year or so ago, it seemed like a natural choice.

    The basic rules for the game:

  • Words must be at least 2 and no more than 10 letters in length
  • Words can only consist of the 9 letters given, but don’t have to include all 9 letters (duh).
  • Words cannot be proper nouns (no names) or have punctuation.
  • You can’t type the words in. You must use their JavaScript keypad and click with the mouse (usability nightmare).

First things first, I needed a dictionary. Being on Windows (shut up), I couldn’t use the trusty /usr/share/dict/words. However, I found a version online that was essentially the same file stripped of proper nouns and punctuation. That easily took care of one of the rules. Now that I had a dictionary, I needed a plan, and I went with my first:

  1. Read each line from the dictionary file (one word per line)
  2. If the word was too short or too long, trash it and go to the next word
  3. If the word consisted of only the required letters, add it to a list

All of them were fairly easy to accomplish, and the only one that might add complexity to the script would be comparing the word to verify it only contained the required letters. I decided to go with a regular expression after a few shamefully naive implementations. After that, it was just 10 quick lines away. Here’s the final implementation:

def get_possible_words(dictionary, required_letters, min_length = 1, max_length = -1)
    required_letters = required_letters.split(//).sort.uniq.join # no duplicate letters

    re_required_letters = Regexp.new(“^[#{required_letters}]*$“) # ensure only required letters are used
    possible_words = []

    File.readlines(dictionary).each { |line|
        len = line.strip!.length

        # only add the word if length fits and it matches the regex

        possible_words << line if (len >= min_length and line =~ re_required_letters) unless (max_length >= min_length and len > max_length)
    }
    possible_words

end

So, am I done? I’ve got a way to get the words, but I have no idea how fast it’ll run, and I’ve got to click all those buttons. Better get the longest words first to maximize my score. Simple enough:

def get_possible_words_longest_first(dictionary, required_letters, min_length, max_length)
    get_possible_words(dictionary, required_letters, min_length, max_length).sort{|x,y| y.length <=> x.length}

end

def list_possible_words(dictionary, required_letters, min_length, max_length)
    get_possible_words_longest_first(dictionary, required_letters, min_length, max_length).each{ |word| puts word }
end

required_letters = ‘qwertasdfgzxcvb‘
dictionary = “words“ #  /usr/share/dict/words stripped of proper nouns and punctuation

list_possible_words(dictionary, required_letters, 9, 40)

It should be obvious that required_letters is where you enter the letters from the game. I could have made this take a command-line argument, but I just run it from inside SciTE, and I’m lazy. You should also have noticed that the string I used (qwertasdfzxcvb) is longer than the 9 letters you’ll get in the game.

Remember learning that “stewardesses” is the longest English word you can type using only your left hand? That’s wrong, for three reasons:

  1. I can type one-handed.
  2. “Sweaterdresses” comes in at 14 letters, two more than “stewardesses”, which only has 12.
  3. The following words have the same length as “stewardesses”:
  • reaggregated
  • decerebrated
  • decerebrates
  • desegregated
  • desegregates
  • extravagated
  • extravagates
  • extravasated
  • extravasates
  • aftereffects
  • reaggregates
  • resegregated
  • resegregates
  • reverberated
  • reverberates
  • sweaterdress
  • abracadabras
  • watercresses

The longest right-handed words?

  • hypolimnion
  • homophony

There you go, 19 words you can type using only your left hand that are as long or longer than “stewardesses”, and the two longest right-handed words in the English language. You can at least say you learned something today. Back to the nerdery.

While I’ve certainly played a lot of Starcraft, there’s no way in hell I’m going to click those buttons fast enough to get more than 20 or so words in, which means I’ll be stuck in the mid 1000’s score-wise (i.e. near the top, but not the best). What’s the point of cheating if you can’t decimate the competition?

Since Make-A-Word uses buttons, and doesn’t submit, they must be using Javascript. If you have Firebug for Firefox, you may have noticed that the console allows you to enter your own Javascript, within the scope of the page. Make-A-Word’s JS simply calls a function named “addletter” on each button-click, and another named “addit” to add the word. All we have to do now is loop through the words, and for each one, loop through the letters, outputting the call to “addletter”, and ending with the call to “addit”. Here’s the code for that:

def cheat_like_hell_on_nerdtests(dictionary, required_letters, min_length, max_length)
    get_possible_words_longest_first(dictionary, required_letters, min_length, max_length).each{ |word|
        word.split(//).each { |letter| puts “addletter(’#{letter}‘);“ }
        puts “addit();“

    }
end

Simply run the script, copy the output, and run it in Firebug’s console. The cutting and pasting turned out to be the longest part, and I had to find something to occupy the remaining minute and 50 seconds. Admittedly, I could’ve had it just output a Javascript array and do the iteration in the Firebug console, but this only took me 10 minutes from start to finish, and I have no current need to make it run any faster, as you can see:

Classy folks, obviously

As with most games, once you know how to cheat, it ceases to be fun, and ruins it for everyone else, so I only recorded my score for one game. Hell, I had more fun writing the script than the game could ever have given me.

One Response to “Ruby is my timewaster”

  1. Ruby is my timewaster, part 2 (Alienware contest) « The AntiChris Says:

    [...] does this have to do with Ruby? Well, I was able to rewrite a few lines of my word finder from Ruby is my timewaster to search for words where you know the length of the word, a few letters, and their positions, like [...]

Leave a Reply