List

My Own Private Hackathon

by Gamefic on Oct 13, 2014

It's been a while since I pulled an all-nighter, but yesterday I tried to work out a minor code puzzle that turned into a deep, dark rabbit hole, and I'm just crawling out of it this afternoon. Let's see if I still have enough wits to unravel everything I did.

I started with the goal of separating Gamefic into two parts: the runtime and the development tools. The game code should be easy to interface with other systems, so I didn't want to bloat the core library with stuff that only matters for building and authoring. The obvious solution was to put all the development stuff into its own subdirectory. Unfortunately, that idea quickly turned into a tangle of thorns, requiring all kinds of warts and bandages to maintain dependencies and stuff. The better solution turned out to be deceiptvely simple: let the runtime and SDK share the same space in the repo, but split them into separate gems in the gemspecs.

Working on the SDK led me to an issue that's been on the table for a long time: cross-platform builds. There are three primary platforms that I'm targeting right now: games that run in the terminal, games that use a web API, and games that work as standalone web apps. Working on that kept me going well into the evening.

Now that I was able to target multiple platforms, I decided to revisit the web-based platforms. I already had an online version, but there was no easy way to customize it. I spent a couple hours tweaking the build process, and now there are three basic variations of the web interface, two of which support in-game graphics. Authors can customize the web assets (HTML, CSS, etc.) and build their changes into the game file that runs on the server.

But what about standalone web apps? A couple weeks ago I started experimenting with Opal. I thought I might be able to use it to compile Javascript versions of games that could run in a browser offline. I had already managed to compile one of the Gamefic examples and execute the introduction, so I decided to see how far I could take it.

Well.

Opal is an impressive piece of work, but it has a few limitations. One minor hurdle was that its strings are immutable. This meant I couldn't use methods that modify the string in place, like gsub! and chomp! An easy problem to fix, just time-consuming. By early this morning I was able to start a game, run the introduction, and send it a command.

The bigger problem was symbols. Opal treats symbols and strings as equivalent. The compiled Javascript will actually return true for "string".kind_of?(Symbol). Unfortunately, my parser differentiated between strings and symbols to determine which parts of commands to treat as plain text and which ones to tokenize. My first hint of the problem was when I entered "look" and the game said, "You talk to yourself." All that work, only to realize that in order to use Opal, I'd have to redesign most of the parser, including a complete rewrite of the Syntax class.

By then it was nearly dawn. I decided to keep messing with it anyway.

After a couple hours of banging my head against the wall, I suddenly realized I had missed something obvious. Instead of building arrays of strings and symbols that get translated into actions, I could feed the Syntax class parameterized strings and let it convert them into regular expressions. The end result was actually easier to use, required less code, and performed at least as well as the kludge that it replaced.

This afternoon I built a Javascript version of a game, ran it offline in my browser, and played it to completion. Twenty-four hours after what was supposed to be a minor refactoring of one small component.

I hope I can get some sleep tonight.

comments powered by Disqus