# \[SUMMARY\] Word Blender (#108)

**URL:** https://rubytalk.org/t/summary-word-blender-108/34381
**Category:** ruby-talk
**Created:** [11 January 2007 13:41 UTC](https://rubytalk.org/t/summary-word-blender-108/34381 "2007-01-11T13:41:47Z")
**Posts on this page:** 8
**Page:** 1

<div class="post-metadata">

### Author: ![James\_Edward\_Gray\_II](https://avatars.discourse-cdn.com/v4/letter/j/ea5d25/32.png) [@James\_Edward\_Gray\_II](https://rubytalk.org/u/James_Edward_Gray_II)
#### Post date: [11 January 2007 13:41 UTC](https://rubytalk.org/t/summary-word-blender-108/34381/1 "2007-01-11T13:41:47Z")

</div>

I'm almost embarrassed to admit that I originally turned this quiz down. I  
thought it would be too similar to the Scrabble Stems problem we did long, long  
ago. Ben politely explained that he felt it was different enough though, and  
then sent some guy named Guido after me in a parking lot one night. That  
convinced me to actually work the problem, and I had a change of heart. I think  
we can tell from the popularity of the problem that Ben is smarter than I am, so  
I'm glad I did.

Many solvers chose to build the entire game, so we will take a look at that  
approach. Though the quiz doesn't explicitly call for it, most programmers  
chose to add scoring or a time limit to their solution to spice up the action.  
I went with the time limit and we will examine my own code below.

The first step is to get a word list, of course. There were several approaches  
to this process, since manipulating every word in the dictionary each time could  
get a little slow. My answer to this was just to cache the word list after I  
had built it once and reuse that for all future runs. Here's the code that  
handles the loading and caching:

&nbsp;&nbsp;# game date cache  
&nbsp;&nbsp;CACHE\_FILE = ".game\_words"  
&nbsp;&nbsp;  
&nbsp;&nbsp;if File.exist? CACHE\_FILE # load from cache  
&nbsp;&nbsp;&nbsp;&nbsp;word\_list = File.open(CACHE\_FILE) { |file| Marshal.load(file) }  
&nbsp;&nbsp;else # build word list  
&nbsp;&nbsp;&nbsp;&nbsp;# prepare data structure  
&nbsp;&nbsp;&nbsp;&nbsp;words\_by\_signature = Hash.new { |words, sig| words[sig] = Array.new }  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;# read dictionary  
&nbsp;&nbsp;&nbsp;&nbsp;File.foreach(ARGV.shift || "/usr/share/dict/words") do |word|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;word.downcase!  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;word.delete!("^a-z")  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;next unless word.length.between? 3, 6  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(words\_by\_signature[word.split("").sort.join] \<\< word).uniq!  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;# prepare recursive signature search  
&nbsp;&nbsp;&nbsp;&nbsp;def choices( sig,  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;seen = Hash.new { |all, cur| all[cur] = true; false },  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&blk )  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sig.length.times do |i|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;shorter = sig[0...i] + sig[(i+1)...sig.length]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unless seen[shorter]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;blk[shorter]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;choices(shorter, seen, &blk) unless shorter.length == 3  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;# prepare game data structure  
&nbsp;&nbsp;&nbsp;&nbsp;word\_list = Hash.new  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;# build game choices  
&nbsp;&nbsp;&nbsp;&nbsp;words\_by\_signature.keys.grep(/\A.{6}\Z/) do |possible|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;word\_list[possible] = words\_by\_signature[possible]  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;choices(possible) do |shorter\_signature|  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if words\_by\_signature.include? shorter\_signature  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;word\_list[possible].push(\*words\_by\_signature[shorter\_signature])  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;# cache for faster loads  
&nbsp;&nbsp;&nbsp;&nbsp;File.open(CACHE\_FILE, "w") { |file| Marshal.dump(word\_list, file) }  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;# ...

This uses Marshal to build a trivial word cache with only a few lines of code.  
If the cache exists, we slurp it back in. Otherwise we build the cache and save  
it for future runs.

To build the cache, I pluck all three to six letter words out of the indicated  
dictionary file and build a word list containing all six letter words linked to  
smaller words using the same letters.

The main trick used in this recursive grouping of words is the use of  
"signatures." A word's signature is just the sorted order of the letters in the  
word: aejms for james, for example. Comparing signatures makes it trivial to  
find words that use the same letters, since their signatures will be the same.

Using the signatures, the choices() method just removes one character at a time  
recursing through the word list. This allows me to find all of the smaller  
words that can be formed using the same letters.

If you wanted to generate the entire list of games as the quiz suggested, the  
above is all you need. Each key is one possible round with the values being the  
words that can be matched in that round.

I wanted to play though and built a full game interface. My interface requires  
Unix, because those are the tricks I know. Here's the start of that code:

&nbsp;&nbsp;# ...  
&nbsp;&nbsp;  
&nbsp;&nbsp;require "io/wait"  
&nbsp;&nbsp;  
&nbsp;&nbsp;### game interface (requires Unix) ###  
&nbsp;&nbsp;TERMINAL\_STATE = `stty -g`  
&nbsp;&nbsp;system "stty raw -echo cbreak"  
&nbsp;&nbsp;at\_exit { system "stty #{TERMINAL\_STATE}" }  
&nbsp;&nbsp;clear = `clear`  
&nbsp;&nbsp;  
&nbsp;&nbsp;# a raw mode savvy puts  
&nbsp;&nbsp;def out(\*args) print(\*(args + ["\r\n"])) end  
&nbsp;&nbsp;  
&nbsp;&nbsp;# for easy selection  
&nbsp;&nbsp;words = word\_list.keys  
&nbsp;&nbsp;  
&nbsp;&nbsp;# ...

This setup code memorizes the original state of the user's terminal, modifies  
that state to raw mode so I can read individual characters as they are pressed,  
arranges to have the terminal settings restored at exit, grabs the escape  
sequence we can use to clear the terminal, and builds a puts() like method that  
works with raw mode. This code doesn't really have much to do with Ruby. I'm  
just shelling out to standard Unix utilities here.

We're now ready for the game event loop:

&nbsp;&nbsp;# ...  
&nbsp;&nbsp;  
&nbsp;&nbsp;rounds = 0  
&nbsp;&nbsp;loop do  
&nbsp;&nbsp;&nbsp;&nbsp;# select letters  
&nbsp;&nbsp;&nbsp;&nbsp;letters = current = words[rand(words.size)]  
&nbsp;&nbsp;&nbsp;&nbsp;while word\_list.include? letters  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;letters = letters.split("").sort\_by { rand }.join  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;letters.gsub!(/.(?=.)/, '\0 ')  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;# round data  
&nbsp;&nbsp;&nbsp;&nbsp;advance = false  
&nbsp;&nbsp;&nbsp;&nbsp;matches = Array.new  
&nbsp;&nbsp;&nbsp;&nbsp;current\_match = String.new  
&nbsp;&nbsp;&nbsp;&nbsp;start = Time.now  
&nbsp;&nbsp;&nbsp;&nbsp;message = nil  
&nbsp;&nbsp;&nbsp;&nbsp;last\_update = start - 1  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;# ...

I begin by selecting a word for the round and scrambling that word's letters  
until they are no longer a real word. Then I setup some variables to hold data  
for the round like whether or not the user has found a six letter word and  
should advance as well as any feedback message I am currently showing the user  
and the last time I refreshed the screen.

Now we start the round event loop:

&nbsp;&nbsp;&nbsp;&nbsp;# ...  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;# round event loop  
&nbsp;&nbsp;&nbsp;&nbsp;until Time.now \>= start + 2 \* 60  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# game display  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if last\_update \<= Time.now - 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print clear  
&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out "Your letters: #{letters}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out " Time left: #{120 - (Time.now - start).round} seconds"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out " Your words: #{matches.join(', ')}"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unless message.nil?  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out message  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print current\_match  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$stdout.flush  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;last\_update = Time.now  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# ...

The round event loop runs for two minutes and this first bit is responsible for  
drawing the screen. After clearing the screen, it prints the letters, time  
left, words found, and any feedback message to the screen. Note that I update  
the screen each second, assuming no other activity, so the user will notice the  
clock counting down.

Here's the input processing:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# ...  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# input handler  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if $stdin.ready?  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;char = $stdin.getc  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case char  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ?a..?z, ?A..?Z # read input  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;current\_match \<\< char.chr.downcase  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;message = nil  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;last\_update = start - 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ?\b, 127 # backspace/delete  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;current\_match = current\_match[0..-2]  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;message = nil  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;last\_update = start - 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;when ?\r, ?\n # test entered word  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if word\_list[current].include? current\_match  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;matches \<\< current\_match  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;matches = matches.sort\_by { |word| [word.size, word] }  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if not advance and current\_match.length == 6  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;advance = true  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;message = "You will advance to the next round!"  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;message = nil  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;message = "Unknown word."  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;current\_match = String.new  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;last\_update = start - 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;# ...

This just checks to see if there is data waiting on STDIN. We don't want to  
read from it without checking as that could block the application waiting for  
input. The ready?() method used here is added by the io/wait library and will  
return true if there is data waiting. The rest of the code just handles the  
input we get. Letters are recorded, we support backspace, and a carriage-return  
tells us to try the current word, set some feedback, and refresh the display.

When the round is done, we finish out the game loop:

&nbsp;&nbsp;&nbsp;&nbsp;# ...  
&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;# round results  
&nbsp;&nbsp;&nbsp;&nbsp;print clear  
&nbsp;&nbsp;&nbsp;&nbsp;missed = word\_list[current] - matches  
&nbsp;&nbsp;&nbsp;&nbsp;unless missed.empty?  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out "Other words using \"#{letters}:\""  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out missed.sort\_by { |word| [word.size, word] }.join(", ")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;if advance  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rounds += 1  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out "You made #{matches.size} word#{'s' if matches.size != 1}, ",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"including at least one six letter word. Nice work."  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out "Press any key to begin the next round."  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;$stdin.getc  
&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;out "You made #{matches.size} word#{'s' if matches.size != 1}, ",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"but failed to find a six letter word."  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break # end game  
&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;end  
&nbsp;&nbsp;  
&nbsp;&nbsp;# game results  
&nbsp;&nbsp;out "You completed #{rounds} round#{'s' if rounds != 1}. ",  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"Thanks for playing."

The above code just prints missed words and results. If you found a six letter  
word, the code will loop to a new round. Otherwise it will break out of the  
program.

A big thank you to Ben (and Guido!) for convincing me to try the quiz and to all  
those that took the time to play with it.

Tomorrow we'll try a problem that has been making the rounds, literally...

---

<div class="post-metadata">

### Author: ![Martin\_DeMello](https://avatars.discourse-cdn.com/v4/letter/m/f17d59/32.png) [@Martin\_DeMello](https://rubytalk.org/u/Martin_DeMello)
#### Post date: [11 January 2007 13:54 UTC](https://rubytalk.org/t/summary-word-blender-108/34381/2 "2007-01-11T13:54:13Z")

</div>

I used a different signature method - I mapped each unique letter in  
the target word to a prime, and then used the product of the primes of  
all the letters in a word as its signature. That way, a is contained  
in b if signature(b) % signature(a) == 0, and you can generate the  
signatures via each\_byte, array lookup and integer multiplication (no  
need for split, sort or string deletion).

martin

> **···**
>
> On 1/11/07, Ruby Quiz \<james@grayproductions.net\> wrote:
> 
> > The main trick used in this recursive grouping of words is the use of  
> > "signatures." A word's signature is just the sorted order of the letters in the  
> > word: aejms for james, for example. Comparing signatures makes it trivial to  
> > find words that use the same letters, since their signatures will be the same.
> > 
> > Using the signatures, the choices() method just removes one character at a time  
> > recursing through the word list. This allows me to find all of the smaller  
> > words that can be formed using the same letters.

---

<div class="post-metadata">

### Author: ![Ben\_Bleything1](https://avatars.discourse-cdn.com/v4/letter/b/3e96dc/32.png) [@Ben\_Bleything1](https://rubytalk.org/u/Ben_Bleything1)
#### Post date: [17 January 2007 23:02 UTC](https://rubytalk.org/t/summary-word-blender-108/34381/3 "2007-01-17T23:02:43Z")

</div>

> I'm almost embarrassed to admit that I originally turned this quiz down. I  
> thought it would be too similar to the Scrabble Stems problem we did long, long  
> ago. Ben politely explained that he felt it was different enough though, and  
> then sent some guy named Guido after me in a parking lot one night. That  
> convinced me to actually work the problem, and I had a change of heart. I think  
> we can tell from the popularity of the problem that Ben is smarter than I am, so  
> I'm glad I did.

Coupla things:

1) Gianni, not Guido. I can understand the confusion, though, as it was  
&nbsp;&nbsp;&nbsp;dark and probably hurt a bunch.

2) I didn't manage to write a solution to the quiz, despite the fact that  
&nbsp;&nbsp;&nbsp;I had at least a month's time more than most everyone else, so none of  
&nbsp;&nbsp;&nbsp;this smarter business. 🙂

> I wanted to play though and built a full game interface. My interface requires  
> Unix, because those are the tricks I know. Here's the start of that code:

\<snip\>

> This setup code memorizes the original state of the user's terminal, modifies  
> that state to raw mode so I can read individual characters as they are pressed,  
> arranges to have the terminal settings restored at exit, grabs the escape  
> sequence we can use to clear the terminal, and builds a puts() like method that  
> works with raw mode. This code doesn't really have much to do with Ruby. I'm  
> just shelling out to standard Unix utilities here.

\<snip awesomeness\>

Further proof of #2 above.

Ben

> **···**
>
> On Thu, Jan 11, 2007, Ruby Quiz wrote:

---

<div class="post-metadata">

### Author: ![James\_Edward\_Gray\_II](https://avatars.discourse-cdn.com/v4/letter/j/ea5d25/32.png) [@James\_Edward\_Gray\_II](https://rubytalk.org/u/James_Edward_Gray_II)
#### Post date: [11 January 2007 13:59 UTC](https://rubytalk.org/t/summary-word-blender-108/34381/4 "2007-01-11T13:59:00Z")

</div>

Very interesting. I've never seen that before. I like it.

My version of "signatures" comes out of Programming Peals, if my memory is right. Just FYI.

James Edward Gray II

> **···**
>
> On Jan 11, 2007, at 7:54 AM, Martin DeMello wrote:
> 
> > On 1/11/07, Ruby Quiz \<james@grayproductions.net\> wrote:
> > 
> > > The main trick used in this recursive grouping of words is the use of  
> > > "signatures." A word's signature is just the sorted order of the letters in the  
> > > word: aejms for james, for example. Comparing signatures makes it trivial to  
> > > find words that use the same letters, since their signatures will be the same.
> > > 
> > > Using the signatures, the choices() method just removes one character at a time  
> > > recursing through the word list. This allows me to find all of the smaller  
> > > words that can be formed using the same letters.
> > 
> > I used a different signature method - I mapped each unique letter in  
> > the target word to a prime, and then used the product of the primes of  
> > all the letters in a word as its signature. That way, a is contained  
> > in b if signature(b) % signature(a) == 0, and you can generate the  
> > signatures via each\_byte, array lookup and integer multiplication (no  
> > need for split, sort or string deletion).

---

<div class="post-metadata">

### Author: ![Ben\_Bleything1](https://avatars.discourse-cdn.com/v4/letter/b/3e96dc/32.png) [@Ben\_Bleything1](https://rubytalk.org/u/Ben_Bleything1)
#### Post date: [11 January 2007 16:26 UTC](https://rubytalk.org/t/summary-word-blender-108/34381/5 "2007-01-11T16:26:42Z")

</div>

Holy crap. That is incredibly cool. Thanks for sharing this technique!

Ben

> **···**
>
> On Thu, Jan 11, 2007, Martin DeMello wrote:
> 
> > I used a different signature method - I mapped each unique letter in  
> > the target word to a prime, and then used the product of the primes of  
> > all the letters in a word as its signature. That way, a is contained  
> > in b if signature(b) % signature(a) == 0, and you can generate the  
> > signatures via each\_byte, array lookup and integer multiplication (no  
> > need for split, sort or string deletion).

---

<div class="post-metadata">

### Author: ![Martin\_DeMello](https://avatars.discourse-cdn.com/v4/letter/m/f17d59/32.png) [@Martin\_DeMello](https://rubytalk.org/u/Martin_DeMello)
#### Post date: [11 January 2007 16:26 UTC](https://rubytalk.org/t/summary-word-blender-108/34381/6 "2007-01-11T16:26:57Z")

</div>

The numbers overflow 32 bits in the general case, I think, but for  
this restricted problem it works very nicely.

martin

> **···**
>
> On 1/11/07, James Edward Gray II \<james@grayproductions.net\> wrote:
> 
> > On Jan 11, 2007, at 7:54 AM, Martin DeMello wrote:  
> > \> I used a different signature method - I mapped each unique letter in  
> > \> the target word to a prime, and then used the product of the primes of  
> > \> all the letters in a word as its signature. That way, a is contained  
> > \> in b if signature(b) % signature(a) == 0, and you can generate the  
> > \> signatures via each\_byte, array lookup and integer multiplication (no  
> > \> need for split, sort or string deletion).
> > 
> > Very interesting. I've never seen that before. I like it.

---

<div class="post-metadata">

### Author: ![Fedor\_Labounko](https://avatars.discourse-cdn.com/v4/letter/f/d78d45/32.png) [@Fedor\_Labounko](https://rubytalk.org/u/Fedor_Labounko)
#### Post date: [11 January 2007 16:59 UTC](https://rubytalk.org/t/summary-word-blender-108/34381/7 "2007-01-11T16:59:57Z")

</div>

I believe with Ruby they'll get converted to Bignum automatically with no  
overflow, though I bet the running time suffers.

> **···**
>
> On 1/11/07, Martin DeMello \<martindemello@gmail.com\> wrote:
> 
> > On 1/11/07, James Edward Gray II \<james@grayproductions.net\> wrote:  
> > \> On Jan 11, 2007, at 7:54 AM, Martin DeMello wrote:  
> > \> \> I used a different signature method - I mapped each unique letter in  
> > \> \> the target word to a prime, and then used the product of the primes of  
> > \> \> all the letters in a word as its signature. That way, a is contained  
> > \> \> in b if signature(b) % signature(a) == 0, and you can generate the  
> > \> \> signatures via each\_byte, array lookup and integer multiplication (no  
> > \> \> need for split, sort or string deletion).  
> > \>  
> > \> Very interesting. I've never seen that before. I like it.
> > 
> > The numbers overflow 32 bits in the general case, I think, but for  
> > this restricted problem it works very nicely.
> > 
> > martin

---

<div class="post-metadata">

### Author: ![Gavin\_Kistner2](https://avatars.discourse-cdn.com/v4/letter/g/b9e5f3/32.png) [@Gavin\_Kistner2](https://rubytalk.org/u/Gavin_Kistner2)
#### Post date: [11 January 2007 17:30 UTC](https://rubytalk.org/t/summary-word-blender-108/34381/8 "2007-01-11T17:30:05Z")

</div>

Martin DeMello wrote:

> \> \> I used a different signature method - I mapped each unique letter in  
> \> \> the target word to a prime, and then used the product of the primes of  
> \> \> all the letters in a word as its signature. That way, a is contained  
> \> \> in b if signature(b) % signature(a) == 0, and you can generate the  
> \> \> signatures via each\_byte, array lookup and integer multiplication (no  
> \> \> need for split, sort or string deletion).  
> \>  
> \> Very interesting. I've never seen that before. I like it.
> 
> The numbers overflow 32 bits in the general case, I think, but for  
> this restricted problem it works very nicely.

Specifically (I was wondering) you can use 9 primes and still be under  
32 bits, 15 primes and still be under 64 bits.

module Enumerable  
&nbsp;&nbsp;def product; inject(){ |n,p| p\*n }; end  
end

first9 = primes[0..8]  
puts "First 9 primes:\n%s\nproduct: %d (%d bits)" %  
&nbsp;&nbsp;[  
&nbsp;&nbsp;&nbsp;&nbsp;first9.inspect,  
&nbsp;&nbsp;&nbsp;&nbsp;first9.product,  
&nbsp;&nbsp;&nbsp;&nbsp;first9.product.to\_s(2).length  
&nbsp;&nbsp;]

first15 = primes[0..14]  
puts "\nFirst 15 primes:\n%s\nproduct: %d (%d bits)" %  
&nbsp;&nbsp;[  
&nbsp;&nbsp;&nbsp;&nbsp;first15.inspect,  
&nbsp;&nbsp;&nbsp;&nbsp;first15.product,  
&nbsp;&nbsp;&nbsp;&nbsp;first15.product.to\_s(2).length  
&nbsp;&nbsp;]

#=\> First 9 primes:  
#=\> [2, 3, 5, 7, 11, 13, 17, 19, 23]  
#=\> product: 223092870 (28 bits)

#=\> First 15 primes:  
#=\> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]  
#=\> product: 614889782588491410 (60 bits)

25 primes puts you at 121 bits; 26 primes at 128 bits on the nose.

> **···**
>
> > On 1/11/07, James Edward Gray II \<james@grayproductions.net\> wrote:  
> > \> On Jan 11, 2007, at 7:54 AM, Martin DeMello wrote:
