Screenshot of a procedurally generated barred spiral galaxy from my WIP space adventure game. There’s a fair bit of intricacy behind this image: a log spiral pattern formula, 1100 realistic stellar classes, simple clustering behaviour, etc. Of course, with only 10,000 stars the extent of scientific accuracy is always going to be limited. My renderer can go up to ~100k stars without serious FPS loss thanks to point sprites, but I’m unlikely to be able to maintain that kind of scale alongside depth of content. Anyway, I’m pretty happy with it for now!
safubot: an event-driven chatbot framework for Ruby
So it occurred to me recently that I’ve never actually published a gem, and this is a sorry state to find oneself in if one considers oneself a Ruby developer. I also realised that @zinzobot, my homophobe-bashing spam-reporting stalkee-archiving twitterbot, desperately needed restructuring. The result: safubot, a framework for building chatbots which operate over Twitter and/or XMPP.
I had quite a bit of trouble getting this to behave reliably, mostly due to the apparent lack of any support for running two EventMachine-based clients (namely TweetStream and Blather) in the same process. Ultimately I resorted to forking, which brought its own race-condition and execution-context issues. I’m still not *entirely* satisfied with it; it uses far too much memory for what it does, a common flaw of Ruby daemons. Nevertheless, it does work. It has documentation and everything!
Connection issues with Linode’s Tokyo datacenter and Verizon FiOS
Since this didn’t seem immediately Googleable for me, here’s a screenshot of my support ticket exchange:
Graph-based research browsing: Vyzkumap
Named after the Czech word “výzkum”, for “research”. I like to think mysterious Czech scientists pronounce it with a deep, dramatic intonation, like “Khazad-dûm”.
Vyzkumap is a hybrid search engine frontend and graph visualiser, used for exploring the huge Mendeley database of research papers. The basic browsing is functionally similar to that of the Mendeley website itself, but with a cool spiffy hipstery animated HTML5 pushState graph-viz interface.
I started development of this before Map My Field, but the implementation proved a fair bit more challenging. My original conception was based on hard citations rather than the more vague “related research” API command. However, acquiring the citations soon proved an intractable task (let alone quickly and reliably!). I also wanted to color-code by open access status, which the API presently doesn’t provide a means to bulk-fetch. So I spent a bit of time dithering about the speed-completeness tradeoff, eventually coming to a decent compromise by using journal names instead of individual paper ids. Aggressive caching is my friend.
So those are my two entries for the Binary Battle competition! I’ll be most pleased if either of them get into the top 10; I’m not really expecting to win (though I could certainly use the money). We shall find out shortly!
Geolocalising academia: Map My Field
Because, sadly, real life does not afford me the opportunity to cover the planet in rainbow-coloured star stickers that expand into giant blobs.
Map My Field overlays the locations of Mendeley users in each academic discipline on top of a Google Maps instance. This may approximate the geographic distribution of research activity as a whole– but the usual sampling bias caveats apply. I don’t know how the probability of academics creating a Mendeley account varies by country, but I’d guess language barriers play at least a minor role. As of the time of writing, Mendeley has a total of 90381 users. It’s a decent number but not yet enough to include a truly representative sample in the smaller fields. For example, there is exactly one (Danish!) Queer Studies guy on the map.
From a technical standpoint, location density maps are hardly a novel idea, but the particular implementation details might be of interest. I use a transparent Raphaël canvas positioned over the Google Maps interface to draw the markers, which allows for all the extraneous fancy animations. When a node is clicked, the specific user information for that location is retrieved via AJAX and displayed in a dynamically positioned/resized HTML div to give the illusion of being “inside” the marker. Geocoding is pre-generated by a Rake task using the lovely API Google provides.
This is actually my second entry for the Mendeley API competition. I’m still refining the primary one; expect a post about that soon!
This is what happens when people ignore me!
require 'pony'
require 'base64'
def pick_subject
possibles = ["Even bytes get lonely for a little bit.", "All the lonely users, where do they all come from?", "Life is very long when you're lonely.", "Solitude is the profoundest fact of the human condition.", "Loneliness is a barrier that prevents one from uniting with the inner self.", "Absolute silence leads to sadness. It is the image of death.", "Good humor is the health of the soul, sadness is its poison.", "I don't need to manufacture trauma in my life to be creative.", "In deep sadness there is no place for sentimentality.", "It's the poignancy and sadness in things that gets to me.", "Sadness is also a kind of defence.", "Sadness is but a wall between two gardens.", "The sadness of the incomplete, the sadness that is often Life, but should never be Art.", "The walls we build around us to keep sadness out also keeps out the joy.", "You get used to sadness, growing up in the mountains, I guess.", "The surest cure for vanity is loneliness.", "There is no loneliness greater than the loneliness of a failure.", "When friendship disappears then there is a space left open to that awful loneliness of the outside world.", "What makes loneliness an anguish is not that I have no one to share my burden, but this: I have only my own burden to bear.", "The sky is one whole, the water another; and between those two infinities the soul of man is in loneliness.", "Loneliness is never more cruel than when it is felt in close propinquity with someone who has ceased to communicate.", "Loneliness is the ultimate poverty.", "Music was invented to confirm human loneliness."]
possibles.sample
end
def pick_image_path
Dir.glob('/home/mispy/sadkittens/*').sample
end
def sad_kitten(target)
imgpath = pick_image_path
imgfn = imgpath.split('/')[-1]
body = %{
This is a multi-part message in MIME format.
--multipart_related_boundary
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<img src="cid:sadkitten">
</body>
</html>
--multipart_related_boundary
Content-Type: image/jpeg; name="#{imgfn}"
Content-Transfer-Encoding: base64
Content-ID: <sadkitten>
Content-Disposition: inline; filename="#{imgfn}"
#{Base64.encode64(File.read(imgpath))}
}
Pony.mail(:to => target,
:from => 'Sad Kitten Foundation <sadkitten@mispy.me>',
:subject => pick_subject,
:headers => { 'Content-Type' => 'multipart/related; boundary="multipart_related_boundary"' },
:body => body)
end
if __FILE__ == $0
sad_kitten(ARGV.first)
end
Deviation Collector
I made a little Chrome extension to express my love for CoffeeScript and deviantART! The former is a marvellous language that makes JavaScript more palatable to a Rubyist, while the latter is simply an endless font of cute. My extension taps in to dA’s API to give you a periodically refreshing counter of new deviations produced by users you are watching, and will open these in tabs when clicked.
This project was slightly interesting due to the closed nature of the API, requiring a small amount of reverse-engineering. The Chrome developer tools remain an excellent choice for this– they even have a JS deobfuscator now! Fortunately, the DiFi request structure is fairly straightforward, and some nice hackers had already done much of the work for me. Code for the extension is on Github if you want to make a similar application of your own, but keep in mind that this API isn’t officially documented and deviantART management may not take too kindly to overuse!
Minecraft: Collage World
A silly project I undertook recently: collecting over 3000 individual Minecraft creation schematics and merging them all into a single, playable world. The results are, well… about as crazy as you would expect:
Here is a dynmap birds-eye view:
You can grab a copy of the world here if you want to mess around in it!
Methodology
The key to this project is the PhoenixTerrainMod customisable world generator, which provides a system for integrating player-designed objects into the generation process. I originally sourced 5488 schematic files from a crawl of the mcschematics.com forums. Converting these to the .bo2 format required by PTM was a somewhat arduous task– kudos to the lovely @unnali for rewriting my Ruby script in C and rendering it at least 10x faster! Of the original dataset, I ended up excluding every object with a duplicate filename or over 100kb in size (total of 2368 files) to fit time and memory constraints.
Downloads
5488 raw .schematic files (74.8MB): 5488schematics.tar.bz2
3120 processed .bo2 files (19.7MB): 3120bobs.tar.bz2
Collage world directory (50.5MB): collage.tar.bz2
.schematic -> .bo2 mass converter: sch2bob on Github
Quixotic symbolism of Lisps
I ported the Emacs psychotherapist from Emacs Lisp to Common Lisp just now, as a means of learning more about each language. This one particular discrepancy took an unreasonable amount of time for me to notice:
t
NIL
The cause of this madness? Observe:
foo
ELISP> (intern "FOO")
FOO
|foo|
:INTERNAL
CL-USER> (intern "FOO")
FOO
:INTERNAL
Symbols in Emacs Lisp behave much as strings with regard to case sensitivity, whereas in Common Lisp casing must be escaped with these curious vertical bars. Intern will perform the appropriate escaping, but raw symbol input is always read by CL as upcased. This may also trip you up in the inverse case of symbol->string conversion– make sure you use symbol-name or string instead of write-to-string!
Fundy Ipsum
Because I’ve made entirely too few websites with the sole purpose of sarcastically mocking universally acceptable targets.


![fiosticket mispy: I have a friend in the US under the Verizon FiOS ISP who has been curiously unable to connect to 106.187.34.177 for some time now. Here's her traceroute: [redacted] deaton: Hello, Thank you for contacting us. Unfortunately this is an issue that we're aware of and is affecting some customers using Verizon. We have been in contact with Verizon and are working to get the issue resolved, however we don't currently have an ETA for resolution. Sorry for any inconvenience this may be causing you in the interim, however we hope to get this problem resolved in short order. Regards, Dave](http://mispy.me/blog/wp-content/uploads/2011/11/fiosticket.png)







