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 06.01.2007 19:31
on 07.01.2007 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 07.01.2007 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 07.01.2007 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 07.01.2007 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 07.01.2007 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 07.01.2007 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 07.01.2007 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 07.01.2007 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 07.01.2007 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 08.01.2007 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 08.01.2007 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 08.01.2007 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 08.01.2007 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 08.01.2007 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 08.01.2007 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 08.01.2007 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 08.01.2007 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 08.01.2007 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 09.01.2007 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 09.01.2007 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 09.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 10.01.2007 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 11.01.2007 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 11.01.2007 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 11.01.2007 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 11.01.2007 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 11.01.2007 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 11.01.2007 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 19.01.2007 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 19.01.2007 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 19.01.2007 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 19.01.2007 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 19.01.2007 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 25.09.2007 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 25.09.2007 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 25.09.2007 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 25.09.2007 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 25.09.2007 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