[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

[QUIZ] Animal Quiz (#15

James Gray

1/14/2005 2:23:00 PM

The three rules of Ruby Quiz:

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

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

http://www.grayproductions.net/...

3. Enjoy!

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

by Jim Weirich

Here's a program I've had a lot of fun with and might make a good Ruby
Quiz entry. The program is a animal quiz program.

It works like this. The program starts by telling the user to think
of an animal. It then begins asking a series of yes/no questions
about that animal: does it swim, does it have hair, etc. Eventually,
it will narrow down the possibilities to a single animal and guess
that (Is it a mouse?).

If the program has guessed correctly, the game is over and may be
restarted with a new animal. If the program has guess incorrectly, it
asks the user for the kind of animal they were thinking of and then
asks for the user to provide a question that can distinguish between
its incorrect guess and the correct answer. It then adds the new
question and animal to its "database" and will guess that animal in
the future (if appropriate).

[ Editor's Note: Here's a sample run of my solution, by way of example:

Think of an animal...
Is it an elephant? (y or n)
n
You win. Help me learn from my mistake before you go...
What animal were you thinking of?
a rabbit
Give me a question to distinguish a rabbit from an elephant.
Is it a small animal?
For a rabbit, what is the answer to your question? (y or n)
y
Thanks.
Play again? (y or n)
y
Think of an animal...
Is it a small animal? (y or n)
y
Is it a rabbit? (y or n)
n
You win. Help me learn from my mistake before you go...
What animal were you thinking of?
a Shih Tzu
Give me a question to distinguish a Shih Tzu from a rabbit.
Is it a kind of dog?
For a Shih Tzu, what is the answer to your question? (y or n)
y
Thanks.
Play again? (y or n)
y
Think of an animal...
Is it a small animal? (y or n)
y
Is it a kind of dog? (y or n)
y
Is it a Shih Tzu? (y or n)
y
I win. Pretty smart, aren't I?
Play again? (y or n)
n

-JEG2 ]


10 Answers

Glenn Parker

1/16/2005 3:43:00 PM

0

Not to toot my own horn, but this was an easy one. I spent more time
polishing and commenting than I did on actual coding. Viva la Ruby!

--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoi...
#!/usr/bin/env ruby -w

STDOUT.sync = true

# $qtree is the root of a tree where non-leaf nodes are 3-element arrays.
# The first element of a non-leaf node is a question. The second
# and third elements are nodes. The second element corresponds
# to a "yes" answer for the question, and the third element corresponds
# to a "no" answer for the question. A leaf node is a 1-element array
# that contains an animal name. The initial node for $qtree is a leaf
# node for the animal "human".

$qtree = [ "human" ]

# $current_node, $parent_node, and $parent_branch maintain our current
# position while navigating down the tree. Except when $parent_node
# is nil, $parent_node[$parent_branch] == $current_node.

$current_node = $parent_node = $parent_branch = nil

def main
$log = File.open("animal-log.txt", "a+")
# Replay all previous sessions from the logfile to initialize $qtree.
read_from($log) until $log.eof
# Play interactively.
read_from(STDIN)
$log.close
end

def read_from(i)
$istream = i
$replay = ($istream != STDIN)
loop do
prompt "Would you like to play a game? "
if get_answer.downcase[0] == ?y
prompt "Please think of an animal...\n\n"
play
else
prompt "Good bye.\n"
break
end
end
end

# Print a prompt unless we are in replay mode.
def prompt(str)
print str unless $replay
end

# Get an answer and log it unless we are in replay mode.
def get_answer
input = $istream.gets.chomp
$log.puts(input) unless $replay
input
end

# Play a round of the game
def play
# Reset pointers to top of $qtree.
$parent_node = $parent_branch = nil
$current_node = $qtree
# Keep guessing until we're done.
while guess; end
end

def guess
question = $current_node.length == 1 ?
"Is your animal \"" + $current_node[0] + "\"? " :
$current_node[0]
prompt question
answer = get_answer.downcase[0]

if $current_node.length == 1
if answer == ?y
prompt "I win!\n\n"
else
learn
end
return false
else
$parent_node = $current_node
$parent_branch = (answer == ?y) ? 1 : 2
$current_node = $parent_node[$parent_branch]
return true
end
end

def learn
last_animal = $current_node[0]
prompt "I give up. What is your animal? "
animal = get_answer
prompt "What question distinguishes \"#{last_animal}\" from \"#{animal}\"?\n"
question = get_answer
# Adjust the punctuation at the end of the question.
question.sub!(/\??\s*$/, '? ')
prompt "What is the answer to this question for \"#{animal}\"? "
yes = (get_answer.downcase[0] == ?y)
prompt "Thank you.\n\n"

# Build a new node refering to $current_node,
# then insert it into the location of $current_node.
node = yes ?
[ question, [animal], $current_node ] :
[ question, $current_node, [animal] ]
if $parent_node == nil
$qtree = node
else
$parent_node[$parent_branch] = node
end
end

main

Markus Koenig

1/16/2005 4:05:00 PM

0

Hello!

Here is my solution. It holds its whole database in a tree of arrays. A
question node is a three-element array: [question, yes_tree, no_tree].
A leaf node is an array containing a single string.

It saves its data into ~/.animal-quiz using Array#inspect and reads it
using eval (to be simplistic).

It is located at <http://www.stber-koenig.de/ruby..., along with
some other solutions for which I hadn't got time to submit.

Have a nice day!

James Gray

1/16/2005 4:59:00 PM

0

My solution.

James Edward Gray II

#!/usr/bin/env ruby

require "yaml"

class AnimalTree
def initialize( question, yes = nil, no = nil )
@question = question
@yes = yes
@no = no
end

attr_reader :yes, :no

def question
if animal?
"Is it #{@question}? (y or n)"
else
"#{@question} (y or n)"
end
end

def learn( question, other, yes_or_no )
if yes_or_no =~ /^\s*y/i
@yes = AnimalTree.new(other)
@no = AnimalTree.new(@question)
else
@yes = AnimalTree.new(@question)
@no = AnimalTree.new(other)
end
@question = question
end

def animal?
@yes.nil? and @no.nil?
end

def to_s
@question
end
end

### Load Animals ###

if test(?e, "animals.yaml")
animals = File.open("animals.yaml") { |f| YAML.load(f) }
else
animals = AnimalTree.new("an elephant")
end

### Interface ###

puts "Think of an animal..."
sleep 3
quiz = animals
loop do
puts quiz.question
response = $stdin.gets.chomp
if quiz.animal?
if response =~ /^\s*y/i
puts "I win. Pretty smart, aren't I?"
else
puts "You win. Help me learn from my mistake before you go..."
puts "What animal were you thinking of?"
other = $stdin.gets.chomp
puts "Give me a question to distinguish #{other} from #{quiz}."
question = $stdin.gets.chomp
puts "For #{other}, what is the answer to your question? (y or n)"
answer = $stdin.gets.chomp
puts "Thanks."
quiz.learn(question, other, answer)
end
puts "Play again? (y or n)"
response = $stdin.gets.chomp
if response =~ /^\s*y/i
puts "Think of an animal..."
sleep 3
quiz = animals
else
break
end
else
if response =~ /^\s*y/i
quiz = quiz.yes
else
quiz = quiz.no
end
end
end

### Save Animals ###

File.open("animals.yaml", "w") { |f| YAML.dump(animals, f) }



Kero van Gelder

1/16/2005 7:16:00 PM

0

> by Jim Weirich
>
> Here's a program I've had a lot of fun with and might make a good Ruby
> Quiz entry. The program is a animal quiz program.

The first thing I thought was "Knowledge Representation" (expert systems
and the like). But then, for new animals you will have unanswered older
questions, and for old animals you will most definitely not have answers
to new questions.

So you could ask answers to the player for those as well, but that's
getting boring rather soon. If you had the info, you could start with the
question that best splits the collection of animals in half; rinse and
repeat. Hopefully, you wouldn't have to ask all questions, then.

But I don't know how to handle unknown answers for this. Anyone?

Put my solution on my webpages (very primitive for now).
http://chmeee.dyndns.org/~kero/ruby/quiz/...
Solution attached at the bottom if you can't read it from there.
Funny stuff is in the querying; I know Animal#to_s is rather
incomplete. Pointers welcome.

+--- Kero ----------------------- kero@chello@nl ---+
| all the meaningless and empty words I spoke |
| Promises -- The Cranberries |
+--- M38c --- http://httpd.chello.nl/k... ---+

Animal = Struct.new(:name, :answers)
TreeNode = Struct.new(:question, :yes, :no) # left/right has no meaning
tree = Animal.new("cat", {})

class Animal
def to_s()
use_an = ["a", "e", "i", "o"].include? name[0,1]
"#{use_an ? "an" : "a"} #{name}"
end
end

def query(str)
STDOUT.write "#{str}? "; STDOUT.flush
gets
end

def boolean_query(str)
begin
STDOUT.write "#{str}? (y/n) "; STDOUT.flush
case gets
when /^y/i; true
when /^n/i; false
else raise "ugh" # an exception feels over the top...
end
rescue
puts "please answer with 'y' or 'n'."
retry # ...but the keyword "retry" feels very appropriate.
end
end

loop {
puts "You think of an animal..."
prev, branch = nil, tree
answers = {}
while branch.kind_of? TreeNode
ans = boolean_query branch.question
answers[branch.question] = ans
prev = branch
branch = ans ? branch.yes : branch.no
end
if boolean_query "Is it #{branch}"
puts "I win! Ain't I smart? :P"
else
puts "I give up. You win!"
target = query "What animal were you thinking of"
target = Animal.new(target.chomp, answers)
puts "I want to learn from my mistake. Please give me"
question = query "a question that distinguishes #{target} from #{branch}"
question.chomp!
question.capitalize!
question.slice!(-1) if question[-1,1] == "?"
answer = boolean_query "What is the answer to '#{question}?' for #{target}"
target.answers[question] = answer
pair = (answer ? [target, branch] : [branch, target])
new_node = TreeNode.new(question, *pair)
if prev
if prev.yes == branch
prev.yes = new_node
else
prev.no = new_node
end
else
tree = new_node
end
end

ans = boolean_query "Do you want to play again"
break if not ans
}

puts "Thanks for playing!"

Lee Marlow

1/17/2005 3:24:00 AM

0

Here's my attempt. It's longer than I would have liked, too many if/else's, but I'm still catching on to Ruby.

-Lee

Dick Davies

1/17/2005 11:57:00 PM

0

* Ruby Quiz <james@grayproductions.net> [0123 14:23]:

Thanks, had fun with that. Sounded easy at first read but had
me stumped several times last night.

Got a slightly hacky version, but hopefully shortish version.
Largely based on Exception abuse :)

Tricky bit was figuring out the tree updating - knocking together
a quick tree and getting the traversal right helped a lot there.
The updating bit wrote itself after that.

I'm sure there's a more rubyish way of switching the node from
'animal mode' to 'question mode', but it seems to avoid a lot of
book-keeping and linked-list-esque linking and unlinking of references,
so it'll do...

It's fairly short so here you go:

--------------------------------8<-----------------------------------
#!/usr/bin/env ruby

class TreeNode

attr_accessor :yes, :no, :question, :animal

def initialize(animal=nil)
@animal = animal
@question = @no = @yes = nil
end

def walk
begin
return (prompt(question) ? yes.walk : no.walk)
rescue NoMethodError
# yes, no or question was nil. Make a guess.
if ( prompt "I think I am a #{animal}. Am I?")
puts "Yay! Let's start again."
else
update_tree
end
end
end

def update_tree
puts "OK, I give up. What am i?"
new_animal = gets.chomp.intern
puts "Give me a question which is true for #{new_animal} and false for #{animal}"
new_question = gets.chomp
# become a decision branch and connect our forks

@no = TreeNode.new(animal)
@yes = TreeNode.new(new_animal)
@animal = nil
@question = new_question

puts "Duly noted. Let's try again:"
end

def prompt(str)
# no question to ask, so punt
raise NoMethodError unless str
puts "#{str} ( q/Q to quit) :"
response = gets

exit if response =~ /q.*/i
return true if response =~ /y.*/i
false
end

end

top = TreeNode.new(:elephant)
loop { top.walk }
--------------------------------8<-----------------------------------

--
'Good news, everyone! I've taught the toaster to feel love!'
-- Prof. Farnsworth
Rasputin :: Jack of All Trades - Master of Nuns


Jim Weirich

1/18/2005 5:20:00 AM

0

On Friday 14 January 2005 09:23 am, Ruby Quiz wrote:
> It works like this. The program starts by telling the user to think
> of an animal. It then begins asking a series of yes/no questions
> about that animal: does it swim, does it have hair, etc. Eventually,
> it will narrow down the possibilities to a single animal and guess
> that (Is it a mouse?).

For a very impressive version of this game, see http....

--
-- Jim Weirich jim@weirichhouse.org http://onest...
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)


Jim Weirich

1/18/2005 5:23:00 AM

0

On Friday 14 January 2005 09:23 am, Ruby Quiz wrote:
> Here's a program I've had a lot of fun with and might make a good Ruby
> Quiz entry. The program is a animal quiz program.
>
> It works like this. The program starts by telling the user to think
> of an animal. It then begins asking a series of yes/no questions
> about that animal: does it swim, does it have hair, etc. ...

Here's my solution ...

#-- animals.rb ----------------------------------------------
#!/usr/bin/env ruby

require 'yaml'
require 'ui'

def ui
$ui ||= ConsoleUi.new
end

class Question
def initialize(question, yes, no)
@question = question
@yes = yes
@no = no
@question << "?" unless @question =~ /\?$/
@question.sub!(/^([a-z])/) { $1.upcase }
end

def walk
if ui.ask_if @question
@yes = @yes.walk
else
@no = @no.walk
end
self
end
end

class Animal
attr_reader :name
def initialize(name)
@name = name
end

def walk
if ui.ask_if "Is it #{an name}?"
ui.say "Yea! I win!\n\n"
self
else
ui.say "Rats, I lose"
ui.say "Help me play better next time."
new_animal = ui.ask "What animal were you thinking of?"
question = ui.ask "Give me a question " +
"to distinguish a #{an name} from #{an new_animal}."
response = ui.ask_if "For #{an new_animal}, the answer to your question
would be?"
ui.say "Thank you\n\n"
if response
Question.new(question, Animal.new(new_animal), self)
else
Question.new(question, self, Animal.new(new_animal))
end
end
end

def an(animal)
((animal =~ /^[aeiouy]/) ? "an " : "a ") + animal
end
end

if File.exist? "animals.yaml"
current = open("animals.yaml") { |f| YAML.load(f.read) }
else
current = Animal.new("mouse")
end

loop do
current = current.walk
break unless ui.ask_if "Play again?"
ui.say "\n\n"
end

open("animals.yaml", "w") do |f| f.puts current.to_yaml end
# END --------------------------------------------------------

The above code depends upon a very simple UI module:

#-- ui.rb ---------------------------------------------------------
#!/usr/bin/env ruby

class ConsoleUi
def ask(prompt)
print prompt + " "
answer = gets
answer ? answer.chomp : nil
end

def ask_if(prompt)
answer = ask(prompt)
answer =~ /^\s*[Yy]/
end

def say(*msg)
puts msg
end
end
# END -----------------------------------------------------------

--
-- Jim Weirich jim@weirichhouse.org http://onest...
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)


why the lucky stiff

1/18/2005 5:38:00 AM

0

Jim Weirich wrote:

>On Friday 14 January 2005 09:23 am, Ruby Quiz wrote:
>
>
>>Here's a program I've had a lot of fun with and might make a good Ruby
>>Quiz entry. The program is a animal quiz program.
>>
>>It works like this. The program starts by telling the user to think
>>of an animal. It then begins asking a series of yes/no questions
>>about that animal: does it swim, does it have hair, etc. ...
>>
>>
>
>Here's my solution ...
>
>#-- animals.rb ----------------------------------------------
>#!/usr/bin/env ruby
>
>require 'yaml'
>require 'ui'
>
>
These solutions are reminding me a lot of the install script that's used
with Hobix. The script basically reads a YAML document that describes
the installation flow and contains the Hobix distribution.

Script: <http://go.hobix.co...
YAML: <http://go.hobix.com/0.3/hobix-instal...

It's such a great quiz, though, because we could use the exercise of
improving our app's interaction with users.

_why



James Gray

1/18/2005 2:42:00 PM

0

On Jan 17, 2005, at 11:19 PM, Jim Weirich wrote:

> On Friday 14 January 2005 09:23 am, Ruby Quiz wrote:
>> It works like this. The program starts by telling the user to think
>> of an animal. It then begins asking a series of yes/no questions
>> about that animal: does it swim, does it have hair, etc. Eventually,
>> it will narrow down the possibilities to a single animal and guess
>> that (Is it a mouse?).
>
> For a very impressive version of this game, see http....

I really like how that one lists all the questions and their answers as
it works.

James Edward Gray II