blogstrapping

A Droll Little Program

I have written a number of kludgey little dice roller programs over the years, mostly for the purpose of satisfying my desire to make my enthusiasms for roleplaying game geekery and computer geekery. If I had to guess, I would say that I have probably written eight or ten of them, in three different languages.

At least half of them have been written in Ruby. Mostly, these programs have had very simplistic functionality, such as one that would take any die code that consisted of a(n optional) number, followed by a case-insensitive letter D, followed by another (mandatory) number. With that, then, it would check the last number against a set of predefined die types and, if the correct type existed, it would produce a pseudorandom (in the programming sense of the term) result. Its output was formatted using printf, and it showed only the die code and the end result total.

I hacked that together, in its basic form, probably in about five minutes. I fiddled with it another half hour in total without really adding any particular interesting functionality. I used it for months -- maybe as much as a year. It is, however, basically a crap program, unworthy of redistribution for others to use.

I started using IRC to play online RPGs with friends in other states more than a year ago. In that group, we all trust each other, so we just rolled dice locally and posted our results in the channel. I used my simplistic dice rolling program most of the time so I would not have to take my lazy hands off the keyboard to roll physical plastic polyhedrons. It worked fine.

A couple months or so ago, particularly for a different group that was starting to play RPGs in IRC, I went searching for an existing dice roller IRC bot that would serve my needs. This group's needs for a dice rolling IRC bot were not as sophisticated as for the other group, because we were not using special die types outside the usual run of eight standard Dungeons and Dragons dice, so it was relatively easy to find a dice roller IRC bot that would work for us.

I specifically narrowed my search down to bots written in Ruby because I wanted something that would be easy and fun to hack, and was delighted to find something called Bones that actually seems to be among the better-known IRC dicebots on the Web. Using the Google search string irc bot dice roller, it shows up for me as the third result, and the text from the Google hit says it's open source! The number one and two results are for the same set of bots available from the same place (that is, each from the same place as the other, not the same place as Bones), with a somewhat obtuse syntax and somewhat game-specific functionality. Worse, it's specifically designed for the mIRC client, which is simply unacceptable for my purposes. Using the search string irc dicebot, Bones comes in first (and second).

I followed the link and read the page announcing the creation and release of Bones. On that page, in the list of features, I saw this bullet point:

Well, Bones it is, then! I looked around briefly for the open source license used for Bones, but did not find it right away. This is a lamentably common problem with open source software, I find; once the developers have told you it is open source, they then assume you do not want to be bothered with details of licensing unless and until you hack the source code to add features or eliminate bugs and want to contribute your changes back to them, so it never occurs to anyone to make the licenses very easily found. Of course, that's pretty annoying for people who care about licensing -- like me. I figured I'd get back to finding the license later, and would just try it out for now.

It turned out to be suitable for my needs at that time, so we used it. I ran the bot script on my laptop while playing RPGs in IRC with the group using pretty standard rules for its games, with no special die type requirements or exploding dice. We used it for a little while.

I started thinking about the idea of extending its functionality and possibly using it for the other group, which does need some of those fancy features for a passel of house rules applied to the Pathfinder RPG system. Before hacking the code, though, I figured I should find out what license is used for this program. I hunted around -- in vain. There was no licensing information anywhere in the code, bundled with it, or anywhere that I found on the site(s) run by the guy who wrote the program.

I contacted the author and asked about it. I found out, through a short email exchange, that he has no idea what "open source" means to people who actually write and/or use open source software. He apparently heard that term used as a buzzword somewhere and decided to use it without ever checking to see if there are any conventions for its use. To him, apparently "this code runs in an interpreter" means it's "open source", whereas the Open Source Initiative (which has the opensource.org domain and whose founders actually coined the term Open Source) defines the term somewhat differently. Check out the Open Source Definition page at the OSI site for details.

Really, what is needed to reasonably call something open source is a legal guarantee that you won't sue someone for using, modifying, and redistributing your source code -- even commercially if you like. That's the short version of the requirements for calling something "open source". That legal guarantee comes in the form of a license. What does the Bones author do? He calls his program "open source" because you can physically see the source if you want to, change it on your local computer, run the changed version, and offer patches to him. Woe betide anyone who shares modified (or even unmodified) copies of the program with anyone else, uses it as the back end of a Website, or otherwise does anything with it that might involve third parties in some way not foreseen by the copyright holder. If you do that, you have no guarantee you won't get legal threats in your email. I asked about licensing, and he said he didn't plan to license it.

Well, screw that.

I decided it was time to write my own IRC dicebot. A friend who sometimes uses the online monicker "n8" was eager to get something put together that would serve our needs, with virtual dice numbered 0-5, exploding die rolls, and so on. He wanted me to wrap an IRC bot around my dice roller, but I told him it sucked and needed to be rewritten from scratch first. I finally decided to tackle the rewrite, and in about ten minutes (give or take) had a totally new dice roller program written in Ruby. I used it for myself then, just as I had used the previous dice roller, but did not have an IRC bot yet. I named it "droll", for "die roll".

I started casually, lazily looking into options for Ruby libraries that provide IRC bot frameworks. Then I forgot about it for a little bit. Meanwhile, n8 decided to start hacking Bones, with the idea that he'd get some of our desired functionality built into it just to tide us over until I got my fourth point of contact in gear and actually wrote an IRC dicebot of my own. I think he must have been taking a lazy approach too, because I'm not sure he spent more than five or ten minutes on figuring out how Bones worked and trying to make some changes to it. I finally got inspired by his low level industriousness to do some work on an IRC bot, though, and got to work. It turns out n8 had actually added support for things like exploding 0-5 die rolls to Bones, but tossed it because he thought it was better to work on my program than Bones.

I used the Isaac library for Ruby as the basis of my IRC bot. It provides a DLS framework for writing IRC bots, offering a very clear, simple syntax that nicely abstracts away the heavy lifting. Thanks to Isaac, my dicebot took about five minutes or so to write in its initial, functional form. Of course my ten minute program, droll, was not written with being used as the basis of an IRC bot in mind; it was just written to get a working algorithm for rolling essentially arbitrary die types ranging from either 0 or 1 to some user-specified upper bound number. I needed to rewrite droll so that my IRC bot, named "drollbot", could call it as a library. For the moment, drollbot was actually shelling out to droll -- calling it as a separate program and capturing the results sent to STDOUT, then sending those results (with a little munging) to an IRC channel.

At that point, I kinda stopped paying attention to how much time I put into changes to the programs involved. The end result, though, is that I have a pretty decent dice roller program suite (droll itself works well as a command line program, and drollbot with droll as its back-end die rolling logic library works well as an IRC bot) if I do say so myself. It handles basic exploding die rolls, it has an unreasonably high upper limit for how many times a given roll can explode so the program itself will not explode thanks to resource exhaustion. Malicious input crashing your dicebot could be pretty annoying, depending on how you use it.

As you can easily discover by checking out the README file at the droll BitBucket repository, which also contains the drollbot IRC script, droll has been released under a copyfree and open source (by definition, if not by certification) license -- the Open Works License, which is the same license currently used for content here at blogstrapping, and for the Lump content management system that powers blogstrapping. The Isaac library used in the making of drollbot is released under another copyfree license, the MIT/X11 license.

At the moment, drollbot lacks exactly two features that Bones offers:

  1. It does add together the results of multiple die codes in a single command.

  2. It does not produce the results of multiple die codes separately from a single command.

At least adding together multiple die codes is something I intend to add to the program. I am not entirely certain the other feature is actually necessary or even particularly desirable, but n8 is keen on implementing that functionality, so I will probably end up adding it to the IRC bot. How much the underlying library needs to be modified to accomodate this stuff remains to be seen.

Other features are also under consideration and/or development, as you can see in droll's issue tracker.

I have two articles pending at TechRepublic that talk about things related to droll and drollbot. One of them discusses the meaning of the term "open source", and uses droll and Bones as example and counterexample; it has been submitted to my editor for the Open Source column at TR. The other talks about writing IRC bots in Ruby with Isaac, mentions drollbot in that context, and offers code examples for using Isaac to produce (and test) a simple "hello world" type of IRC bot, in TR's Programming column. Neither has been published yet, but I anticipate at least one of them being published for public consumption in a week or so.

For the sake of including some code in this account, and perhaps satisfying the curiosity of those who like to see the code, this is the most-relevant chunk of code from the drollbot.rb file for demonstrating how an IRC bot works with the Isaac library:

on :channel, /^([0-9]*[dx][0-9]+[+-]?[0-9]*)\s*(.*)/ do
  result = roll_dice(match[0])
  result.each do |r|
    output = "#{nick} rolls " + r
    if match[1].length > 0
      output += ' (' + match[1] + ')'
    end
    msg channel, output
  end
end

See the rest at the BitBucket page for the drollbot.rb file if you like. It's pretty simple. The code in droll.rb, used as a library in drollbot, is a little bit less simple -- but still quite simple in the grand scheme of things, I think. Using it looks something like this:

12:33 <@apotheon> 2x05
12:33 < drollbot> apotheon rolls 2x05: [4, 5, 3] + 0 = 12
12:33 <@apotheon> d20+3
12:33 < drollbot> apotheon rolls d20+3: [15] + 3 = 18
12:34 <@apotheon> 2d10+7 With Comments!
12:34 < drollbot> apotheon rolls 2d10+7: [8, 7] + 7 = 22 (With Comments!)

The x in that first die code tells droll(bot) to make dice "explode" when they return a result equal to the highest number possible for the die type -- that is, to add an extra die of the same type every time one of the virtual dice returns the maximum possible value for that die type. The zero at the beginning of the die type, 05, indicates that the range of possible die values starts at zero rather than one.

Pretty simple.

Both droll and drollbot are tested using the MRI and Rubinius implementations of the Ruby language.