Misconception about Class instance variables: Ruby Warrior level 4 and 5

I have a confession to make, my code for Ruby Warrior Level 4 and 5 is broken! How did this happen? I made sure all the levels passed! It was through answering someone else’s question in a Lighthouse Labs forum that I realized my own mistake.

Level 4 - my code
Level 4 – my old code

If you look at the above code, you’ll see I set @health in the Player class. Then in the play_turn function, I set @health = warrior.health if @health.nil? returns true. I initially created the variable in the class, thinking it would be passed down into any instances of Player. When the @health variable was nil… I just assigned it the value of warrior.health… that fixed the problem, but why was it nil?
The real problem was that I did not understand what I was doing with the @health variable in the first place. When I set the @health variable in class Player, I incorrectly assumed it would be passed down to warrior (since warrior is-a Player). This is WRONG.
I went back to the documentation for Class.The very first line says, classes are first-class objects in Ruby. Every class is an instance of Class. When a new class is created, such as our class Player, an object of type Class is created and assigned to the global constant, in this case player.
This is very important, because every class is an instance of Class, it is an object. What does that mean? A class, as an instance, has its own instance variables.
If we look at our code, we have a class Player and a warrior object. The warrior object is an instance of the class Player. But the Player class and the warrior object are two different objects and have their own instance variables.

Example of class instance variables
Example of class instance variables

Here’s an example. I create a class Character and give it an instance variable of @name with the value “Disney Name”. I then create a class function that prints the name to the screen. When I call sayname() on the Character class, we get “Disney Name”. Then, I create an instance of Character and assign it to mickey. It should have the same name right? Nope. If we call the sayname() function on mickey, we see that the @name variable is nil.

This is exactly how I went wrong in Ruby Warrior. I set @health in the class Player, and then wrongly assumed that the warrior would have a @health variable. Class instance variables are specific to that instance!
In the first line of play_turn(), @health is nil for warrior because it was never created. If we want each instance of class Player to have a @health instance variable, we have to put that in the initialize function. Then, every time a new instance of Player is created, that instance will have its own @health instance variable.

instance variables in the initialize function
instance variables in the initialize function

I fixed my code for Ruby Warrior Level 4 as below:
I set @health to 0, so that under_attack will return false in the very first turn. (Because warrior.health will not be less than 0 in the first turn.) Then at the end of the each turn, I update @health to warrior.health for comparison in the next turn.

Ruby Warrior Level 4
Ruby Warrior Level 4

I also moved the min_health variable from the class to the should_walk? function. When I tried to run the original code again, it gave me an undefined variable error for line 24. This makes sense because the function could not access the min_health variable. I did not put min_health as an instance variable because it is a constant and would not vary between different instances.
For Level 5, captives are added. If the space before the warrior is not empty, we want to check whether it’s a captive. We’ll rescue if it’s a captive, otherwise we attack!

Level 5: added line
Level 5: modified 14-18.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s