From: James Koppel Date: 2007-07-12T06:41:57+09:00 Subject: [QUIZ][SOLUTION] Hangman (#130) --0-1461783126-1184190105=:46326 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: 8bit Sorry it's a little late; I'm on vacation and have spent limited time on a computer with Ruby installed. My solution reads a dictionary stored in "words.txt." It uses this to construct an array of arrays containing the possible words for each word in the pattern, which is constantly updated according to feedback. I thought about making something based on making sure each word has a vowel and hardcoding in the general English language letter frequencies, but I realized that that would be compeltely unnecessary, as I could constantly calculate the true letter frequency for the possible letters. This is highly effective and surprisingly simple, although it could definitely benefit from some basic knowledge of English grammar and parts of speech. Unfortunately, this program is definitely not unbeatqable. Likewise, with only 6 lives, probably none of the others are as well. As expected, when facing the word "cwm," the program guesses AEOIUY ["A" is more frequent than "E" for three-letter words, apparently], and loses before it has a chance to guess correctly. $Words = (file=File.new("words.txt")).read.upcase.split(/\n/) file.close def hangman_start puts "Please enter word pattern." word_pattern = gets.chomp possible_words = [] word_pattern.split.length.times do |t| possible_words << $Words.select{ |word| word_pattern.split[t].length == word.length} end hangman_round word_pattern, possible_words end $avail_letters= ("A".."Z").to_a def hangman_round(word_pattern, possible_words, lives=6) guess(word_pattern, possible_words) puts word_pattern puts "Are there any #{$guess}s?\t\tComputer lives=#{lives}" if gets.chomp=="y" puts "Please indicate all positions with a #{$guess}" puts "(0-indexed, comma-delimited)" gets.chomp.split(/,/).each{|pstr| word_pattern[pstr.to_i] = $guess} possible_words.each_index do |i| possible_words[i] = possible_words[i].select{|word| word.gsub(/[^#{$guess}]/, '_') == word_pattern.split[i].gsub(/[^#{$guess}]/, '_')} end else lives -= 1 possible_words.each {|words| words.reject! {|word| word.index $guess}} end if word_pattern !~ /_/ puts word_pattern puts "I win" elsif lives > 0 hangman_round(word_pattern, possible_words, lives) else puts "You win" end end #Guesses by frequency analysis. If a letter appears in a possible word, it's a vote for #that letter. If a word is possible more than once, that's multiple votes, but not #if the letter appears multiple times in a possible word (it's still one possibility) #It then removes that letter from $avail_letters and stores the guess into $guess #for convenience def guess(word_pattern, possible_words) all_words = possible_words.flatten guess = $avail_letters.sort_by {|c| all_words.select{|w|w.index c}.length}.last $avail_letters -= [guess] $guess = guess end hangman_start --------------------------------- Boardwalk for $500? In 2007? Ha! Play Monopoly Here and Now (it's updated for today's economy) at Yahoo! Games. --0-1461783126-1184190105=:46326--