ROFLBALT

Glenn Goodrich
Ruby Editor

Today, we’re going to dissect a project that a couple of Rubyists we are fond of wrote for RailsCamp last month. Paul Annesley (twitter) and Dennis Hotson (twitter) joined forces at RailsCamp Australia and the result was ASCII-based fireworks.

First, though, if you are unfamiliar with RailsCamps, here is a blurb from their site:

Imagine yourself and a posse of like-minded ruby hackers on a country retreat with zero internet for a weekend of fun. You’ll laugh, hack, learn, cry (well, you probably won’t cry… but you know… it felt poetic) and most likely play a crap-load of Guitar Hero.

The point of a RailsCamp, from what I can tell, is to disconnect with everything except Ruby and the other attendees. It is a programming fest where they encourage hackery and creativity, two things that live in abundance in the Ruby community. Everyone that I know who has attended a RailsCamp has come back saying it was a gamechanger for their career. I need to get one scheduled near Charlotte.

The Project

The project that Dennis and Paul settled on was entitled ROFLBALT. It is a Ruby port of the somewhat famous Canabalt game that you can play online or download to your mobile device. The goal of Canabalt (and all -balt type games) is to run and jump from building to building, going for as long as you can. Your score is directly proportional to how long you go before you die. It’s just a bit of brain candy in simple gaming form, the kind of candy my brain likes best.

Paul and Dennis aimed to complete ROFLBALT using less than 500 lines of Ruby, and they pulled it off. Let’s see how.

How They Did It

I am going to attempt to breakdown the code for ROFLBALT. I am warning you now that Paul and Dennis are, seemingly and unsurprisingly, much smarter than I am. In some parts of this code, I had no idea how they 1) came up with what they did or 2) what the heck the code does. This is, in no way, a reflection on the code or project, but more on the author, who is, um, “special.”

You can find the code for the project on github.

ROFLBALT is broken into the following classes.

  • Game
  • Screen
  • Pixel
  • BAckground
  • WindowColor
  • FrameBuffer
  • World
  • BuildingGenerator
  • (module)Renderable
  • Building
  • Player
  • Blood
  • Scoreboard
  • GameOverBanner
  • RoflCoptor

The executable (in the bin dir) simple runs Game.new.run, so we’ll start there.

Game


Game, as you may have guessed, puts all the bits in place to start the game, as well as handles exiting of the game. It depends on a World and a Screen. In the Game methods, things are pretty high-level, as we’re dealing with a World object and Screen object, both abstractions created by this project. The game starts the fun, basically, in the render method. It draws the buildings, the player, and the rest of the world objects. Note that “drawing” an object means passing it to the screen’s draw method, something we’ll cover when we get to Screen. Other than that, the Game listens for a signal interrupt (Ctrl-C in this case) and kills the game when that happens.

World


World holds all of the renderable items that the game can contain. The Buildings, the background, scoreboard, and a player. it also has a couple of non-visible items, like a Building Generator and a horizon (screen width). The world has a lot of dependencies:

  • BuildingGenerator
  • Background
  • Player
  • Building
  • Scoreboard

The world also controls the speed of the game and the distance travelled, which is how scoring is calculated. The tick method is each “move” of the world. As the player moves right (or, more appropriately, the buildings move left) the tick method checks to make sure that a building is still under the player, otherwise it’s time to RENDER BLOOD and call PLAYER.DIE (Sorry, just feels like I should capitalize those) If you aren’t dead, it adds one to ticks

Screen


Screen is initialized with the width and height of our game “canvas”, along with the game world. If you look at the initialize method, it does some funky stty magic to clear the screen and disable the cursor. It’s things like this that make me love reading other people’s code. I always see and learn things that I would miss in my everyday coding routine. The Screen is responsible for rendering the initial game “canvas” and drawing Renderables onto the canvas. Renderable are classes that include the Renderable module, which we will get to soon.

Pixel


The Pixel class represents a game pixel, which is an ASCII character in ROLFBALT. A Pixel instance consists of a foreground color, a background color, and a character. The cool thing I learned from the Pixel class is how to mess with my terminal colors. The to_s method shows this, using a fancy string interpolation method (http://apidock.com/ruby/String/%25). I was switching my terminal from black-on-white to white-on-black and giggling when my wife walked by and asked what I was giggling about. All I could say was “um, nerd stuff” and realize that it’s situations like this that separate us from the “normals.” Anyway, I digress… back to the code…

Background


Background takes a world as a dependency and is mainly responsible for returning the color behind everything else. For example, when the player is rendered, it passes backround.color() as the background color to the players Pixels. Nice little, focused class. I like it.

WindowColor


The color of the windows on the buildings. BuildingGenerator uses it. That is all

Framebuffer


Framebuffer is instantiated by the Screen class and takes a background. The initialize method reminded me of the cool way to pass a block to the Hash intializer to get a hash that has a default value for any member that is accessed. Rendered pixels are fed into the framebuffer, and then other things (like the screen) can grab them later. It also uses Background, passing back background pixels as a default when the Framebuffer does not have a pixel for a requested x,y.

BuildingGenerator


BuildingGenerator takes a World and a Background and, as you may have guessed, it creates the buildings. The methods here focus on destroying building objects (by taking them out of the world.buildings property) and creating builidings as the world turns moves. The generate_if_necessary method is a loop that keeps creating builidings while the last building’s x coord is less than the worlds Horizon (which is the width of the screen, if you remember.) It calls the build method which builds the building. The next_y function takes the previous building into account and makes sure the new building isn’t too high for our player to jump. This is the kind of domain problem you have to solve when creating a video game. :)

Renderable


This brings us to the Renderable module, which is included in:

  • Building
  • Player
  • Blood (hee)
  • Scoreboard
  • GameOverBanner
  • RoflCopter

It defines two methods: each_pixel and right_x. Also, it expects the included class to define a pixel method, along with x, y, height, and width properties (or methods.) In a nutshell, the each_pixel method loops over every pixel for the object, and returns the x, y of that pixel, along with the character that represents that pixel for the given Renderable. In a way, it works just like your TV, scanning through the images and rendering them, pixel by pixel. I like how Renderable delegates the pixel method, making a clear contract with things that include Renderable as well as things that use Renderables.

Building


Looking at our first Renderable, Building, it takes an x,y, which is the left most x and the highest y, a width, and a background object. Moving it left is as simple as decrementing its x property. The complexity happens in the pixel method expected by Renderable. The goal is to figure out, for the given x/y, what character and color to render. Looking at the start of the if statement, if the distance between the y of the current pixel and the y of the building is zero, then we are at the top of the building, so draw a “=”. The rest of the logic is the same, figuring out which character and color to return. Pretty smart.

Player


Player is another renderable, but it handles what character to draw differently than building. Looking at the code, it uses the delta in between the current x,y and the player x,y to figure out which character to return based on one of three states: dead, walking, or other (jumping). Each state is made up of a two-dimensional array that holds the actual characters, which you can literally see. The code here is very clever in its structure, forming the player in the state, then using the rx/ry indices to grab the character. Pretty ingenious.

Blood


The Blood class always uses the same character and color, but I had to give it its own section. I just LOVE that there is a Blood class.

Scoreboard and GameOverBanner


Both of these renderables use a template method to find the character to render. Again, the structure of the code is awesome, as it reads like it looks in the game. This makes it easy to figure out what the code is doing. If only you could make all code this visually obvious.

RoflCoptor


The coup de grace, our ROFLCopter. The approach here is, again, similar to the other template-based renderables, but here we have multiple templates or frames. The x and y of the coptor change based on current time, using a formula I don’t really understand. I think my favorite bit is the rescue, where it claims that “RoflCopter” crashes from time to time.

Time to Play

Well, Paul and Dennis pulled it off. A port of Canabalt to Ruby that is highly playable, as well as being comprised of interesting code. When I asked the gents for any anecdotes about it, they came back with “Ask the readers to make it work in Ruby 1.8 as a challenge.” So, anyone up for the that? In the meantime, I am going to try and beat my record in ROFLBALT.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://marketingpartners.com.au Kristen

    Nice to see some familiar faces posting some very cool code. Dennis, you always did love your visualisations :) I guess you guys had to something, inbetween getting drunk, eh? :P

  • http://dhotson.tumblr.com Dennis Hotson

    Thanks Kristen. It was a lot of fun to make. :-)

    Also, in case anyone couldn’t get it running properly—I made a quick video demo: http://www.youtube.com/watch?v=VoHmJfXqwbM

  • http://loudcoding.com Paulo Casaretto

    About Framebuffer#initialize

    Wouldn’t @pixels = Hash.new({}) suffice here?