-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 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!
on 2009-06-05 22:57
on 2009-06-06 02:27
This Quiz sounds fun! I wish I knew more about Ruby than I do now. I would participate in this one, but I am a beginner rubyist :-( Anyhow, I can not wait to see the results. Good luck to everyone participating! JC
on 2009-06-06 06:25
On Jun 5, 2009, at 7:26 PM, Joshua Collins wrote: > This Quiz sounds fun! > > I wish I knew more about Ruby than I do now. I would participate in > this one, but I am a beginner rubyist :-( > > Anyhow, I can not wait to see the results. Good luck to everyone > participating! This is an almost perfect quiz for a beginner! Don't give up! Use it as a chance to stretch your skills. Try this: 1. Build a funny saying in a String 2. Replace some words in that saying with variables that hold the words you removed 3. Adjust each variable so it gets two or three different words that all could fit the saying, selected randomly It's just like reverse Madlibs. You may get more ideas to keep improving it too. It's not a spoiler to get help on the Twitter side, or you can just skip that part completely. Don't be afraid to change the task to suit your skill. If you learn something, you win. You can even ask us questions on the personality side, just be clever in avoiding spoiler material. "Hey guys, I'm building a new navigation system for NASA's next generation rocket and it needs to randomly choose between three directions…" We're here to help. :) "Never give up! Never surrender!" James Edward Gray II
on 2009-06-06 07:22
From: "James Gray" <james@grayproductions.net> > > "Never give up! Never surrender!" http://grabthar.ytmnd.com/ ;D SCNR, Bill
on 2009-06-06 07:30
From: "Daniel Moore" <yahivin@gmail.com> > > 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. So... to clarify, the personality component is not replying to--or seeing--other messages as input, but is merely producing a sequence of 140 character messages determined solely according to its internal black box state? Regards, Bill
on 2009-06-06 09:33
Bill Kelly wrote: > to--or seeing--other messages as input, but is merely > producing a sequence of 140 character messages determined > solely according to its internal black box state? > > > Regards, > > Bill I don't think that is a necessary restriction, as it "can take any inputs." I will interpret that to mean you can feed in whatever you would like - a dictionary or Shakespeare, for example. -Justin
on 2009-06-06 18:44
Joshua, James has some great suggestions and I couldn't have said it better. One of the best ways to learn more about Ruby is to dive in. You may find out that it is easier than you imagine. If you get stuck on a particular issue you can search the web or ask on the mailing list; there are many great resources and lots of people interested in helping out.
on 2009-06-06 18:46
Bill, The personality component can see other messages as input and use those to determine the next message to provide. Of course, in order for the component to see the replies and other messages they would need to be gathered and passed in as arguments in some form. If you like you can share the techniques for gathering these messages with the mailing list. The goal is to make it as easy as possible to focus on the "personality" part without worrying about the nitty-gritty of interfacing with Twitter. Additionally sharing information on available libraries or general techniques will help all the solutions to be better.
on 2009-06-07 02:02
I will give the Quiz a shot. After all, I will surely learn from it :-) Thanks for the encouragement guys!
on 2009-06-08 08:47
So the "personality" is just a random message, perhaps a Clint Eastwood quote or something? Jayanth On Sun, Jun 7, 2009 at 8:42 PM, Sandro Paganotti <sandro.paganotti@gmail.com
on 2009-06-08 12:16
Daniel Moore wrote: > Visit: http://rubyquiz.strd6.com/suggestions > > The programs will consist of two parts: a component for interacting > save them until everyone has had a chance to consider their own > implementations. > > > Have Fun! > Posting to Twitter, I found, was pretty straightforward using the Twitter4r gem, although it only worked for me under Ruby 1.9: require 'twitter' client = Twitter::Client.new :login => "myname", :password => "mypassword" client.status :post, "Working on Ruby Quiz." I hope that helps anyone who might have been hesitant because of the Twitter interface part. -Justin
on 2009-06-08 16:46
On Jun 8, 2009, at 1:46 AM, Srijayanth Sridhar wrote: > So the "personality" is just a random message, perhaps a Clint > Eastwood > quote or something? It is if you want it to be. :) If you would prefer to be trickier, you could try to build intelligent replies to things people say. James Edward Gray II
on 2009-06-08 23:32
Ok, I've just finished mine... it was a really crazy experience... now I'm going to create a twitter account for it and generate some random phrases with different personalities :D
on 2009-06-08 23:41
Sandro Paganotti wrote: > [Note: parts of this message were removed to make it a legal post.] > > Ok, I've just finished mine... it was a really crazy experience... now I'm > going to create a twitter account for it and generate some random phrases > with different personalities :D You mean like "The Policeman's Beard is Half-Constructed"?
on 2009-06-09 00:03
Phlip wrote:
> You mean like "The Policeman's Beard is Half-Constructed"?
Racter ftw!
on 2009-06-09 01:08
Justin Collins wrote: >> as often as you can! >> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- >> >> The no-spoiler period applies for the "personality" component; please > require 'twitter' > client = Twitter::Client.new :login => "myname", :password => > "mypassword" > client.status :post, "Working on Ruby Quiz." > > > I hope that helps anyone who might have been hesitant because of the > Twitter interface part. > > -Justin > So, here is my full solution, which just pulls transcriptions of Dijkstra's notes and tries to extract decent quotes. Also attempts to find relavent replies. Works most of the time. You can see some results at http://twitter.com/ewdbot and get a nicer-looking version from http://gist.github.com/125781 #A little Twitter thing to post random quotes from Edsger Dijkstra require 'yaml' require 'open-uri' require 'twitter' #twitter4r gem require 'hpricot' class DijkstraQuote class << self #Match the website's naming scheme def random_ewd (rand(1289) + 30).to_s.rjust(4, "0") end #Get a random quote def get_quote quote = nil while quote.nil? do quote = fetch random_ewd end quote end #Get a random quote from the specified set of notes def fetch ewd #Check if we've already retrieved this set of notes if File.exists? "ewd#{ewd}" quotes = YAML.load_file "ewd#{ewd}" quotes[rand quotes.length] else $stderr.puts "Fetching EWD#{ewd}..." if $DEBUG #Fetch a transcript of Dijkstra's notes begin file = open("http://www.cs.utexas.edu/~EWD/transcriptions/EWD#{ewd[0,2]}xx/EWD#{ewd}.html") { |f| Hpricot(f) } rescue OpenURI::HTTPError $stderr.puts "Not found" if $DEBUG return nil end text = file.to_plain_text #Pick some sentences that seem good lines = text.split(/\.(?:\s+|\n+)/).map do |l| l.gsub(/\t|\n|\r/, " ").squeeze(" ").strip end.select do |l| l.length > 40 and l.length < 140 and l[0,1] == l[0,1].upcase and l[-3, 3] != "viz" end #Cache them for later File.open "ewd#{ewd}", "w" do |f| YAML.dump lines, f end #Return a random sentence lines[rand lines.length] end end #(Very crudely) tries to find a quote that is related to the message. #Note that this only searches the cache. def find_related message #Get the longer words words = message.gsub(/[^a-zA-Z ]/, "").split.select { |w| w.length > 4 } quote = nil if words.length > 0 word = /#{Regexp.union(words)}/i #Check local files for the search word Dir.glob("ewd*").find do |f| text = File.read f if text =~ word #Get the list of quotes and grab one quotes = YAML.load_file f matches = quotes.grep word quote = matches[rand(matches.length)] else false end end else quote = get_quote end quote end end end #Post to Twitter class DijkstraTwitter def initialize @twitter = Twitter::Client.new :login => "?", :password => "?" end #Post a random quote. If _ask_ is true, asks for approval first def post_random ask = false quote = DijkstraQuote.get_quote if ask and ask_permission("Would you like to post this: \"#{quote}\"") or not ask @twitter.status :post, quote end end #Check unanswered @ewd messages and come up with responses def post_replies ask = false require 'set' #Keep track of what has been replied to already if File.exist? "ewdreplies" replied = YAML.load_file "ewdreplies" else replied = Set.new end replies = @twitter.status(:replies) replies.each do |status| status_id = status.id.to_s next if replied.include? status_id message = status.text user = status.user.screen_name if reply_to message, user, ask replied << status_id end end File.open "ewdreplies", "w" do |f| YAML.dump replied, f end end #Send a reply if we can find one def reply_to message, sender, ask = false puts "Responding to \"#{message}\"" quote = DijkstraQuote.find_related message if not quote puts "Nothing related. Skipping." return false elsif ask and ask_permission("Would you like to post this: \"#{quote}\"") or not ask response = "@#{sender} #{quote}" if response.length > 140 response = response[0,140] end @twitter.status(:post, "@#{sender} #{quote}") true else if ask and ask_permission "Ignore this reply?" true else false end end end #Ask if the user would like to post the quote which was found def ask_permission message puts message response = nil until response =~ /^(y|n)/i print "(Y/N): " response = gets end $1.downcase == "y" end end #Try it out puts DijkstraQuote.get_quote #dt = DijkstraTwitter.new #dt.post_random true #dt.post_replies true My favorite so far: "In this sense, Programming = Mathematics + Murphy's Law" -Justin
on 2009-06-09 01:15
On Jun 5, 2:54 pm, Daniel Moore <yahi...@gmail.com> wrote: > Visit:http://rubyquiz.strd6.com/suggestions > > The programs will consist of two parts: a component for interacting > save them until everyone has had a chance to consider their own > implementations. > > Have Fun! > -- > -Danielhttp://rubyquiz.strd6.com Here's the twitter "personality" I made: http://twitter.com/iloveweather It posts about the weather conditions of a random US city. The phrase list needs some expanding, and there's a little code to clean up before I post it. It was pretty fun, and I'm really enjoying seeing tweets like "Step outside, Bliss. It's nice." and "It's days like this that make people happy to be in Beverly." It doesn't do replies (yet). More to come, Chris
on 2009-06-09 16:34
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
on 2009-06-10 10:14
Here's my solution: http://github.com/sandropaganotti/Twitter-Sapiensit uses Wordnet to create a group of words semantically-related to a starting sentence (its personality); the uses this words to generate a new sentence. Sandro
on 2009-06-10 10:27
Uops. real link: http://github.com/sandropaganotti/Twitter-Sapiens<http://github.com/sandropaganotti/Twitter-Sapiensit> On Wed, Jun 10, 2009 at 8:13 AM, Sandro Paganotti <
on 2009-06-14 21:13
There were two main components to this week's quiz, the interface to
Twitter and the personality that generates messages.
Let's start with the Twitter interfaces.
Justin Collins' @ewdbot uses the Twitter4r gem[1]. The usage is
straightforward. When given the account credentials it creates an
object that provides methods to post status updates and get
information from Twitter.
def initialize
@twitter = Twitter::Client.new :login => "?", :password => "?"
end
...
# Posting a quote
@twitter.status :post, quote
# Getting replies
replies = @twitter.status(:replies)
Sandro Pagonatti's @twsapiens uses the Twitter gem[2].
# Initialize Twitter credentials
base = Twitter::Base.new(Twitter::HTTPAuth.new('twsapiens', <psw
here>))
# Get a random status text from the account's friends timeline
base.friends_timeline.sort{|a,b| rand()<=>rand()}.first.text
One comment about randomizing arrays is that Ruby 1.9.1 provides a
`shuffle` method to `Array`, so now you no longer need to use
`sort_by{ rand }` or the like if you are on 1.9.1.
Both of the gems make connecting to Twitter via HTTP a breeze, though
Twitter4r seems a little bit simpler to use. No matter which one you
choose it shouldn't be more than a couple of lines to connect.
Now that we're all connected to Twitter let's examine some ways to
generate messages to send.
@ewdbot by Justin selects a random Edsger Dijkstra quotation from the
Edsger W. Dijkstra Archive[3]. The quotation is selected by
downloading a random page from the archive, selecting sentences that
meet certain size and regex requirements, then choosing a random
sentence from that list. The trimmed word lists are cached locally to
make future access easier. Justin's solution is well written and
definitely worth examining if you are interested in learning more.
Remember that Sandro was selecting a random status message from
@twsapiens friends timeline? Well that message is used to seed the
personality. Sandro uses the Linguistics gem[4] to create a collection
of words related to the selected message. Those words are then
arranged according to grammar rules from Ola Bini's port[5] of Peter
Norvig's _Paradigms of Artificial Intelligence Programming_. Some of
these sentences come out a little crudely constructed, but it is a
very difficult problem for a program to construct it's own sentences.
This sentence in particular is rather thought provoking:
the character in table on a variation by he by volume with he on a
variation typecast a notebook computer
Thorsten Hater submitted a Markov chain text generator. I tested it
out on some of the EWD quotations and it had some successful results.
It would be interesting to combine the Markov chain text generation
with trending topics in an attempt to create a popularity bot. Let the
mailing list know when your bot hits 1,000,000 followers!
Twitter Personality Accounts:
* @iloveweather - Chris Shea
* @ewdbot - Justin Collins
* @twsapiens - Sandro Pagonatti
Thank you everyone for your great responses to this week's quiz!
[1]: http://twitter4r.rubyforge.org/
[2]: http://twitter.rubyforge.org/
[3]: http://www.cs.utexas.edu/~EWD/transcriptions/transcriptions.html
[4]: http://www.deveiate.org/projects/Linguistics/
[5]:
http://github.com/olabini/paipr/tree/7334d50b3b62924bc7c02effd22be0201658945b/lib/ch02
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.