[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

[QUIZ] Word Blender (#108

James Gray

1/5/2007 1:06:00 PM

The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rub...

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

by Ben Bleything

This is a riff on the Jumble puzzle found in many (US) newspapers. More
specifically, it's based on the game TextTwist[1], made by GameHouse[2] and
published in various places around the web.

The mechanic of TextTwist is simple. The player is given six letters and is
tasked with unscrambling those letters into as many words as possible. If the
player can use all six letters in a word, they proceed to the next round.

Your task is to build the back-end engine to run a TextTwist clone. Effectively,
this means that you must generate a list of three- to six-letter words that can
all be constructed from the same six letters. This list must contain at least
one six-letter word.

Bonus points for building a completely functional game!

[1]: http://games.yahoo.com/games/text... (just one example, java)
[2]: http://www.game...

20 Answers

James Gray

1/5/2007 2:01:00 PM

0

On Jan 5, 2007, at 7:53 AM, Jason Mayer wrote:

> Is the goal to get the most points or to get to the highest round?
> Do you
> get points based on the number of letters used (as in Upwords) or
> do you get
> points based on the obscurity of the letter to be used (as in
> scrabble)?

My solution doesn't track "points" per say, just the rounds really.
So for my code, it's getting to the highest round.

Feel free to add some scoring though. I would probably score based
on words found, with bigger words earning more points.

James Edward Gray II

matthew.moss.coder

1/5/2007 3:54:00 PM

0

I'd be interested in trying this, but I've avoided dictionary-type
quizzes in the past for lack of a good dictionary file. Does anyone
have links to decent word/dictionary files? Or perhaps does Mac OS X
come with one?

Oin Maple

1/5/2007 4:06:00 PM

0

On Friday 05 January 2007 17:53, Matthew Moss wrote:
> I'd be interested in trying this, but I've avoided dictionary-type
> quizzes in the past for lack of a good dictionary file. Does anyone
> have links to decent word/dictionary files? Or perhaps does Mac OS X
> come with one?

I'm using the ones here: http://wordlist.source...
I don't know how good they are, but they're ok for this particular quiz IMHO

James Gray

1/5/2007 4:10:00 PM

0

On Jan 5, 2007, at 9:53 AM, Matthew Moss wrote:

> Or perhaps does Mac OS X come with one?

Sure: /usr/share/dict/words

James Edward Gray II

Oin Maple

1/6/2007 1:44:00 PM

0

did anyone find a unix-friendly text-twist?
the yahoo one doesn't like me: "Note: TextTwist is not compatible with Unix or
Macintosh computers."
and every other site with texttwist seems to use the yahoo one.

Oin Maple

1/7/2007 1:05:00 PM

0

This is my first ruby program that actually does all it's supposed to do. It
doesn't have a lot of style, but hopefully my next programs will look better.
The wordlist is from wordlist.sourceforge.net and the first part of the
program reads the word.lst file and puts all the words in an array.

Hope it does what it's supposed to do, as i couldn't see the texttwist on
yahoo.
I'm also attaching the Unnoficial Jargon File Wordlist word.lst file.

-Ionut Artarisi


James Gray

1/7/2007 4:54:00 PM

0

Here's the solution I wrote while considering this quiz (it requires
Unix):

#!/usr/bin/env ruby -w

require "io/wait"

# game date cache
CACHE_FILE = ".game_words"

if File.exist? CACHE_FILE # load from cache
word_list = File.open(CACHE_FILE) { |file| Marshal.load(file) }
else # build word list
# prepare data structure
words_by_signature = Hash.new { |words, sig| words[sig] = Array.new }

# read dictionary
File.foreach(ARGV.shift || "/usr/share/dict/words") do |word|
word.downcase!
word.delete!("^a-z")

next unless word.length.between? 3, 6

(words_by_signature[word.split("").sort.join] << word).uniq!
end

# prepare recursive signature search
def choices(sig, seen = Hash.new { |all, cur| all[cur] = true;
false }, &blk)
sig.length.times do |i|
shorter = sig[0...i] + sig[(i+1)...sig.length]
unless seen[shorter]
blk[shorter]
choices(shorter, seen, &blk) unless shorter.length == 3
end
end
end

# prepare game data structure
word_list = Hash.new

# build game choices
words_by_signature.keys.grep(/\A.{6}\Z/) do |possible|
word_list[possible] = words_by_signature[possible]

choices(possible) do |shorter_signature|
if words_by_signature.include? shorter_signature
word_list[possible].push(*words_by_signature
[shorter_signature])
end
end
end

# cache for faster loads
File.open(CACHE_FILE, "w") { |file| Marshal.dump(word_list, file) }
end

### game interface (requires Unix) ###
TERMINAL_STATE = `stty -g`
system "stty raw -echo cbreak"
at_exit { system "stty #{TERMINAL_STATE}" }
clear = `clear`

# a raw mode savvy puts
def out(*args) print(*(args + ["\r\n"])) end

# for easy selection
words = word_list.keys

rounds = 0
loop do
# select letters
letters = current = words[rand(words.size)]
while word_list.include? letters
letters = letters.split("").sort_by { rand }.join
end
letters.gsub!(/.(?=.)/, '\0 ')

# round data
advance = false
matches = Array.new
current_match = String.new
start = Time.now
message = nil
last_update = start - 1

# round event loop
until Time.now >= start + 2 * 60
# game display
if last_update <= Time.now - 1
print clear

out "Your letters: #{letters}"
out " Time left: #{120 - (Time.now - start).round} seconds"
out " Your words: #{matches.join(', ')}"
out
unless message.nil?
out message
out
end
print current_match
$stdout.flush

last_update = Time.now
end

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

# round results
print clear
missed = word_list[current] - matches
unless missed.empty?
out "Other words using \"#{letters}:\""
out missed.sort_by { |word| [word.size, word] }.join(", ")
out
end
if advance
rounds += 1

out "You made #{matches.size} word#{'s' if matches.size != 1}, ",
"including at least one six letter word. Nice work."
out "Press any key to begin the next round."

$stdin.getc
else
out "You made #{matches.size} word#{'s' if matches.size != 1}, ",
"but failed to find a six letter word."

break # end game
end
end

# game results
out "You completed #{rounds} round#{'s' if rounds != 1}. Thanks for
playing."

__END__

James Edward Gray II

Ben Bleything

1/7/2007 7:32:00 PM

0

On Sat, Jan 06, 2007, Oin Maple wrote:
> did anyone find a unix-friendly text-twist?
> the yahoo one doesn't like me: "Note: TextTwist is not compatible with Unix or
> Macintosh computers."
> and every other site with texttwist seems to use the yahoo one.

It's just a java game. I'm on a mac and it works fine. If you have a
current JRE and an appropriate browser plugin, it should work.

Ben

Ben Ford

1/7/2007 9:56:00 PM

0

Here is my solution. Takes the filename of the dictionary to use as the
first argument and optionally a word to solve for as the second
argument. If no second argument is provided, you play the game.

--

class String
def lettercount
split(//).uniq.map{|c| [c, count(c)]}
end
def fisher_yates_shuffle
a = self.dup
(length-1).downto(0){|i|
j = rand(i+1)
a[i], a[j] = a[j], a[i] if i != j
}
a
end
end

class Array
def random_element
self[rand(length)]
end
end

class Dictionary
def initialize(filename)
@words = []
IO.foreach(filename) do |line|
word = line.chomp
@words << word.downcase if word.length.between?(3, 6)
end
end
def blend(word)
@words.select{|x|
x.count(word.downcase) == x.length &&
x.lettercount.all?{|c, n|
n <= word.downcase.lettercount.assoc(c).last }
}
end
def randomword
@words.select{|x| x.length == 6}.random_element
end
end

class WordBlender
def initialize(dictionary)
@dictionary = dictionary
end
def blend_to_s(word)
word_blend = @dictionary.blend(word)
puts "WordBlender: '#{word}' has #{word_blend.length} answers."
puts
max = -1
word_blend.sort_by{|x| [x.length, x]}.each do |x|
if x.length > max
max = x.length
puts "Words of length #{max}:"
end
puts " #{x}"
end
end
def play
puts "Welcome to WordBlender! (enter a blank line to quit)"
puts
round = 0
points = 0
continue = true
while continue do
points = points + 10 * round
round = round + 1
word = @dictionary.randomword
word_blend = @dictionary.blend(word)
word_shuffled = word.fisher_yates_shuffle
puts "Round: #{round} - Blend: '#{word_shuffled}' - Total Score:
#{points}"
current_word = ""
current_words = []
current_continue = true
while continue && current_continue do
current_word = STDIN.gets.chomp.downcase
if current_word == ""
puts
puts "Final Word: '#{word}' - Final Score: #{points}"
continue = false
elsif current_words.include?(current_word)
puts "'#{current_word}' already used."
elsif word_blend.include?(current_word)
current_words << current_word
points = points + current_word.length * current_word.length
current_continue = (current_word.length < word.length)
elsif current_word.count(word) == current_word.length
puts "'#{current_word}' not in dictionary."
else
puts "'#{current_word}' not found in '#{word_shuffled}'."
end
end
end
end
end

if ARGV.size == 0
puts "Usage: wordblender.rb <filename> - play WordBlender with the
specified dictionary"
puts "Usage: wordblender.rb <filename> <word> - show all blends for
the word using the dictionary"
elsif ARGV.size == 1
WordBlender.new(Dictionary.new(ARGV[0])).play
elsif ARGV.size >= 2
WordBlender.new(Dictionary.new(ARGV[0])).blend_to_s(ARGV[1])
end


On Jan 5, 8:05 am, Ruby Quiz <j...@grayproductions.net> wrote:
> The three rules of Ruby Quiz:
>
> 1. Please do not post any solutions or spoiler discussion for this quiz until
> 48 hours have passed from the time on this message.
>
> 2. Support Ruby Quiz by submitting ideas as often as you can:
>
> http://www.rub...
>
> 3. Enjoy!
>
> Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
> on Ruby Talk follow the discussion. Please reply to the original quiz message,
> if you can.
>
> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>
> by Ben Bleything
>
> This is a riff on the Jumble puzzle found in many (US) newspapers. More
> specifically, it's based on the game TextTwist[1], made by GameHouse[2] and
> published in various places around the web.
>
> The mechanic of TextTwist is simple. The player is given six letters and is
> tasked with unscrambling those letters into as many words as possible. If the
> player can use all six letters in a word, they proceed to the next round.
>
> Your task is to build the back-end engine to run a TextTwist clone. Effectively,
> this means that you must generate a list of three- to six-letter words that can
> all be constructed from the same six letters. This list must contain at least
> one six-letter word.
>
> Bonus points for building a completely functional game!
>
> [1]:http://games.yahoo.com/games/text...(just one example, java)
> [2]:http://www.game...

Eric I.

1/8/2007 12:34:00 AM

0

This solution simply chooses a six-letter word (or allows the user to
choose one) and then displays a list of words that can be composed with
a subset of the letters (size >= 3). Here is output from a sample run:

ape
lap
pal
pea
sap
sea
see
else
leap
pale
peas
peel
sale
seal
slap
sleep
asleep
please

Eric
------------
Interested in Ruby training with a well-reviewed instructor and
training materials? www.LearnRuby.com
============

# Given an array of letters, a whole/partial word built up so far, and
# a hash, adds to the hash all permutations of subsets built from the
# partial word and the array of letters. If a block is given it acts
# as a filter since the words must produce a true result when submitted
# to the block in order to be added to the hash.
def permute(letters, word, possible_words, &filter_block)
possible_words[word] = true if filter_block.nil? ||
filter_block.call(word)
return if letters.empty?

letters.each_with_index do |letter, i|
(new_letters = letters.dup).delete_at(i)
permute(new_letters, word + letter, possible_words, &filter_block)
end
end

# Verify that a filename was provided as the first argument and that
# it is a readable file
if ARGV[0].nil?
$stderr.puts("Usage: #{$0} dictionary-file [word]")
exit 1
elsif ! File.file?(ARGV[0]) || ! File.readable?(ARGV[0])
$stderr.puts("Error: \"#{ARGV[0]}\" is not a readable file.")
exit 2
end

# Build list of all six-letter words from dictionary file
words6 = Array.new
open(ARGV[0], "r") do |f|
f.each_line { |w| words6 << w if w.chomp! =~ /^[a-z]{6}$/ }
end

# Determine whether a random six-letter word is chosen or the user
# specifies one.
if ARGV[1]
# user attempted to specify a word; check its validity
if words6.include?(ARGV[1])
word = ARGV[1]
else
$stderr.puts("Error: \"#{ARGV[1]}\" is not a known six-letter
word.")
exit 3
end
else
word = words6[rand(words6.size)] # choose a random word
end

# Generate a hash of all three- to six-letter permutations using the
# letters of the chosen six-letter word. Note: most will not be valid
# words.
possible_words = Hash.new
permute(word.split(""), "", possible_words) { |w| w.length >= 3 }

# Generate a list of all valid words that are also permutations of
# subsets of the chosen six-letter word. This is done by
# re-reading the word file and testing each word against the
# possible permutations.
actual_words = Array.new
open(ARGV[0], "r") do |f|
f.each_line { |w| actual_words << w if possible_words[w.chomp!] }
end

# Display the resulting actual words sorted first by length and then
# alphabetically.
puts actual_words.sort_by { |w| [w.length, w] }