Wednesday, January 4, 2012

Exercise 41: Gothons From Planet Percal #25: Learn Ruby the Hard Way: Practicum

Happy New Year everyone. Sorry for the delay between sections, but with it being New Years weekend, and activities with my kids, putting away Christmas decorations, and all of the other things related to turning a house upside down after a holiday season, I needed the holiday time. I'm back now, though, and on track to finish these modules out by the end of January if not sooner (good thing, as the end of January is my hard completion deadline for this project :) ).


So back in Exercise 40, we did some playing around with arrays and dictionaries/hashes. Alone with that, we had some additional items added into the mix that, to tell the truth I wasn't 100% sure what was going on. Fortunately, Zed and Rob tell us specifically what is happening here.

Here's the code block that they wanted me to focus on:

cities[:find] = method(:find_city)
puts cities[:find].call(cities, state)

variables can hold lots of values. They can also hold entire blocks of code. To do that, a "proc" (short for procedure) is made. to do that, a built in command called "method" is called (yes, functions and methods are interchangeable, but this is a specific method called "method" that actually does this... now you see why it's been a few days since I've posted ;) ).

The return from "method" is the full proc of "find_city" method (again, we need to pay attention here). That full proc is then stored in the hash called "cities", and uses a key called ":find".

The point here is that, with the "find_city" proc being included in a hash with the key of ":find", we can use that proc by calling on its key.

The 2nd line of code does the following:

Ruby reads the variable "cities" and determines it's a hash/dictionary.

[:find] looks at the "cities" hash/dictionary and evaluates the value of  ":find".

This is the proc "find_city" that we set up using "method". When it sees the method ".call", it calls the proc code.

Since the proc expects parameters, we pass it two parameters, in this case, "cities" and "state". find_city then tries to look up states inside cities.

If it finds a match, it returns it. If it doesn't it returns a message saying it couldn't find a match.

Finally, prints out the value returned by the ":find_city" proc using the puts method.

Did that seem confusing? Yeah, I agree. It's taken me a while to get my head around this, too, and I'm still not entirely sure that I'm 100% there. Zed offers the following trick to help us remember these things. Read the code backwards.

Try this:

* state and city are...
* passed as parameters to...
* a proc at...
* :find inside...
* the hash cities...
* and finally printed on the screen

Here's another way to read it, this time "inside-out".

* Find the center item of the expression, in this case [:find].
* Go counter-clock-wise and you have a hash cities, so this finds the element :find in cities.
* That gives us a proc. Keep going counter-clock-wise and you get to the parameters.
* The parameters are passed to the proc, and that returns a result. Go counter-clock-wise again.
* Finally, we are at the puts statement, and we have our end result.


I have to admit, it's these circular references that kind of drive me crazy (sort of like saying "to understand recursion, you have to first understand recursion"... please don't get me started!).

Zed suggests that reading code and not getting totally lost helps if you can do the three approaches he's described. When reading code, you should read it:

* Front to back.
* Back to front.
* Counter-clock-wise.

So here's the code for today's project. this spans multiple pages, so the screen shots are mostly carved up into their respective methods, give or take a couple. See below:








What You Should See

$ ruby ex41.rb

--------
The Gothons of Planet Percal #25 have invaded your ship and destroyed
your entire crew.  You are the last surviving member and your last
mission is to get the neutron destruct bomb from the Weapons Armory,
put it in the bridge, and blow the ship up after getting into an
escape pod.


You're running down the central corridor to the Weapons Armory when
a Gothon jumps out, red scaly skin, dark grimy teeth, and evil clown costume
flowing around his hate filled body.  He's blocking the door to the
Armory and about to pull a weapon to blast you.
> dodge!
Like a world class boxer you dodge, weave, slip and slide right
as the Gothon's blaster cranks a laser past your head.
In the middle of your artful dodge your foot slips and you
bang your head on the metal wall and pass out.
You wake up shortly after only to die as the Gothon stomps on
your head and eats you.

--------
Such a luser.

$ ruby ex41.rb

--------
The Gothons of Planet Percal #25 have invaded your ship and destroyed
your entire crew.  You are the last surviving member and your last
mission is to get the neutron destruct bomb from the Weapons Armory,
put it in the bridge, and blow the ship up after getting into an
escape pod.


You're running down the central corridor to the Weapons Armory when
a Gothon jumps out, red scaly skin, dark grimy teeth, and evil clown costume
flowing around his hate filled body.  He's blocking the door to the
Armory and about to pull a weapon to blast you.
> tell a joke
Lucky for you they made you learn Gothon insults in the academy.
You tell the one Gothon joke you know:
Lbhe zbgure vf fb sng, jura fur fvgf nebhaq gur ubhfr, fur fvgf nebhaq gur ubhfr.
The Gothon stops, tries not to laugh, then busts out laughing and can't move.
While he's laughing you run up and shoot him square in the head
putting him down, then jump through the Weapon Armory door.

--------
You do a dive roll into the Weapon Armory, crouch and scan the room
for more Gothons that might be hiding.  It's dead quiet, too quiet.
You stand up and run to the far side of the room and find the
neutron bomb in its container.  There's a keypad lock on the box
and you need the code to get the bomb out.  If you get the code
wrong 10 times then the lock closes forever and you can't
get the bomb.  The code is 3 digits.
[keypad]> 123
BZZZZEDDD!
[keypad]> 234
BZZZZEDDD!
[keypad]> 345
BZZZZEDDD!
[keypad]> 456
BZZZZEDDD!
[keypad]> 567
BZZZZEDDD!
[keypad]> 678
BZZZZEDDD!
[keypad]> 789
BZZZZEDDD!
[keypad]> 384
BZZZZEDDD!
[keypad]> 764
BZZZZEDDD!
[keypad]> 354
BZZZZEDDD!
[keypad]> 263
The lock buzzes one last time and then you hear a sickening
melting sound as the mechanism is fused together.
You decide to sit there, and finally the Gothons blow up the
ship from their ship and you die.

--------
You died.  You kinda suck at this.


Extra Credit

Explain how returning the next room works.


[We are effectively using hash keys, and those hash keys are calling on the actual functions as their return values. Each function is being stored as a proc in the ROOMS dictionary/hash table.]

Add cheat codes to the game so you can get past the more difficult rooms.

Instead of having each function print itself, learn about "here document" strings.

Write the room description as here document strings, and change the runner to use them.

[ Here document strings make it easy to put text together, and it's  nice to not have to repeat all of the puts statements. The only thing I don't like about them is that it kinda hurts my sensibility of properly indented code. Still, I guess I can live with it :) ].






TESTHEAD's TAKEAWAYS

Here documentation will take some getting used to, but I like the idea of being able to use a hash to keep the return values in check. It makes it easier to read once you see what it's actually doing, plus it makes it easier to maintain. Also, I need to pick up the pace and get back into a  daily groove where possible (it's amazing what you have to go back and review when you've been away for a few days!).

No comments: