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.rubyquiz.com/ 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/texttwist.html (just one example, java) [2]: http://www.gamehouse.com/
on 2007-01-06 19:31
on 2007-01-07 13:48
It just so happened that I was learning ruby last summer and wrote a program to cheat for me at TextTwist. Needless to say the game got boring really fast, but it was neat writing the program. I've modified it a bit to instead play the game, but I'm a fairly new user and would appreciate any feedback, both ruby related and general programming. To run this you'll need a dictionary file of words, each on a new line, called wordlist.txt and you need to require 'dict' and run Dict.reduce(3,6) before you run the program. This will create a reduced dictionary file of only words of lengths between 3 and 6. I didn't make this part of my program's general execution as I figured doing this once was enough and I could do it manually. It's got a simple text interface that looks sort of like the TextTwist gui layout on Yahoo. Sort of. The dictionary I use, which I did not attach because its size (3 megs, I believe it was the full Scrabble dictionary) makes for a much harder game than the Yahoo TextTwist as some of the words are really obscure. Also, I have the problem that when running this on Windows it doesn't allow me to manipulate files with Ruby, giving me a Permission Denied error on File.open. Any idea why this might be? This is if I try to run Dict.reduce(3,6) for example.
on 2007-01-07 14:05
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. -Ionuţ Arţărişi
on 2007-01-07 15:41
Here it is. Scoring works like this: 10 points per (tile used)^2. 100 bonus points for using all 6 tiles. 5 wrong answers in a row ends the game. I made a dictionary file of 3-6 letter words using crop.rb. Any feedback is appreciated, both from a stylistic and general programming perspective. If there's a way to rubify (not a word!) anything, also let me know.
on 2007-01-07 15:58
On 1/7/07, Fedor Labounko <fedor.labounko@gmail.com> wrote: > only words of lengths between 3 and 6. I didn't make this part of my > allow me to manipulate files with Ruby, giving me a Permission Denied error > on File.open. Any idea why this might be? This is if I try to run > Dict.reduce(3,6) for example. E:\ruby\programs\rubyquiz\quiz108\other submissions>ruby texttwistGame.rb ./dict.rb:20:in `initialize': No such file or directory - reducedwordlist3.txt ( Errno::ENOENT) from ./dict.rb:20:in `open' from ./dict.rb:20:in `get_dict' from texttwistGame.rb:37 fixed that, but then: E:\ruby\programs\rubyquiz\quiz108\other submissions>ruby texttwistGame.rb texttwistGame.rb:17:in `pick_word': undefined method `delete_if' for nil:NilClas s (NoMethodError) from texttwistGame.rb:38 I'm too tired to go bug hunting. Check out my submission crop.rb to see how I did the windows file writing. Now that I've submitted my program I'm going back to sleep. :)
on 2007-01-07 17:54
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
on 2007-01-07 19:04
> E:\ruby\programs\rubyquiz\quiz108\other submissions>ruby texttwistGame.rb > texttwistGame.rb:17:in `pick_word': undefined method `delete_if' for > nil:NilClas > s (NoMethodError) > from texttwistGame.rb:38 > > I'm too tired to go bug hunting. Check out my submission crop.rb to see > how > I did the windows file writing. Now that I've submitted my program I'm > going back to sleep. :) Thanks Jason. It seems that the problem is that as long as a file by that name exists, Windows doesn't let me overwrite it. I'm using the FILE::CREAT flag but I don't think that should be causing this. It seems to be saying I don't have the permission to modify the file with ruby, though I haven't done much research into why that is so and how to fix it.
on 2007-01-07 21:09
Here is my solution.
-Chunyun
#== Synopsis
#This is the solution to Ruby Quiz #108 described on
http://www.rubyquiz.com/quiz108.html.
#
#== Usage
# text_twist.rb dictionary_file
#
#== Author
# Chunyun Zhao(chunyun.zhao@gmail.com)
#
class Dictionary
MIN_LEN, MAX_LEN = 3, 6
attr_reader :max_letters
def initialize(dict_file, min=MIN_LEN, max=MAX_LEN)
@min_len = min
@max_len = max
@words = Hash.new {|hash,key|hash[key]=[]}
File.foreach(dict_file) {|word|add_word(word.strip)}
@max_letters = @words.keys.select {|key| key.size==@max_len}
end
def word_list(letters)
words=[]
permutate(letters).select {|letters|
letters.size.between? @min_len, @max_len
}.uniq.each {|key|
words += @words[key]
}
words.sort_by {|word| word.size}
end
private
def add_word(word)
if (@min_len..@max_len)===word.size && word=~/^[a-z]+$/i
word.downcase!
@words[word.split(//).sort] << word
end
end
def permutate(letters)
_letters = letters.dup
result = []
while letter = _letters.shift
permutate(_letters).each do |perm|
result << [letter] + perm
end
result << [letter]
end
result
end
end
Task = Struct.new(:letters, :words)
class GameUi
def initialize(dict)
@dictionary = dict
@history_tasks = []
@rounds = 1
@score = 0
end
def next_task
letters =
@dictionary.max_letters[rand(@dictionary.max_letters.size)]
retry if @history_tasks.include?(letters)
task = Task.new(letters, @dictionary.word_list(letters))
@history_tasks << task
task
end
def run_task
@task = next_task
@found = []
@cleared = false
puts "\nRound #{@rounds}. Letters: #{@task.letters*', '}. Hint:
number
of matching words: #{@task.words.size}"
while !(word=ask("Enter your word:")).empty?
if @found.include? word
puts "Word already found!"
elsif @task.words.include? word
@found << word
@score += word.size
puts "Good job! You scored #{word.size} points!"
if word.size == @task.letters.size
@cleared = true
puts "\nBingo! Round #@rounds cleared. You found
#{@found.size}
word#{'s' if @found.size > 1}. "
break
end
else
puts "Wrong word!"
end
end
puts "Missed words: #{(@task.words-@found)*', '}."
@cleared
end
def run
while run_task
answer = ask("\nProceed to next round?")
break if answer !~ /^y/i
@rounds += 1
end
puts "\nYou've cleared #{cleared=@cleared?@rounds:@rounds-1}
round#{'s'
if cleared > 1}, and your total score is #{@score}."
end
def ask question
print question, " (Hit enter to exit)=> "
gets.strip
end
end
if __FILE__ == $0
if ARGV.size != 1
puts "Usage: #{File.basename(__FILE__)} dictionary_file"
exit
end
GameUi.new(Dictionary.new(ARGV.shift)).run
end
__END__
on 2007-01-07 21:17
There is a bug in my previous submission, here it is again:
#== Synopsis
#This is the solution to Ruby Quiz #108 described on
http://www.rubyquiz.com/quiz108.html.
#
#== Usage
# text_twist.rb dictionary_file
#
#== Author
# Chunyun Zhao(chunyun.zhao@gmail.com)
#
class Dictionary
MIN_LEN, MAX_LEN = 3, 6
attr_reader :max_letters
def initialize(dict_file, min=MIN_LEN, max=MAX_LEN)
@min_len = min
@max_len = max
@words = Hash.new {|hash,key|hash[key]=[]}
File.foreach(dict_file) {|word|add_word(word.strip)}
@max_letters = @words.keys.select {|key| key.size==@max_len}
end
def word_list(letters)
words=[]
permutate(letters).select {|letters|
letters.size.between? @min_len, @max_len
}.uniq.each {|key|
words += @words[key]
}
words.sort_by {|word| word.size}
end
private
def add_word(word)
if (@min_len..@max_len)===word.size && word=~/^[a-z]+$/i
word.downcase!
@words[word.split(//).sort] << word
end
end
def permutate(letters)
_letters = letters.dup
result = []
while letter = _letters.shift
permutate(_letters).each do |perm|
result << [letter] + perm
end
result << [letter]
end
result
end
end
Task = Struct.new(:letters, :words)
class GameUi
def initialize(dict)
@dictionary = dict
@history_tasks = []
@rounds = 1
@score = 0
end
def run
while run_task
answer = ask("\nProceed to next round?")
break if answer !~ /^y/i
@rounds += 1
end
puts "\nYou've cleared #{cleared=@cleared?@rounds:@rounds-1}
round#{'s'
if cleared > 1}, and your total score is #{@score}."
end
private
def next_task
letters =
@dictionary.max_letters[rand(@dictionary.max_letters.size)]
retry if @history_tasks.include?(letters)
task = Task.new(letters, @dictionary.word_list(letters))
@history_tasks << task
task
end
def run_task
@task = next_task
@found = []
@cleared = false
puts "\nRound #{@rounds}. Letters: #{@task.letters*', '}. Hint:
number
of matching words: #{@task.words.size}"
while !(word=ask("Enter your word:")).empty?
if @found.include? word
puts "Word already found!"
elsif @task.words.include? word
@found << word
@score += word.size
puts "Good job! You scored #{word.size} points!"
if word.size == @task.letters.size
@cleared = true
puts "\nBingo! Round #@rounds cleared. You found
#{@found.size}
word#{'s' if @found.size > 1}. "
break
end
else
puts "Wrong word!"
end
end
puts "Missed words: #{(@task.words-@found)*', '}."
@cleared
end
def ask question
print question, " (Hit enter to exit)=> "
gets.strip.downcase
end
end
if __FILE__ == $0
if ARGV.size != 1
puts "Usage: #{File.basename(__FILE__)} dictionary_file"
exit
end
GameUi.new(Dictionary.new(ARGV.shift)).run
end
on 2007-01-07 23:01
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 2007-01-08 00:12
I made two this time. One plays a game and one just picks the word.
Here's the one that just picks a word. It runs very quickly, about .155
or .2 seconds.
#! /usr/bin/ruby -w
class Array
def rand_elem
self[rand(size)]
end
end
# Open and read the dictionary.
dict = IO.read("/usr/share/dict/words")
# Pick a random word with 6 letters.
baseWord = dict.scan(/^[a-z]{6}$/).rand_elem
# Find words that use the same letters
selectedWords = dict.scan(/^[#{baseWord}]{3,6}$/)
# Display the words.
puts baseWord + ":\n\t" + selectedWords.join("\n\t")
And here's the one that plays a game. Basically, it gives you the
letters and you enter the 5 best words you can think of. The score is
the length of the word ** 3 added to a running total, but subtracted if
the word isn't valid (already said or not in the list).
#! /usr/bin/ruby -w
class Array
def rand_elem
self[rand(size)]
end
def english_join
self[0...-1].join(', ') + ', and ' + self[-1]
end
end
class String
def letters
unless $DEBUG
split(//).uniq.sort_by{rand}
else
split(//)
end
end
end
class Game
@@dict = nil
def initialize
# Open and read the dictionary.
@@dict ||= IO.read("/usr/share/dict/words")
@points = 0
@round = 1
end
def play
# Pick a random word with 6 letters.
baseWord = @@dict.scan(/^[a-z]{6}$/).rand_elem
# Find words that use the same letters
selectedWords = @@dict.scan(/^[#{baseWord}]{3,6}$/)
# Initialize word list & continue var.
guessed = []
continue = false
# Display banner
puts "",
"Round #{@round}:",
"Enter the 5 longest words you can make from the letters
#{baseWord.letters.english_join}.",
"Invalid and repeated words count towards the 5 words but subtract
points.",
""
# Gather all the points, calculate the score, and see if the player
should go to the next round.
5.times do
print "#{@points}\t"
word = gets.chomp.downcase
if !guessed.include?(word) && selectedWords.include?(word)
@points += word.length ** 3
guessed << word
continue = true if word.length == 6
else
@points -= word.length ** 3
end
end
# Go on to the next round or lose.
if continue
@round += 1
play
else
puts "Sorry, you didn't get a 6 letter word. You got #{@points}
points, however."
end
end
end
Game.new.play
on 2007-01-08 00:38
On 1/7/07, Daniel Finnie <danfinnie@optonline.net> wrote: > # Find words that use the same letters > selectedWords = dict.scan(/^[#{baseWord}]{3,6}$/) I was really impressed when I first saw this. It doesn't quite work if you want to exclude reusing the same letter more than once ("hhh".scan(/^[hello]{3,6}$/) => ["hhh"]) but it comes so close to something I've only ever thought about implementing as a recursive method. Unfortunately I don't know much about this but now I wonder if it's possible to find all partial permutations of a word with a regexp.
on 2007-01-08 01:33
Here is a version of the regex that works around that: regex = /^([a-z])(?!\1)([a-z])(?!\1|\2)([a-z])(?!\1|\2|\3)([a-z])(?!\1|\2|\3|\4)([a-z])(?!\1|\2|\3|\4|\5)([a-z])$/ Some examples: >> regex.match 'abcdef' => #<MatchData:0xb7b46a18> >> regex.match 'abcdefg' => nil >> regex.match 'abcddf' => nil >> regex.match 'abbdtf' => nil >> regex.match 'Abbdtf' => nil >> regex.match 'awesom' => #<MatchData:0xb7b3a5b0> >> regex.match 'hollow' => nil
on 2007-01-08 01:35
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] }
on 2007-01-08 01:41
Oops, that doesn't match {3,6} just {6}.
>> regex =
/^([a-z])(?!\1)([a-z])(?!\1|\2)([a-z])(?:(?!\1|\2|\3)([a-z]))?(?:(?!\1|\2|\3|\4)([a-z]))?(?:(?!\1|\2|\3|\4|\5)([a-z]))?$/
=> [a-z]1[a-z]12[a-z]:123[a-z]:1234[a-z]:12345[a-z]
>> regex.match 'hhh'
=> nil
>> regex.match 'hal'
=> #<MatchData:0xb7cfb7d0>
>> regex.match 'sos'
=> nil
>> regex.match 'sauce'
=> #<MatchData:0xb7c12bac>
>> regex.match 'hatoff'
=> nil
>>
That's a better one.
on 2007-01-08 06:20
Here is my solution. I didn't post the whole thing. This is the main
engine minus the Tk UI class. You can get the full solution
(dictionary, graphics and ruby source at
http://www.dalemartenson.com/files/rubytexttwist.tar.gz
--Dale Martenson
class TextTwist
# Algorithm derived from online book by Robert Sedgewick and Kevin
Wayne.
# It was origomally written in Java.
#
# References:
# http://www.cs.princeton.edu/introcs/31datatype/
# http://www.cs.princeton.edu/introcs/31datatype/TextTwist.java.html
class Profile < Hash
def initialize( word )
super
word.downcase.each_byte do |b|
self[b.chr] = 0 unless self.has_key?(b.chr)
self[b.chr] += 1
end
end
def contains( p )
p.each_pair do |k,v|
return false unless self.has_key?(k)
return false if self[k] < v
end
true
end
end
attr_reader :word, :words
def initialize( dictionary, word=nil )
@dictionary = dictionary
@dictionary.collect! {|x| x.downcase}
@dictionary.sort!
word.nil? ? restart : start( word )
end
def start( word )
@word = word.downcase
@profile = Profile.new( @word )
@words = process
end
def process
result = []
@dictionary.each do |dw|
next if dw.length < 3 || dw.length > @word.length
result << dw if @profile.contains( Profile.new( dw ) )
end
result
end
def mix
a = @word.split(//)
1.upto(a.length) do
i1 = rand(a.length)
i2 = rand(a.length)
t = a[i1]
a[i1] = a[i2]
a[i2] = t
end
a
end
def check( word )
@words.include?( word.downcase )
end
def contains( word )
@profile.contains( Profile.new( word ) )
end
def restart
six_letter_words = []
@dictionary.each do |w|
six_letter_words << w if w.length == 6
end
start( six_letter_words[ rand(six_letter_words.size) ] )
end
end
class TextTwistUI
# ... SNIP ...
end
# MAIN PROGRAM -- where the magic begins
# Using the crossword dictionary from "Moby Word Lists" by Grady Ward
(part of
# Project Gutenberg). I reduced the dictionary to only words that are 3
to 6
# characters in length.
#
# Note: This may not be the best dictionary for this game, but it
works. It
# does contain numerous obscure words which is both good and bad.
#
# irb(main):001:0> f = File.open("crosswd.txt")
# => #<File:crosswd.txt>
# irb(main):002:0> of = File.open("3-6.txt","w+")
# => #<File:3-6.txt>
# irb(main):003:0> f.each_line do |line|
# irb(main):004:1* l = line.strip
# irb(main):005:1> if l.length >= 3 && l.length <= 6 then
# irb(main):006:2* of.puts( l )
# irb(main):007:2> end
# irb(main):008:1> end
# => #<File:crosswd.txt>
# irb(main):009:0> of.close
# => nil
#
# References:
# http://www.gutenberg.org/etext/3201
dictionary = []
f = File.open("3-6.txt")
dictionary = f.read.split
tt = TextTwist.new( dictionary )
TextTwistUI.new( tt )
Tk.mainloop
on 2007-01-08 07:10
Here is my solution. I'm not too good with regular expressions, so the
word selection script is a little bit slow.
#!/usr/bin/env ruby
#
# Author: Dan Manges - http://www.dcmanges.com
# Ruby Quiz #108 - http://rubyquiz.com/quiz108.html
class WordList
include Enumerable
attr_accessor :file
attr_reader :filters
def initialize(file = nil)
@file, @filters = file, []
end
def each
File.open(@file, "r") do |file|
while line = file.gets
yield apply_filters(line.chomp)
end
end
end
protected
def apply_filters(word)
@filters.inject(word) do |word, filter|
filter.call(word)
end
end
end
# Module to select words based on length and letter composition.
module WordFinder
# Finds words of length +size+ which can be composed with the letters
in +base_word+
def find_words(size, base_word)
letter_counts = base_word.split(//).inject(Hash.new(0)) {
|hash,letter| hash[letter] += 1; hash }
regexp = Regexp.new("^" + letter_counts.map { |letter,count|
"#{letter}{0,#{count}}"}.sort.join + "$")
select { |word| word.to_s.size == size && word.split(//).sort.join
=~ regexp }
end
# Finds a random word of the given size
def random_word_of_size(size)
words = find_words(size, (('a'..'z').to_a * 3).join)
words[rand(words.size)]
end
end
WordList.send(:include, WordFinder)
# Dictionary file from: http://wordlist.sourceforge.net/
# http://prdownloads.sourceforge.net/wordlist/alt12dicts-4.tar.gz
@wordlist = WordList.new("/Users/dan/Desktop/alt12dicts/2of12full.txt")
# This particular wordlist has an offset
@wordlist.filters << lambda { |word| word[17..-1] }
# Skip proper names, contractions, etc.
@wordlist.filters << lambda { |word| word =~ /^[a-z]+$/ ? word : "" }
module WordBlender
class Round
def initialize(wordlist, word_size = (3..6))
@wordlist = wordlist
@min_size, @max_size = word_size.first, word_size.last
@qualified = false
@hits = Hash.new { |h,k| h[k] = [] }
load_words
end
def qualified?
@qualified
end
def guess?(word)
word = word.to_s.strip
dup?(word) || qualify?(word) || hit?(word) || :miss
end
def letters
@base.split(//).sort
end
def status
result = []
@min_size.upto(@max_size) do |size|
result << [size, @hits[size].size, @words[size].size]
end
result.map { |data| "#{data[0]} letters: #{data[1]} of
#{data[2]}"}.join(", ")
end
protected
def dup?(word)
:dup if @hits[word.size].include?(word)
end
def qualify?(word)
if @words[word.size].include?(word) and word.size == @max_size
@hits[word.size] << word
@qualified = true
:qualify
end
end
def hit?(word)
if @words[word.size].include?(word)
@hits[word.size] << word
:hit
end
end
def load_base_word
@base = @wordlist.random_word_of_size(@max_size)
end
def load_words
@words = Hash.new([])
load_base_word
@min_size.upto(@max_size) do |size|
@words[size] = @wordlist.find_words(size, @base)
end
end
end
class Game
def initialize(wordlist)
@wordlist = wordlist
reset
end
def start!
help
start_round
print 'guess> '
while input = gets
input = input.strip
break if input == ".quit"
if input[0,1] == "." && respond_to?(input[1..-1])
send(input[1..-1])
print 'guess> '
next
end
result = @round.guess?(input)
puts case result
when :miss
"Wrong!"
when :dup
"Already guessed that!"
when :hit
"You got it!"
when :qualify
"You got it! And you qualify for the next round!"
end + " " + input
status unless result == :miss
print 'guess> '
end
puts "Goodbye!"
end
alias :play! :start!
protected
def letters
puts "Available Letters: " + @round.letters.sort_by {rand}.join(',
')
end
def next
if @round.qualified?
start_round
else
puts "You have not yet qualified for the next round!"
end
end
def help
puts <<-END_HELP
When prompted, either enter a word or a command.
The following commands are available:
.quit => quits the game
.help => display this help
.next => goes to the next round (if qualified)
.letters => display available letters
.status => show the current status of this round
END_HELP
end
def reset
@round_number = 0
end
def start_round
@round_number += 1
@round = Round.new(@wordlist)
puts "Beginning Round #{@round_number}!"
letters
end
def status
puts @round.status
end
end
end
@blender = WordBlender::Game.new(@wordlist)
@blender.play!
on 2007-01-08 08:28
My game will play almost like the one you can play at yahoo without
score. Has
cheating and giving-up commands and draws possible solutions on the
screen.
Run it with wordblender.rb <dictfile>.
martin
# wordblender.rb
#
# Usage: wordblender.rb [dictfile]
#
class String
# Checks if string can be build out of these characters.
#
# "hello".build_outof?("llohe") => true
# "world".build_outof?("dlrowl") => true
def build_outof?(other)
return false if self.length > other.length
o = other.clone
self.each_byte do |c|
return false unless o.include?(c.chr)
o[o.index(c.chr)] = 0
end
true
end
# Shuffle a word.
#
# "hello".shuffle => "oellh"
def shuffle
return self.scan(/./).sort_by{rand}.to_s
end
end
class WordBlenderGame
attr_reader :words
# limits for words for the game
MINCHARACTERS = 3
MAXCHARACTERS = 6
# time limit per game
TIME_PER_GAME = 90
# how to display the board
DISPLAY_COLUMNS = 5
# read the dictionary from a file.
# we also keep words with length of MAXCHARACTERS to find
# good initial letters quickly.
def initialize(dictionary)
@words, @maxwords = [], []
File.open(dictionary).each do |line|
l = line.strip.downcase
@words << l if (l.length >= MINCHARACTERS && l.length <=
MAXCHARACTERS)
@maxwords << l if l.length == MAXCHARACTERS
end
end
# this generates a bunch of letters to play with and looks up words
# that can be build by them from the dictionary ("candidates").
def prepare_game()
@letters = @maxwords[rand(@maxwords.size-1)].shuffle
@candidates = []
@words.each { |w| @candidates << w if w.build_outof?(@letters) }
@candidates = @candidates.uniq # this fixed duplicated entries
@candidates = @candidates.sort {|x,y| x.length <=> y.length }
@found_candidates = @candidates.collect { false }
end
#
# This is to display the candidates to the screen. Draws it into
columns
# and returns a string.
#
def get_board(solution=false, title="Words to find")
result = "" ; i = 0
sempty = ' '*(DISPLAY_COLUMNS*(MAXCHARACTERS+2))
s = String.new(sempty)
result << title << ":\n"
@found_candidates.each_index do |idx|
f = @found_candidates[idx] || solution
s[i.modulo(DISPLAY_COLUMNS)*(MAXCHARACTERS+2)] =
f ? "[#{@candidates[idx]}]" : "["+(' '*@candidates[idx].length)+"]"
if i.modulo(DISPLAY_COLUMNS) == DISPLAY_COLUMNS-1 then
result << (s + "\n")
s = String.new(sempty)
end
i+=1
end
result << s if s.include?('[')
result << "\n"
end
# This plays one round of the game, returns true if won
def play
self.prepare_game
message = "Press RETURN to shuffle the letters, '!' to give up, '?'
to
cheat."
# start the time.
@time = TIME_PER_GAME
timer = Thread.new { while true do @time-=1; sleep 1 end }
# game loop
while @found_candidates.include?(false) do
# print board and other stuff
puts get_board
puts
puts "Time: " + @time.to_s
puts "Msg: " + message if message != ''
puts "Use: " + @letters
print "Try: "
# get user's guess and handle it
$stdout.flush
s = STDIN.gets.downcase.strip
if @time <= 0 then
puts "Time's up!"
break
end
if s == "" then
@letters = @letters.shuffle
message = "Letters shuffled!"
next
end
break if s == '!'
if s == '?' then
puts get_board(true)
message = "Cheater!"
next
end
if !s.build_outof?(@letters) then
message = "Invalid word!"
next
end
if @candidates.include?(s) then
@found_candidates[@candidates.index(s)] = true
message = "#{s} Found!"
else
message = "#{s} not listed!"
end
end
Thread.kill(timer)
# print solution
puts get_board(true, "Solution is")
# Check if player found a word with all characters
@found_candidates.each_index do |idx|
return true if @found_candidates[idx] && @candidates[idx].length
==
MAXCHARACTERS
end
false
end
end
print "Loading game...";$stdout.flush
game = WordBlenderGame.new(ARGV[1] || '/usr/share/dict/words')
puts "#{game.words.size} words found."
while game.play do
puts "You won, press any key to play next round."
gets
end
puts "Game over!"
on 2007-01-08 14:07
On Sunday 07 January 2007 15:04, Ionut Artarisi wrote: > 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. > > -Ionuţ Arţărişi I remembered a mistake that i had done. Well not actually a mistake. I just wrote some 10 lines of code that did some extra work that i never actually used later in the program. I deleted those lines now, which resulted in a unidimensional array (it was bidimensional before). This version should be more readable and should make more sense though there's no extra functionallity. So the version on rubyquiz.com should be replaced with this one, for the sake of future readers if it's not too much of a trouble. Thank you. And thank you for rubyquiz.com. It's fantastic!
on 2007-01-09 20:44
Here's my submission. There are two programs: the first builds a set
of puzzles from a decent word list I found on line. Files like
/usr/dict/words have too many obscure words. The second is a (very)
simplistic game interface.
A "puzzle" is just a string of six or more 3-6 letter words, sorted by
length. The output of the first program is just one line per puzzle,
with the words separated by colons. The words are ROT13 encoded. The
puzzles.rb program generates 905 different puzzles.
Bob Showalter
---------- puzzles.rb ----------
# puzzles.rb
# generate puzzles for use by wordblend.rb program
#
# usage: ruby puzzles.rb >puzzles.dat
require 'open-uri'
require 'yaml'
# these urls point to text files with lists of 2000 commonest
# English word "families", including plurals and other forms.
# this ends up generating reasonably good puzzles.
URIS = %w{
http://www1.harenet.ne.jp/~waring/vocab/wordlists/full1000.txt
http://www1.harenet.ne.jp/~waring/vocab/wordlists/full2000.txt
}
# minimum number of words necessary to form a puzzle
MIN_SIZE = 6
# define some helper functions
class String
# returns string with characters in sorted order
def sort
split(//).sort.join
end
# returns true if s is a subword of the string. both
# the string and s must be sorted!
def subword?(s)
i = j = 0
while j < s.length
i += 1 while i < length and self[i] != s[j]
i < length or return false
j += 1
i += 1
end
true
end
end
# grab the 3-6 letter words from word lists. sort each word by
# character (e.g. 'test' becomes 'estt'), and then accumulate
STDERR.puts "Fetching words..."
words = Hash.new {|h,k| h[k] = []}
URIS.each do |uri|
open(uri) do |f|
f.read.split.select {|w| w.length >= 3 and w.length <= 6}.each do
|word|
word.upcase!
sword = word.sort
words[sword] << word
end
end
end
# find puzzles by looking at which sorted words are contained in
# other six-character sorted words.
STDERR.puts "Finding puzzles..."
n = 0
words.keys.select {|w| w.length == 6}.each do |key|
puzzle = words.select {|ssub, subs| key.subword?(ssub)}.collect {|a|
a.last}.flatten.sort_by {|w| "#{w.length}#{w}"}
next if puzzle.size < MIN_SIZE
puts puzzle.join(':')
end
---------- wordblend.rb ----------
# wordblend.rb
# simplistic Word Blend puzzle game
# uses puzzles.dat file created by separate puzzles.rb program
class String
def rot13
tr 'A-Za-z', 'N-ZA-Mn-za-m'
end
end
class Puzzle
attr_reader :words, :letters, :board
def self.pick
@@puzzles ||= IO.readlines('puzzles.dat')
new(@@puzzles[rand(@@puzzles.size)].chomp.rot13.split(':'))
end
def initialize(words)
@words = words
scramble
@board = words.collect {|w| w.gsub(/./, '-')}
end
def scramble
@letters = words.last.split(//).sort_by {rand}.join
scramble if words.include? @letters
end
def help
puts "Enter 'Q' to give up, 'S' to scramble letters"
end
def play
help
turn while board != words
puts board
end
def turn
puts board
puts
puts letters
while true
print "? "
guess = gets.strip.upcase
if guess == ''
help
redo
end
if guess == 'S'
scramble
puts letters
redo
end
@board = words.dup if guess == 'Q'
i = words.index(guess) and board[i] = guess
break
end
end
end
# play a random game
p = Puzzle.pick
p.play
on 2007-01-09 22:09
On 1/7/07, Fedor Labounko <fedor.labounko@gmail.com> wrote: > Unfortunately I don't know much about this but now I wonder if it's possible > to find all partial permutations of a word with a regexp. > > Here's a revision to Daniel's approach that seems to work well: # Open and read the dictionary. dict = IO.read("/usr/share/dict/words").scan(/^[a-z]{3,6}$/) # Pick a random word with 6 letters. baseWord = dict.grep(/^[a-z]{6}$/).rand_elem # Find words that use the same letters sortWord = baseWord.scan(/./).sort.to_s selectedWords = dict.select {|w| Regexp.new(w.scan(/./).sort.join('.*')).match(sortWord) } I started by just extracting only the 3-6 letter words. Then I sort the base word so the letters are in order. Let's say the baseWord is "parlor". Then sortWord would be "aloprr". Now for each word in the dictionary, sort it in letter order and create a regex. Suppose the word is "roar". The sorted version is "aorr" and the regex is "a.*o.*r.*r". If that regex matches the sortWord, we found a valid subword. This could be sped up by precompiling the regexes I would guess. Bob
on 2007-01-09 22:43
On 1/9/07, Bob Showalter <showaltb@gmail.com> wrote: > > want to exclude reusing the same letter more than once > > > This could be sped up by precompiling the regexes I would guess. > > Bob > > Very interesting, you create a matching in the opposite direction, matching against the picked word vs against the dictionary of words, very nice. I like this more than Daniel's approach since it doesn't involve creating Regexps which grow as n^2 compared to the length of our word. This ends up going through all the words (length 3 to 6 anyhow) in the dictionary to find the matches. Finding all permutations would go through the dictionary roughly the number of subwords times, taking log of the length of the dictionary number of steps each time. For all but the largest of words with lots of subwords would the latter be slower, but the Regexp approach is very nice and simple, and I can't decide what I like more. It does bother me a bit though that I don't know how long each regexp takes to run to get its match as I know that some Regexps do take quite a while to match or exclude some words, just not sure if this one would be one of them.
on 2007-01-10 00:11
The regexes for me are fairly fast. For my regex that weeds out words
with repeated characters (I posted it before and it is fairly lengthy,
so I won't post it again unless by request), a 10,000 word dictionary
(the one in my /usr/share/dict/words) takes less than .5 seconds to pick
the baseword and all related words. Under .2 seconds if it doesn't weed
out words with repeated characters.
One thing I can see to optimize in your method is all the sorting. I
haven't tested the performance yet, but sets might be the answer.
require 'set'
class String
def letters
split(//).to_set
end
end
baseWordSet = baseWord.letters
dict.select {|x| x.letters.subset?(baseWordSet)}
It's more readable, IMHO, at least.
Dan
on 2007-01-10 01:12
On 1/9/07, Daniel Finnie <danfinnie@optonline.net> wrote: > end > end > > baseWordSet = baseWord.letters > dict.select {|x| x.letters.subset?(baseWordSet)} > > It's more readable, IMHO, at least. > > Dan I used sets in my submission, and the letters of each 6 letter word were split in the above fashion so as to provide faster scanning (at least according to one of the books I was reading the night before - please correct me if I'm wrong). The answers to these questions was actually what I was hoping to hear when I said any feedback would be appreciated :) And yes, it's definitely more readable - I wrestled with the idea of how to see if one array was a subset of another (and which way was more efficient) when I came across the concept of a set for the first time. s2.subset?(s1) sure made life easy and using it made it a lot easier to understand what my code was doing just by reading it. One last question, which I've been thinking about but not able to dedicate a lot of time to is this: how do you 'benchmark' the speed of individual programs? I've been wondering how fast my program is compared to others since I wrote it. And finally, here's to you, Mr. Ruby Quiz creator guy, for coming up with a Ruby quiz that really peaked my interest :)
on 2007-01-10 01:29
If you are using *nix (unix, linux) then you can run "time <ruby program>" at the command line. This might work with Macs as well because they are based on unix but I don't have one so I'm not sure. From within Ruby, you can do this: require 'benchmark' puts Benchmark.measure do # program code here. end I agree that this was one of the best Ruby Quizzes. It is simple and yet has different, but equally good, types of solutions (regex, sets, etc.). I also enjoyed the opportunity to explore sets and things; more RQs that can involve the standard library would be awesome. Dan
on 2007-01-10 03:11
On Fri, 05 Jan 2007 22:05:52 +0900, Ruby Quiz wrote: > 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/texttwist.html (just one example, java) > [2]: http://www.gamehouse.com/ require 'rubygems' require 'facets/core/enumerable/permutation' require 'set' #usage: texttwist [word] # specifying a word will use /usr/share/dict/words to solve a TextTwist # problem. If no word is specified, a random word will be selected, to # generate an round of texttwist. #load dictionary matcher=Set.new allwords=Array.new open("/usr/share/dict/words") do |f| f.each do |line| line.chomp! next if line !~ /^[a-z]+$/ matcher << line if line.length<=6 allwords << line if line.length==6 end end #generate subwords of a word word=ARGV[0] || allwords[rand(allwords.length)] thiswordmatcher=Set.new word.split(//).each_permutation do |perm| perm=perm.join (3..6).each do |len| candidate=perm[0,len] if matcher.include?(candidate) thiswordmatcher << candidate end end end #output puts word puts "======" thiswordmatcher.each do |subword| puts subword end
on 2007-01-10 03:18
Hi Ken, Ken Bloom wrote: > #load dictionary > matcher=Set.new > allwords=Array.new > thiswordmatcher=Set.new I'm interested in why you used a set for matcher and an array for allwords. Was this a kindof random decision (many of mine are!) or is there something that allwords needed to be able to do and so is an array or vice versa? Thanks, Dan
on 2007-01-10 04:58
On Jan 9, 2007, at 6:28 PM, Daniel Finnie wrote: > If you are using *nix (unix, linux) then you can run "time <ruby > program>" at the command line. This might work with Macs as well > because they are based on unix but I don't have one so I'm not sure. It works on a Mac, sure. James Edward Gray II
on 2007-01-10 05:10
On Wed, 10 Jan 2007 11:17:24 +0900, Daniel Finnie wrote: > allwords. Was this a kindof random decision (many of mine are!) or is > there something that allwords needed to be able to do and so is an array > or vice versa? allwords needs array indexing to pick a random word from the array. The sets, however, need duplicate removal and/or need include? to operate in O(1). --Ken
on 2007-01-10 16:01
On Jan 7, 2007, at 6:47 AM, Fedor Labounko wrote: > It just so happened that I was learning ruby last summer and wrote > a program to cheat for me at TextTwist. Needless to say the game > got boring really fast, but it was neat writing the program. I've > modified it a bit to instead play the game, but I'm a fairly new > user and would appreciate any feedback, both ruby related and > general programming. Looking pretty good to me. > Also, I have the problem that when running this on Windows it > doesn't allow me to manipulate files with Ruby, giving me a > Permission Denied error on File.open. Any idea why this might be? Well, you pass open() three arguments: File.open("reducedwordlist#{i}.txt", "w", File::CREAT) do |fout| I'm pretty sure that form has the third argument being the permissions of the new file. Take out the File::CREAT, the "w" mode string handles that automatically, and see if that fixes it up. Hope that helps. James Edward Gray II
on 2007-01-10 16:14
On Jan 7, 2007, at 6:41 PM, Daniel Finnie wrote: > Oops, that doesn't match {3,6} just {6}. > > >> regex = /^([a-z])(?!\1)([a-z])(?!\1|\2)([a-z])(?:(?!\1|\2|\3)([a- > z]))?(?:(?!\1|\2|\3|\4)([a-z]))?(?:(?!\1|\2|\3|\4|\5)([a-z]))?$/ Wow. That's darn clever, but probably over abusing regular expressions a bit, don't you think? ;) James Edward Gray II
on 2007-01-10 16:26
On Jan 9, 2007, at 6:11 PM, Jason Mayer wrote: > And finally, here's to you, Mr. Ruby Quiz creator guy, for coming > up with a Ruby quiz that really peaked my interest :) Thank Ben Bleything. It was his idea. James Edward Gray II
on 2007-01-10 16:28
On Jan 9, 2007, at 6:28 PM, Daniel Finnie wrote: > I also enjoyed the opportunity to explore sets and things; more RQs > that can involve the standard library would be awesome. You make the quizzes and I will run them: suggestion@rubyquiz.com James Edward Gray II
on 2007-01-10 21:19
That makes sense :).
On a side note, I did make some methods for getting a rand value from
any enumerable (they can be integrated as instance methods):
def rand_value_each(enum)
index = rand(enum.size)
curr = 0
enum.each {|x| return [x].flatten.last if curr == index; curr += 1 }
end
def rand_value_to_a(enum)
[rand_value_ary(enum.to_a)].flatten.last
end
def rand_value_ary(ary)
ary[rand(ary.size)]
end
Dan
on 2007-01-10 21:24
On Jan 10, 2007, at 2:18 PM, Daniel Finnie wrote: > On a side note, I did make some methods for getting a rand value > from any enumerable (they can be integrated as instance methods): > > def rand_value_each(enum) > index = rand(enum.size) Enumerable doesn't have a size() method. > curr = 0 > enum.each {|x| return [x].flatten.last if curr == index; curr += 1 } > end James Edward Gray II
on 2007-01-10 21:27
It does hurt readability, however, if it is in a variable then the
readability goes up a little. regex is probably a bad name, tho.
noRepeats3-6Letters = ...
dict.scan(noRepeats3-6Letters)
Even better would be a method that creates a regex for an arbitrary
length so it's ugliness is never shown in its entirety in one
concentrated spot. The king of readability, I admit, doesn't involve
/regexp?e[ns]/ at all.
class Array
def uniq?
(uniq == self)
end
end
class String
def letters
split(//)
end
end
dict.select {|x| x.length.between?(3, 6) && x.letters.uniq?}
dan
on 2007-01-10 22:45
Vincent Fourmond wrote long time ago: >"Try transposing your matrix first, if that is what you need. Are you >dealing with transformation matrices (which are not real matrices) ?" >Yes indeed, SVG 2D transformation matrices. These matrices are 3x3. They never taught me about transposing, just matrix multiplication, and googel didnt help either. What is transposing (for transformation matrices)? Cameron McBride wrote: >"the included Matrix library in ruby is NOT FAST. There are other libs >that do this on the C level, such as NArray, that will allow much better >performance for some graphical uses." Thanks. While my simple graphic genny isnt time-critical, and a SVG file might take 2-3 seconds to render, I will look into NArray. Is NArray what you would recommend?
on 2007-01-10 22:55
T. W. Urp wrote: > Vincent Fourmond wrote long time ago: > >"Try transposing your matrix first, if that is what you need. Are you > >dealing with transformation matrices (which are not real matrices) ?" > >Yes indeed, SVG 2D transformation matrices. These matrices are 3x3. > > They never taught me about transposing, just matrix multiplication, and > googel didnt help either. What is transposing (for transformation matrices)? If you have a 3x3 matrix like: a b c d e f g h i then its transpose is: a d g b e h c f i If you have a 2x3 matrix like: a b c d e f then its transpose is: a d b e c f In other words, swap rows and columns. (Excel even knows how to do this, under Paste Special.)
on 2007-01-10 22:56
T. W. Urp wrote: > They never taught me about transposing, just matrix multiplication, and > googel didnt help either. What is transposing (for transformation matrices)? Also, RTFM :) C:\>ri transpose More than one method matched your request. You can refine your search by asking for information on one of: Array#transpose, Matrix#transpose C:\>ri Array.transpose -------------------------------------------------------- Array#transpose array.transpose -> an_array ------------------------------------------------------------------------ Assumes that _self_ is an array of arrays and transposes the rows and columns. a = [[1,2], [3,4], [5,6]] a.transpose #=> [[1, 3, 5], [2, 4, 6]] C:\>ri Matrix.transpose ------------------------------------------------------- Matrix#transpose transpose() ------------------------------------------------------------------------ Returns the transpose of the matrix. Matrix[[1,2], [3,4], [5,6]] => 1 2 3 4 5 6 Matrix[[1,2], [3,4], [5,6]].transpose => 1 3 5 2 4 6 (also known as t)
on 2007-01-10 23:10
"T. W. Urp" <twu@orb.free.net> writes: > Vincent Fourmond wrote long time ago: >>"Try transposing your matrix first, if that is what you need. Are you >>dealing with transformation matrices (which are not real matrices) ?" >>Yes indeed, SVG 2D transformation matrices. These matrices are 3x3. > > They never taught me about transposing, just matrix multiplication, > and googel didnt help either. What is transposing (for > transformation matrices)? Matthew 20:16 for the dimensions.
on 2007-01-10 23:12
On 1/10/07, James Edward Gray II <james@grayproductions.net> wrote: > James Edward Gray II > > > Aah, thanks so much. I must have misread that before, never noticed the bit about permissions. I tried changing that line before but it still didn't work since I was trying to modify the file I created with the weird permission (whatever permission option File::CREAT would be interpreted as). Erasing and starting over it all works as expected, creating and overwriting the file on subsequent calls. Thanks!
on 2007-01-11 00:15
On Wed, 10 Jan 2007 13:54:43 -0800, Phrogz wrote: > C:\>ri transpose > More than one method matched your request. You can refine > your search by asking for information on one of: > > Array#transpose, Matrix#transpose > C:\>ri Array.transpose > C:\>ri Matrix.transpose Thats very nice. I never realized such a great help tool exists! Thank you Phrogz!! :)
on 2007-01-11 03:16
Ruby Quiz wrote: > > published in various places around the web. > Bonus points for building a completely functional game! > > [1]: http://games.yahoo.com/games/texttwist.html (just one example, java) > [2]: http://www.gamehouse.com/ class String def chars split("") end def sorted chars.sort.join end end # Generate combinations. def comb array, n, str = "", &blk 0.upto(array.size - n){|i| if 1 == n yield str + array[i] else comb array[i+1..-1], n-1, str+array[i], &blk end } end word_groups = Hash.new {[]} shorts = Hash.new {[]} while word = gets do next unless (word=word.downcase.delete('^a-z')).size.between?(3,6) if 6 == word.size word_groups[word.sorted] += [ word ] else shorts[word.sorted] += [ word ] end end word_groups.each_key{|key| 3.upto(5){|n| combinations = [] comb( key.chars, n ){|s| combinations << s} combinations.uniq.each{|s| word_groups[key] += shorts[s] }}}
on 2007-01-11 03:46
T. W. Urp wrote: > Vincent Fourmond wrote long time ago: > >> "Try transposing your matrix first, if that is what you need. Are you >> dealing with transformation matrices (which are not real matrices) ?" >> Yes indeed, SVG 2D transformation matrices. These matrices are 3x3. >> > > They never taught me about transposing, just matrix multiplication, and > googel didnt help either. What is transposing (for transformation matrices)? > Given a matrix "a" with "m" rows and "n" columns for i = 1 to m do for j = 1 to n do atranspose[j, i] = a[i, j] end end > I will look into NArray. Is NArray what you would recommend? > Yes! -- M. Edward (Ed) Borasky, FBG, AB, PTA, PGS, MS, MNLP, NST, ACMC(P) http://borasky-research.blogspot.com/ If God had meant for carrots to be eaten cooked, He would have given rabbits fire.
on 2007-01-11 12:23
This just solves the find-all-subwords problem:
target = ARGV[0]
dict = ARGV[1] || 'sowpods'
reduced = target.split(//).sort.uniq.join
primes = [2, 3, 5, 7, 11, 13]
factors = []
reduced.split(//).each_with_index {|e, i|
factors[e[0]] = primes[i]
}
target_num = 1
target.each_byte {|i| target_num *= factors[i]}
IO.foreach(dict) {|word|
word.chomp!
next unless (word =~ /^[#{reduced}]+$/) &&
(word.length < 7) && (word.length > 2)
p = 1
word.each_byte {|i| p *= factors[i]}
puts word if target_num % p == 0
}
on 2007-01-11 15:16
On 1/11/07, Martin DeMello <martindemello@gmail.com> wrote: > factors[e[0]] = primes[i] > word.each_byte {|i| p *= factors[i]} > puts word if target_num % p == 0 > } > > That's neat, and is a good general way of checking for inclusion (so you can extend it past characters in a string which might not have an .include? method). You probably don't want that .uniq in there though as that excludes you from matching 'hell' out of 'hello', for example.
on 2007-01-11 15:27
On 1/11/07, Fedor Labounko <fedor.labounko@gmail.com> wrote: > > reduced.split(//).each_with_index {|e, i| > > p = 1 > > word.each_byte {|i| p *= factors[i]} > > puts word if target_num % p == 0 > > } > > That's neat, and is a good general way of checking for inclusion (so you can > extend it past characters in a string which might not have an .include? > method). You probably don't want that .uniq in there though as that excludes > you from matching 'hell' out of 'hello', for example. No, I do need the uniq since I want one prime per unique letter in the target word. I then calculate its signature by a loop over the entire word, not the reduced word, so "hell" is indeed matched for "hello" but not for, say, "whelps". martin
on 2007-01-19 16:30
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?
on 2007-01-19 16:31
On 1/5/07, Ruby Quiz <james@grayproductions.net> wrote: > > > the > Bonus points for building a completely functional game! > > [1]: http://games.yahoo.com/games/texttwist.html (just one example, > java) > [2]: http://www.gamehouse.com/ 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)?
on 2007-01-19 16:31
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
on 2007-01-19 16:31
On 1/5/07, Matthew Moss <matthew.moss.coder@gmail.com> 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? Official scrabble list of accepted 6 letter words: http://www.math.utoronto.ca/jjchew/scrabble/lists/ospd-only-6.html
on 2007-01-19 16:31
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.sourceforge.net/ I don't know how good they are, but they're ok for this particular quiz IMHO
on 2007-09-25 22:30
On 1/5/07, Jason Mayer <slamboy@gmail.com> wrote: > > > > > > Official scrabble list of accepted 6 letter words: > > http://www.math.utoronto.ca/jjchew/scrabble/lists/ospd-only-6.html > > > > My mistake, that's the list of *obscure* accepted 6 letter words. I'll > find a better list this afternoon. > http://www.scrabulous.com/twl_dictionary.php Complete list of accepted scrabble words. You'll need to go through and nuke any long words... I think learn Ruby in 21 days has a regex on day 8 that can assist people with this :)
on 2007-09-25 22:34
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.
on 2007-09-25 22:36
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
on 2007-09-25 22:38
On 1/5/07, Jason Mayer <slamboy@gmail.com> wrote: > Official scrabble list of accepted 6 letter words: > http://www.math.utoronto.ca/jjchew/scrabble/lists/ospd-only-6.html > My mistake, that's the list of *obscure* accepted 6 letter words. I'll find a better list this afternoon.
on 2007-09-25 22:41
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
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.