[QUIZ] Animal Quiz (#15)

Discussion in 'Ruby' started by Ruby Quiz, Jan 14, 2005.

  1. Ruby Quiz

    Ruby Quiz Guest

    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/ruby_quiz/

    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 ]
    Ruby Quiz, Jan 14, 2005
    #1
    1. Advertising

  2. Ruby Quiz

    Glenn Parker Guest

    Re: [SOLUTION] Animal Quiz (#15)

    --------------060403060801050107090001
    Content-Type: text/plain; charset=ISO-8859-1; format=flowed
    Content-Transfer-Encoding: 7bit

    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.tetrafoil.com/>

    --------------060403060801050107090001
    Content-Type: text/plain;
    name="animal.rb"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: inline;
    filename="animal.rb"

    #!/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

    --------------060403060801050107090001--
    Glenn Parker, Jan 16, 2005
    #2
    1. Advertising

  3. [SOLUTION] Animal Quiz (#15) (was: [QUIZ] Animal Quiz (#15))

    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-quiz/>, along with
    some other solutions for which I hadn't got time to submit.

    Have a nice day!
    Markus Koenig, Jan 16, 2005
    #3
  4. Re: [SOLUTION] Animal Quiz (#15)

    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) }
    James Edward Gray II, Jan 16, 2005
    #4
  5. Ruby Quiz

    Kero Guest

    > 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/index.html
    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.vangelder ---+

    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!"
    Kero, Jan 16, 2005
    #5
  6. Ruby Quiz

    Lee Marlow Guest

    Re: [SOLUTION] Animal Quiz (#15)

    ------=_NextPart_000_01B5_01C4FC09.5579B000
    Content-Type: text/plain;
    charset="US-ASCII"
    Content-Transfer-Encoding: 7bit

    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

    ------=_NextPart_000_01B5_01C4FC09.5579B000
    Content-Type: application/octet-stream;
    name="quiz15.rb"
    Content-Transfer-Encoding: 7bit
    Content-Disposition: attachment;
    filename="quiz15.rb"

    $stdout.sync = true

    class Question
    attr_accessor :parent
    attr_reader :q
    attr_accessor :yes
    attr_accessor :no
    attr_reader :answer

    def initialize(question, answer = nil)
    @q = question
    @answer = answer
    end
    end

    class Quiz
    def initialize(quiz_type, first_guess)
    @type = quiz_type
    @root = Question.new("Is it #{first_guess}?", first_guess)
    end

    def a_an(word)
    /^[^aeiou]/i =~ word ? "a" : "an"
    end

    def ask(prompt)
    puts prompt
    gets.chomp
    end

    def ask?(prompt)
    /^y/i =~ ask("#{prompt} (y or n)")
    end

    def add_question(failed_question)
    new_ans = ask("What #{@type} were you thinking of?")
    new_ques = ask("Give me a question to distinguish #{new_ans} from #{failed_question.answer}.")
    differentiator = Question.new(new_ques)
    if ask?("For #{new_ans}, what is the answer to your question?")
    differentiator.yes = Question.new("Is it #{new_ans}?", new_ans)
    differentiator.no = failed_question
    else
    differentiator.no = Question.new("Is it #{new_ans}?", new_ans)
    differentiator.yes = failed_question
    end
    parent_question = failed_question.parent
    if parent_question
    differentiator.parent = parent_question
    if parent_question.yes == failed_question
    parent_question.yes = differentiator
    else
    parent_question.no = differentiator
    end
    else
    @root = differentiator
    end
    differentiator.yes.parent = differentiator.no.parent = differentiator
    puts "Thanks"
    end

    def play
    playing = true
    while playing
    puts "Think of #{a_an(@type)} #{@type}..."
    question = @root
    win = false
    continue = true
    while continue
    if ask?(question.q)
    if question.answer
    win = true
    continue = false
    else
    question = question.yes
    end
    else
    if question.no
    question = question.no
    else
    continue = false
    end
    end
    end
    if win
    puts "I win. Pretty smart, ain't I?"
    else
    puts "You win. Help me learn from my mistake before you go..."
    add_question(question)
    end
    playing = ask?("Play again?")
    end
    end
    end

    quiz = Quiz.new("animal", "an elephant")
    #quiz = Quiz.new("food", "Is it an apple?")
    quiz.play

    ------=_NextPart_000_01B5_01C4FC09.5579B000--
    Lee Marlow, Jan 17, 2005
    #6
  7. Ruby Quiz

    Dick Davies Guest

    Re: [Solution] Animal Quiz (#15)

    * Ruby Quiz <> [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
    Dick Davies, Jan 17, 2005
    #7
  8. On Jan 17, 2005, at 7:44 PM, Vance A Heron wrote:

    > Both my kids love playing it, although the first time
    > it asked my 13 y.o. "what is your animal", she said
    > she didn't want to tell ... it was a secret :)


    That's priceless! :D

    James Edward Gray II
    James Edward Gray II, Jan 18, 2005
    #8
  9. Ruby Quiz

    Jim Weirich Guest

    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://20q.com.

    --
    -- Jim Weirich http://onestepback.org
    -----------------------------------------------------------------
    "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, Jan 18, 2005
    #9
  10. Ruby Quiz

    Jim Weirich Guest

    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 http://onestepback.org
    -----------------------------------------------------------------
    "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, Jan 18, 2005
    #10
  11. 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.com/0.3/>
    YAML: <http://go.hobix.com/0.3/hobix-install.yaml>

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

    _why
    why the lucky stiff, Jan 18, 2005
    #11
  12. 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://20q.com.


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

    James Edward Gray II
    James Edward Gray II, Jan 18, 2005
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steve Green

    is there such an animal

    Steve Green, Mar 24, 2005, in forum: Java
    Replies:
    8
    Views:
    394
    Joona I Palaste
    Mar 25, 2005
  2. cyber science
    Replies:
    0
    Views:
    281
    cyber science
    Sep 1, 2009
  3. David Tran
    Replies:
    9
    Views:
    187
    David Tran
    Jan 21, 2005
  4. Ruby Quiz

    [SUMMARY] Animal Quiz (#15)

    Ruby Quiz, Jan 20, 2005, in forum: Ruby
    Replies:
    0
    Views:
    138
    Ruby Quiz
    Jan 20, 2005
  5. Ruby Quiz

    [QUIZ] 1-800-THE-QUIZ (#20)

    Ruby Quiz, Feb 18, 2005, in forum: Ruby
    Replies:
    15
    Views:
    325
    gabriele renzi
    Feb 24, 2005
Loading...

Share This Page