Ruby? For games? I’ll hang you for that shit. PART 1

Ruby is an amazing language. It is, without a doubt, my favorite scripting language. The first time I heard about it was back in late 2006. I dove right in, and I haven’t looked back. I’ve built myself a pretty decent career on Ruby, and I couldn’t be happier about that.

And now I decided I wanted to fuck around and make games for fun, and later, for profit. Like a true goddamn American.

Ruby isn’t designed for that. Its bread and butter is the command line, and thanks to DHH, the web. There are gems around that’ll help you out, like rubygame, gosu, and releasy. None of those are ideal. I would strongly recommend against wasting your time building games with Ruby. If you want to create a desktop game, learn C++, Java, or Objective-C (and Cocoa) and start playing in hard mode. If browser games are your thing, look into Javascript with HTML5, it’s seriously sexy stuff these days. Creating a game in Ruby is stupid.

With that said, I’m going to show you how to build a game in Ruby.

Let’s build a hangman game!

Alright, enthusiastic me, let’s do it! We’re not going to use any of those gems I pointed out earlier. We’re going old-school with this one and we’re just going to build a command line hangman game.

Let’s start off by outlining the rules of the game as algorithmic as possible:

  1. User is given a phrase to decipher. The phrase is obfuscated by replacing each letter with an underscore.
  2. User guesses, one at a time, a letter believed to match an obfuscated letter in the phrase. The user is allowed 6 incorrect guesses before they lose.
  3. If the user’s guess is correct, replace every instance of the obfuscated letter on the board with the guessed letter. If the guess is incorrect, add the letter to the “used letters” pile and mark off an attempt.
  4. The game ends when all obfuscated letters are guessed, or when the user gets 6 incorrect guesses.

That’s the gist of the game. If you’ve never played hangman, you’re probably from the Third World, and that’s OK. Also, we’re going to go ahead and keep this traditional by drawing a dude getting hanged. We’re not going to pussyfoot around it like teachers do nowadays by drawing an apple tree with some apples and removing one upon each incorrect guess. No, we’re going to murder an ASCII character, so make sure you’re OK with this before continuing.

Getting our assets in order

A lot of people play hangman with a simple word list. I’ve decided to use movie titles, because yes. I went over to IMDB and copied their top 250 movies list, then dumped it in a file which you can find here. I’m aware there are only 246 movies on that list, and I don’t know what happened either. I may have just gotten rid of movies I didn’t like, or maybe it was movies with numbers in them? Whatever, it doesn’t matter, we’ve got 246 movies to work with.

If you don’t want to use movies, use whatever you want. Just make sure each word/phrase is on its own line, and I recommend against numbers since all of that will be stripped out later anyway.

It’s all about phrasing

Ruby is an object-oriented language. If you don’t know what that means, I have no idea why you’re reading this tutorial to begin with. The first thing we should do is set up a Phrase class to manage the phrase the player needs to guess. We’ll initialize it and select a phrase right off the bat.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Phrase
  def initialize
    @letters = []
    selection.each {|l| @letters << l.gsub(/[^a-zA-Z ]/, "")}
  end
 
  def selection
    seed = phrases.length # Get the number of phrases
    # And use that for a random index
    phrases[rand(seed)].split("").map {|x| x.upcase}
  end
 
  def phrases
    lines = []
    file = File.open('phrases.dat', 'r').each {|line| lines << line.chomp }
    file.close
    return lines
  end
end

Let’s go over this.

The selection method simply counts the number of lines in the file (remember, each line is its own word/phrase), creates an array of phrases (lines), transforms them all to uppercase, and selects a random phrase.

The phrases method supplies all of the phrases in the file to the selection method. Could those two methods be condensed into one? Absolutely, but I’m trying to tutorialize you, damn it.

@letters is an array where each letter in the phrase is an element in the array, with everything that isn’t a letter or a space stripped out. If our word were “Sprinkletits!!1”, our array would look like this:

puts @letters # => ["S", "p", "r", "i", "n", "k", "l", "e", "t", "i", "t", "s"]

If you instantiate that class and run your script, you should see something like:

class Phrase
# ... all that code up there
end
 
phrase = Phrase.new
puts phrase.letters # => undefined method `letters' for #<Phrase:0x100169580> (NoMethodError)

Oh. Right. We forgot to define a method for letters.

def letters
  @letters
end
 
# Let's prettyprint the phrase
def show
  @letters.to_s
end

Add that to your class, run the script again, and you should see:

class Phrase
# ... all that code up there
end
 
phrase = Phrase.new
puts phrase.letters # => ["A", "L", "I", "E", "N"]

phrase.show will simply show “ALIEN”. Now we’re getting somewhere.

But I’m tired of writing for today, so I’ll stop right here and continue this another day. I know, it’s a pretty anti-climactic ending.