From: Thorsten Hater Date: 2009-06-09T23:34:08+09:00 Subject: Re: [QUIZ][Solution] Twitter Personalities (#208) Hi, here my implementation of a Markov chain based text generator for Twitter. class MarkovText def initialize file @word_map = {} do_preprocessing file end def do_preprocessing file text = File.readlines(file).collect{|l| l.chomp}.join text.gsub! /([a-zA-Z0-9])([,:.!?;])/, '\1 \2' text.squeeze! ' ' words = text.split ' ' for i in (0..words.size-2) # if we encounter this word first initialize with empty Hash @word_map[words[i]] ||= {} @word_map[words[i]][words[i+1]] ||= 0.0 @word_map[words[i]][words[i+1]] += 1.0 end # generate prob distribution for text @word_map.keys.each do |k| sum = @word_map[k].values.inject(&:+) @word_map[k].keys.each{|l| @word_map[k][l] /= sum} end end def generate start_word = nil text = [start_word || @word_map.keys[rand(@word_map.keys.size-1)] ] while !(/[.!?:;]/ === text.last) do w = choose_next(text.last) break if !w text << w end text end def choose_next word roll = rand cand = @word_map[word] return nil if !cand i = 0 while roll > cand.values[i] roll -= cand.values[i] i += 1 end cand.keys[i] end end And the inteface code which maintains a collection of text generators and does interfacing with the output (stdout at the moment) and restriction to 140 chars etc: # load Markov chain text generator require 'markov_text' class TwPerson def initialize min, max, *files @max = max @min = min @name = "Mark V. Shaney" @pwd = "markovianSplitPersonality" @gens = [] files.each do |f| @gens << MarkovText.new(f) end @pool = [] generate_text end def run loop do make_post time = rand(@max-@min) + @min puts "> Made my post, sleeping for #{time}s" sleep(time) end end def make_post puts "> Trying to post" text = "" while (text.length + @pool.last.length) < 139 text += " " + get_next end text.squeeze! ' ' text.gsub!(/( )([.,;:?!])/) do |m| m[1] end do_post text end def generate_text puts "> Generating new text pool" m = @gens.shift @pool += m.generate @gens << m end def do_post txt puts txt end def get_next generate_text if @pool.size < 2 @pool.shift end end This solution is quick and dirty and does not claim to be either elegant or free from errors ;) Thorsten Daniel Moore wrote: > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- > > The three rules of Ruby Quiz: > > 1. Please do not post any solutions or spoiler discussion for this > quiz until 48 hours have elapsed from the time this message was > sent. > > 2. Support Ruby Quiz by submitting ideas and responses > as often as you can! > Visit: http://rubyquiz.strd6.com/suggestions > > 3. Enjoy! > > Suggestion: A [QUIZ] in the subject of emails about the problem > helps everyone on Ruby Talk follow the discussion. Please reply to > the original quiz message, if you can. > > RSS Feed: http://rubyquiz.strd6.com/quizzes.rss > > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- > > ## Twitter Personalities (#208) > > Merhaba Rubyists, > > This week's quiz is to create a program that will generate messages > 140 characters in length. There primary use will be to create a > Twitter "personality". At the end of the quiz period these > "personalities" will be unleashed on the internet and we'll see how > they do in the wild. > > The programs will consist of two parts: a component for interacting > with Twitter, and a top secret "personality" module. > > For the Twitter interface component there will be no no-spoiler > period. Please feel encouraged to discuss different libraries or > methods on the mailing list. Let's all work together to find the best > interface. > > The "personality" component can take any inputs and will produce a 140 > character message when called. The "personality" may remember state. > The no-spoiler period applies for the "personality" component; please > save them until everyone has had a chance to consider their own > implementations. > > > Have Fun! >