Showing posts with label Learn Ruby the Hard Way. Show all posts
Showing posts with label Learn Ruby the Hard Way. Show all posts

Sunday, December 4, 2011

Making a web game with Ruby

Tonight I sat down to work through a bit more of Learn Ruby the Hard way (seriously, I'm on lesson 47 out of 52, I really should just finish it) but it didn't take long for me to get antsy to work on an actual program. Here's my current idea:

Building on AAN and Skyrim Stories, I want to take the dungeon crawler I made back in the summer, add some "awesome" hand-drawn graphics, and make it playable online. So far I'm picturing this as a Sinatra app, since it's pretty small and I won't need a database, and I can use Yaml for room descriptions. I've thought about ways to display different graphics for rooms when certain events happen, and I need to figure out how to take input from a user when an application is running on the web.

How do you collect information from a user, manipulate it, and trigger other functions based on the input when an application is running on the web? I'm unclear as to what the web equivalent of input = gets.chomp() is.

This project is largely just an excuse to take code I've already written (for the most part, it'll have to be refactored a bit for the web) and get more comfortable with getting a Ruby application to run on the internet. On top of that, I'm big into video games myself, and I'd love to have a hand in making games. Ruby probably isn't the best choice for making a game, but whatever!

Friday, October 28, 2011

Require - No Such File to Load Ruby

The exercise I'm working through in Learn Ruby the Hard Way has me spending a week creating my own game. I've been building various text-based adventure games, but was stuck on what to do next. This morning on the way to work I got the idea of building a MadLib generator.

The way I picture it working is like this: the generator will ask the user to select a Mad Lib from a list, then ask for the requisite input (nouns, adjectives, verbs) depending on the Mad Lib selected and finally spit out the finished product. Simple, right?

So my thinking is this project would be split into two parts: the main program that asks for which Mad Lib the user wants to do, and the Mad Lib that is loaded upon selection. To get this up and running, I decided just to make one Mad Lib work at first, then add support for multiple Mad Libs and the ability to choose between them.

This is just a simple matter of requiring the Mad Lib file from the Main file, but I ran into a small problem.

Using the require 'mermaid' command threw this error:

<internal:lib/rubygems/custom_require&rt;:29:in `require': no such file to load -- mermaid (LoadError)

I wasn't sure why this wasn't working, since I've used require to load separate .rb files before. After a bit of digging around, all I had to to do fix this problem is change require 'mermaid' to require_relative 'mermaid'. This makes sense - both files are in the same directory, so this command is simply saying to require this file located in the same directory as the main file.

On a side note, I'm glad to have fixed the problem, but I'm getting to the point that I think it's better to understand WHY things don't work, rather than finding the solution and moving on. Can anyone tell me why 'require' wasn't working, but 'require_relative' does? Is it an older style of Ruby code that's outdated? Do I need to change some configurations? Is require_relative a workaround? Where does require look for files, if not in the same directory the file that's making that call is located?

Anyway, it felt good to make some progress on this tonight. It's pretty awesome that I can come up with an idea on the way to work, come home and put it together in an hour and a half or so. I think this will be a fun little project. I plan to work on it more this weekend, expanding it, polishing it up and so on. Here's a link to the gist I made of it:

MadLib Generator

Wednesday, October 26, 2011

Initializing Classes in Ruby

I definitely tend to jump around a bit when it comes to learning to code. I was working on Manticore quite a bit, then jumped into Learn Ruby the Hard Way, and now I've hit a bit of a snag so I checked out Code School and the introductory Ruby lesson. (This lesson is based on Why's old lesson, by the way, and is awesome so far) I'm a big fan of Code School so far, because hands-on, practical tutorials have always been one of the best ways for me to learn.

Much like Learn Ruby the Hard Way, it's a lot of review, but I've been interested in seeing what Code School has to offer for a while. Not to mention, switching gears slightly seemed like a better use of my time than sitting around frustrated because I wasn't understanding defining Classes and initializing them properly. I also think the more time I can spend having fun with Ruby, the better. I end up coding longer and enjoying it more, which leads to me learning more and it all kind of snowballs from there until I can build a sort of half-ass snowman with string literals for arms and instance variables for eyes.

Still, Ruby isn't all string methods and arrays, so I'll get back to my problem with Learn Ruby the Hard Way pretty soon. Probably tomorrow!

Here's the basic problem: I have one Class, Game, and the extra credit for this particular exercise is to break the Game into two Classes: Engine and Map.

Here's a link to a gist of what I've got:

Gothons Attack!!

What I'm assuming I need to do is break the rooms a player goes through (methods such as :central_corridor, :bridge_access, etc) into the Map method, and put the code that drives the game into the Engine method. So now I need to learn what initializing a class does, how to do it, how to call a method in one class from another class, and how to make two classes work in concert. For example, Engine will be starting the game (I assume) and moving the player through the rooms that are defined in Map (I also assume) Any input on this problem is, as always, HIGHLY appreciated!

I used to think of learning Ruby as unearthing this huge artifact, but now I think of it as being inside of a mostly darkened room, where I'm stumbling around and looking for switches. Once in a while, I flip a switch and it might illuminate a far off corner, or a whole corridor, or just a tiny little piece of the room. Does that make sense? Or in other words, learning how to do new things is really frustrating right up until it's not, and then it's awesome.

Sunday, October 23, 2011

Here documents in Ruby

I'm still working through Learn Ruby the Hard Way, and the last few exercises have been about building simple text based dungeons. You may recall I spent about a week working on one of my own. This latest exercise introduced me to here documents.

A here document is an easy way to display a long, multiline string. This would have been PERFECT for use in my dungeon adventure. Here's how it works.

This is the original description from the first room in the dungeon game I built:

  puts "This room is dimly lit by two sputtering torches."
  puts "There are three doors leading out of here."
  puts "What do you do?"

You may notice a few "puts" methods. It's not terrible in this case, since I'm putting several small statements, but it's tedious to type and unnecessary to use so many methods when you could do it with one here document. So forget what I said. There's a better, simpler way to do it, so it IS terrible!

So this example can be re-written like this:

  puts <<−TORCH_DESCRIPTION
  This room is dimly lit by two sputtering torches.
  There are three doors leading out of here.
  What do you do?
  TORCH_DESCRIPTION

You begin the here document with <<−TORCH_DESCRIPTION and end it with TORCH_DESCRIPTION. Ruby interprets everything in between as one string. Cleaner, right? Easier to read? Who wouldn't love it!

On a side note, you CAN write this as <<TORCH_DESCRIPTION instead, but by adding the minus sign, you can indent the code as usual. Without the minus sign, the terminator can't be indented, or else Ruby will start complaining about "can't find string TORCH_DESCRIPTION anywhere before EOF".

So I recommend using the minus sign. One more character makes your code a lot easier to read. I also ran into another little problem. Since a here document is a string literal, any variables won't be interpreted correctly. This is where interpolation comes into play. Here's an example from the current exercise I'm working through.

guess = gets.chomp()

if guess != good_pod
  puts "You jump into pod %s and hit the eject button." % guess
  puts "The pod escapes out into the void of space, then"
  puts "implodes as the hull ruptures, crushing your body"
  puts "into jam jelly."
return :death

This bit of code describes an escape pod room, where the user has to pick an escape pod to use. So we set the user's choice as "guess" and then call it in the method below. This is what happens when a user chooses poorly. However, if I were to rewrite the code using a here document, this is what I'd have:

  puts <<-BAD_POD
  "You jump into escape pod %s  and hit the eject button. %guess
  The pod escapes out into the void of space, then
  implodes as the hull ruptures, crushing your body
  into a jelly."
  BAD_POD
return :death

Now the %s and %guess won't be interpreted as variables, since they're inside of a string literal. Luckily, there's an easy solution: interpolation. Just swap out the %s for this little guy:

#{guess}

Ruby will look at this, understand that means you want to use a variable inside of a string literal, and then pull the information it collected when it asked the user which pod he wanted to jump into.

I'm hoping this post will be helpful for others who are grappling with here documents. It took me a bit of digging to figure out how to get the code to index properly (since it looked awful in Textmate, and my solution was to put the string in quotes, which was unnecessary) and I needed to figure out how to call a variable from inside the here document.

Sunday, October 16, 2011

Finished text-based adventure in Ruby

I'm now more or less done with the text dungeon I was building last week. I finished it up last night and fixed some bugs, refactored a bit and made the game more playable today. I also ran the code with -w, which shows a list of warnings. I'm not entirely clear how warnings differ from errors (other than errors stop the code, whereas warnings allow it to continue) but running the code this way allowed me to clean up bad indentation (plenty of it) as well as cleaning up a few other things. I also found out you can indent or unindent (is this a word?) a block of text in TextMate using keyboard shortcuts. Of course, I only found this out after getting frustrated at how long it took to manually indent or unindent a block of code. What's the lesson here? Sometimes it's not about learning new tools, but learning to be more efficient with the tools you already know. 

I've learned a lot with this little project: defining instance variables such as @brass_key, using instance variables to do things within other methods, such as opening doors, creating an inventory for the player that adds or subtracts items as they're found or used, logic to change events if certain instance variables were true and so on and so forth.


Tomorrow I plan to get back to work on Learn Ruby the Hard Way, but this week has definitely been well spent. I may revisit this project in the future and rewrite it to use case / while instead of if / elsif, but I'm ready to work on something new. I spend a lot less time flailing around and feeling stupid now and a lot more time coding or looking for ways to do what I want to do. 


My vocabulary is getting better, too, so it's easier to find information now. And all of this work makes me think about Manticore (don't worry, baby, I haven't forgotten about you!) and how much easier it'll be to redo now. The code I had before I sat down with Eli a few weeks ago took me a couple of months to write, and I'm pretty confident I can get the same functionality with less code in about a week now. But first I need to finish up Learn Ruby the Hard Way. 


And I've been thinking about using what I've learned to write tutorials. I'm sure other new Rubyists will be confused about local and instance variables, or how to write and call methods, and I'd like to try and write something up that will explain these basic ideas in the context of a text-based dungeon crawler. I haven't thought too much about it yet, but it's something I'd like to do in the future.


Here's a link to a gist of the game:

Dungeon of Unfathomable Horror

Tuesday, October 11, 2011

Calling a method in another method in Ruby

I've hit another turning point in my work with Ruby. I've slowly been getting more and more excited about coding, but this week marks the point that sitting down to code seems like something that's fun to do and not just something I kind of enjoy doing that I'm working on because I'm interested in a career change.

I had a really good day with the project I'm currently working on as part of Learn Ruby the Hard Way: a text-based adventure. It's a pretty simple concept, but so far I've got a working four room dungeon. And it's a game! Sure, it's a basic, text based game, not even as complicated as Zork, but it works! Do you know how awesome it feels to build something that works? REALLY AWESOME.

Today at work I was thinking of ways to expand it. How about adding a "help" command to give players some commands that will allow them to manipulate objects in the dungeon? How about a "look" command to remind players which room they're in and to point out important features of that room? How about a "quit" command to let players quit the game?

Done, done and done. Pretty easy: these were all just matters of defining methods and then calling them within the methods I've defined for the various rooms. For example, here's the "help" method I made:

def help()
  puts "Here's a list of handy commands to help you in your quest:"
  puts "Open: open doors, chests, your heart, etc."
  puts "Look: get a description of the room you're in."
  puts "Take: take an item and add it to your inventory."
  puts "Inventory: see what items you're carrying."
  puts "Quit: quit the game, as a coward would."
end

And then I wire it up in each of my room methods, so in the torch_room method, I use this code to allow a player to call up the Help menu:

if next_move == "help"
  prompt; next_move = gets.chomp
end

As I may have mentioned before, I don't have much coding experience with other languages, so I don't know how easy it is to add functionality, but it's really easy with Ruby, and that's part of what I love about it. I'm having a ton of fun with this little project, and while I did a similar project when I was working through Beginning Ruby, those were different times. It was a matter of simply copying code at that point and not understanding what I could do with Ruby and how to make it do what I want it to.

I'm not saying I've mastered Ruby (not by a long shot!) but I'm definitely getting better and it's getting more fun the more I learn. My goal is to keep working on this dungeon for a few more days, expanding the number of rooms, adding monsters and traps, coming up with an inventory system and so on before I move forward with Learn Ruby the Hard Way.

Monday, October 10, 2011

Scope issues with Ruby

I'm still working through Learn Ruby the Hard Way. The exercise I'm working on now has me building a text-adventure dungeon (similar to the one I made when I was going through Beginning Ruby) but this time around I've got a better idea of how to map the project out.

Basically I'm starting with a simple 4 room dungeon. Players will need to go to one room to get a key to unlock a door in the starting room. I understand how to build rooms and allow a player to navigate between them, but managing an inventory and allowing players to access different rooms once they have completed a specific action (such as finding a key to unlock the door) was confusing me.

After asking for some input on Stack Overflow, I think I've got a better handle on the problem now. I'd originally set door_open = false in the start_room method, then once a player had found the key in the chest_room method, I set door_open to true. However, I couldn't call this from the start_room, because it was a local variable. As far as start_room was concerned, door_open was still = false. Am I thinking about this the right way?

I'll simply make key into an instance variable, so it can be accessed by all methods, and edit the logic so while !@key_present, players can open the door. Easy enough! Just wish I could get to coding NOW instead of tonight.

Also, I've been using if/elsif statements, but the same user suggested case/when is cleaner and simpler. I haven't used case/when before, but from the snippet of code he posted, I think I agree. Your thoughts?