From: David Tran Date: 2005-01-21T07:13:13+09:00 Subject: Re: [SOLUTION] Ruby Quiz #15 Animal Quiz Just found a bug, animals[animal] += asked_question should be db_animals[animal] += asked_question ... etc Resend the correct one: =begin My second solution. Most solutions do a tree walk. Kids will get boring soon, because it always ask the questions in the same order. No fun at all... Here I try to do "ask question in random order". ( ==> Not good to quick find the answer. ) Random select "possible" question. If we try to count the "weight" of the possible questions, and select the "heaviest" one, we end up like tree walk order. Except, if there are equal-heavy, example: Q1 Q2 / \ ==> / \ Q2 Q2 Q1 Q1 Note: You could change the program to take the average weigth question instead of random select. Since we random ask "possible" questions, that may help to get more information about existing knowledge animal, example: Q1 ==> Q1 / \ / \ Q2 c Q2 Q2 / \ / \ \ a b a b Q3 / \ c d ==> this may happend ask Q2 first, and finally distinct c,d by Q3. A little explanation about my data structure: * db_questions: array to store questions. (index 0 no use) * db_animals: hash; key == animals, value == array of questions, the absolute value map to db_questions's index; and positive for 'Yes' answer and negative for 'No' answer. =end require 'yaml' ANIMALS_FILE = 'animals.yaml' QUESTIONS_FILE = 'questions.yaml' # reuse Jim Weirich ConsoleUi class and modified class ConsoleUi def ask(prompt) print prompt + "\n" answer = gets answer ? answer.chomp : nil end def ask_if(prompt) answer = ask(prompt + " (y or n)") answer =~ /^\s*[Yy]/ end def say(*msg) puts msg end end def ui $ui ||= ConsoleUi.new end def get_possible_questions(animals, asked_questions) questions = [] animals.each_value do |qs| qs.each do |q| q = q.abs; if !questions.include?(q) && !asked_questions.include?(q) && !asked_questions.include?(-q) questions << q end end end questions end def filter_animals(animals, question) animals.each do |animal, questions| animals.delete(animal) if questions.include? question end end db_animals = File.exist?(ANIMALS_FILE) ? YAML.load_file(ANIMALS_FILE) : { 'an elephant' => [] } db_questions = File.exist?(QUESTIONS_FILE) ? YAML.load_file(QUESTIONS_FILE) : [ '' ] loop do asked_questions = [] animals = db_animals.dup ui.say "Think of an animal..." while animals.size > 1 qs = get_possible_questions(animals, asked_questions) q = qs[rand(qs.size)] q = -q unless ui.ask_if db_questions[q] asked_questions << q filter_animals(animals, -q) end animal = animals.keys[0] if ui.ask_if "Is it #{animal}?" ui.say "I win!" # update knowledge, we may have more infomation # about the animal, since we random asked questions db_animals[animal] += asked_questions db_animals[animal].uniq! else ui.say "You win. 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 " + "#{animal} from #{new_animal}." response = ui.ask_if "For #{new_animal}, " + "what is the answer to your question?" ui.say "Thanks." if db_animals.key?(new_animal) ui.say "Hey! You are cheating, accroding asked questions," + "it cannot be #{new_animal}." # ... end q = db_questions.index(question) if q if asked_questions.include?(q) || asked_questions.include?(-q) ui.say "Hey! That question already asked! You try to confuse me." # ... end else db_questions << question q = db_questions.size - 1 end db_animals[animal] += asked_questions db_animals[animal] << (response ? -q : q) db_animals[animal].uniq! db_animals[new_animal] = asked_questions.dup db_animals[new_animal] << (response ? q : -q) end break unless ui.ask_if "Play again?" ui.say "\n\n" end open(ANIMALS_FILE, 'w') { |f| f.puts db_animals.to_yaml } open(QUESTIONS_FILE, 'w') { |f| f.puts db_questions.to_yaml } -- David Tran http://www.doublegifts.com