## Ruby

*As it stands, this is not a valid answer, as it accesses the word list. I'm currently working on a version that doesn't do so, but it might take a while.*

A joint submission from user [PragTob](http://codegolf.stackexchange.com/users/20217/pragtob) and myself.

<!-- language: lang-rb -->

    MAX_TURNS = 6
    
    words_by_length = File.read('wordlist.txt').split.group_by &:length
    
    while !(input=gets.chomp)['END']
      current_turns = MAX_TURNS
      won = false
      fitting_words = words_by_length[input.length]
      characters_used = '_'
      while (current_turns > 0) && !won
        pattern_regex = Regexp.new(input.gsub('_', "[^#{characters_used}]"))
        fitting_words = fitting_words.select do |word|
          pattern_regex.match word
        end
    
        char_count = Hash.new 0
        fitting_words.each do |word|
          word.chars.uniq.each {|c|
            char_count[c] += 1
          }
        end
    
        chars = char_count.keys.reject {|c|
          /[#{characters_used}]/.match c
        }.sort_by {|c|-char_count[c]}
    
        characters_used << chars[0]
        puts chars[0]
        $stdout.flush
        old_input     = input
        input         = gets.chomp
    
        current_turns -= 1 if input == old_input
        won = !input[?_]
      end
    
      if won
        words_by_length[input.length].delete input
      end
    end

Currently this takes about 13 seconds on my machine, but there is certainly room for some speed optimisation.

It averages a score of about 3940 and total error of 6050.

I say "averages", because the algorithm depends on the order of the words used.

This is roughly how it works:

- for each guess, we figure out all possible words from the given list, count in how many of them each character occurs and guess the most common one
- here is how we determine "all possible words":

    - we start with all words on word list
    - every time we guess a word correctly, we delete it
    - at each turn we also remove all words that do not fit the input pattern
    - finally we remove all words where "blanks" in the input pattern correspond to characters we already guessed

It's getting late over here and that's currently the best method we can think of. We might be able to make minor improvements by including logic to throw out missed words as well (say we missed three words that all read `_o_` at the end, and there are three words like this left in our list, we could throw those out as well). But it seems that this might take at least as much code as we already have, and the improvements may or may not be negligible.