Solving Hanging with Friends (Pt. 2)

Continued from Part 1, I’m off to write something to solve my Hanging with Friends games for me.

Because the two pieces of the puzzle (word list and next letter) are independent I can begin with either. I started with the letter frequency analysis because it seemed like a more tractable problem.

Ok, so it seemed easier but where should I start? Since I’m not totally clear on what it should do yet, I’ll start with the tests (aka how it should behave).

Given a list of viable words,

  • it should count how many words a letter appears in
  • it should only count a letter once per word
  • it should count each letter of the word

Here’s the full RSpec for those tests:

describe "letter frequencies" do before(:each) do @words = ["lilly", "silly"] end it "should not return nil" do Frequency.letter_frequencies(@words).should_not be_nil end it "should return a hash" do Frequency.letter_frequencies(@words).is_a?(Hash).should be_true end it "should count the number of words with that letter" do freqs = Frequency.letter_frequencies(@words) freqs[:s].should == 1 freqs[:y].should == 2 end it "should not return a frequency larger than the number of words in the list" do freqs = Frequency.letter_frequencies(@words) freqs.values.count {|c| c> @words.length}.should == 0 end end describe "letters per word" do before(:each) do word = "lilly" @letters = Frequency.letters_per_word(word) end it "should not return nil" do @letters.should_not be_nil end it "should return an array" do @letters.is_a?(Array).should be_true end it "should convert the letter to symbols" do @letters.count { |e| e.class == Symbol}.should == @letters.length end it "should only include repeated letters once" do @letters.count { |e| e == :l}.should == 1 end end

Hmm, that’s a little more straightforward than I thought. Let’s make those tests green.

My first pass:

class Frequency def self.letter_frequencies(words) freq = {} words.each do |word| letters_per_word(word).each do |letter| freq[letter] ||= 0 freq[letter] += 1 end end freq end def self.letters_per_word(word) chars = [] word.each_char do |char| char = char.to_sym chars < < char unless chars.include? char end chars end end

Ok, so more code to the tests than to the actual implementation. That seems about right...

Next we'll deal with how to make the word list contain only the words we actually want.

The most recent version of the code is available at GitHub: