-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 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. 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 Suggestions?: http://rubyquiz.strd6.com/suggestions -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ## IRC Teams (#221) Tashi dele Rubyists, This week's quiz was suggested by [Martin DeMello][1] A common feature in IRC-based games is the ability to create and join teams. The game bot sits in the channel and watches for messages in a certain format, which it interprets as commands. Write a small gamebot that accepts the following commands: create team <teamname> join <teamname> leave team show teams show team <teamname> show my team delete team <teamname> reset Here's a typical transcript <foo> create team ruby <bot> - created team ruby - <bar> create team python <bot> - created team python - <foo> show teams <bot> teams: ruby, python <foo> show team ruby <bot> team ruby: foo <bar> show my team <bot> bar is in team python <baz> join ruby <bot> baz is now in team ruby <bar> show team ruby <bot> team ruby: foo, baz Think up some useful extensions, such as the ability to join a user rather than a team, and restricting some commands like reset to ops. There are plenty of IRC bot libraries out there, go explore! If one already does team formation, that's cheating, though :) Have fun! [1]: http://zem.novylen.net
on 16.10.2009 18:34
on 19.10.2009 13:44
The main idea behind this quiz was to show how easy it is to get an irc bot up and running in ruby, especially given the number of bot frameworks out there. I chose to base my bot on rif [http://gitorious.org/ruby-irc], a nice lightweight library with everything I needed to get started immediately. The code consists of a Teams class, which does all the actual work of maintaining teams, and a TeamBot, which inherits from RIF::Bot and handles the IRC part of it. For the sake of simplicity, the bot frontend does no real validation; it just unpacks an incoming message into a command and arguments, and blindly sends those arguments to the Teams object. All public methods on the Teams object accept a player name as a first argument, whether they need it or not, and return either a string or an array of strings, or raise an exception. The bot sends the return value to the channel; an array is sent one message at a time. ------------------------------------------------------------------------------------- gem 'rif' require 'rif/bot' class TeambotError < Exception end def TeambotError(foo) TeambotError.new(foo) end class Teams attr_accessor :teams, :members def initialize @teams = {} @members = {} end def create(player, team) raise TeambotError("team #{team} already created") if teams[team] teams[team] = true "created team #{team}" end def delete(player, team) teams.delete(team) members.delete_if {|k,v| v == team} "deleted team #{team}" end def join(player, team) raise TeambotError("no such team: #{team}") if not teams[team] members[player] = team "#{player} has joined team #{team}" end def leave(player, *args) team = members.delete(player) "#{player} has left team #{team}" end def reset(*args) @members = {} @teams = {} "deleted all teams" end def show(player, *args) if args[0] == 'my' members[player] elsif args[0] == 'teams' teams.map {|team, _| "#{team}: #{show_players(team)}"} elsif args[0] == 'team' show_players(team) end end private def players(team) members.select {|k,v| v == team}.keys end def show_players(team) players(team).join(" ") end end class TeamBot < RIF::Bot attr_reader :teams, :channel def initialize(channel, nick, server, port, username) @teams = Teams.new @channel = channel super(nick, server, port, username) end def on_endofmotd(event) join(channel) end def on_message(event) return unless event.channel == channel msg, *args = event.message.split(" ") player = event.nick begin *ret = teams.send(msg, player, *args) rescue NameError ret = nil rescue TeambotError => e ret = [e.message] end ret.each {|m| send_message(channel, m)} if ret end end if __FILE__ == $0 channel = "##{ARGV[0]}" bot = TeamBot.new(channel, "teambot", "irc.freenode.net", 6667, "RIF Bot") bot.connect end martin
on 01.11.2009 00:42
This week's quiz was solved by Martin DeMello.
Martin uses the [rif gem][1] to handle the nitty gritty details of
IRC, like establishing a connection and sending and receiving
messages.
The `TeamBot#on_message` method receives the messages from the users
and dispatches them to an instance of the Teams class. This is
accomplished by simply splitting the the message that was sent and
using the first part as the method to invoke. The first argument is
always the player, which is taken from the message event's `nick`
field. The rest of the message, if any, is passed as the remaining
arguments.
Let's look at a full pass through of the following sample message from
user Bob:
<Bob> create ruby
This will land in the bot's `on_message` method. The `event` will have
"Bob" as the nick and "create ruby" as the message. That is parsed
into the method to call and the arguments like so:
msg, *args = event.message.split(" ")
The `teams` object is then sent the parsed message arguments with the
specified method as follows:
*ret = teams.send(msg, player, *args)
This takes us into the create method and the team is created (if all
goes according to plan):
def create(player, team)
raise TeambotError("team #{team} already created") if teams[team]
teams[team] = true
"created team #{team}"
end
The other actions that users may wish to perform are taken care of in
the same way. This simple and flexible solution allows for easy
modification and extension.
Thank you Martin for your solution (and also for the quiz suggestion)!
[IRC Teams (#221) - Solutions][2]
[1]: http://gitorious.org/ruby-irc
[2]: http://rubyquiz.strd6.com/quizzes/221.tar.gz