Need Help Debugging Ruby Code

thehobbster

The Kwisatz Haderach
Jul 26, 2010
4,445
94
0
www.tricklecheddar.com
So I've been going through the "Learn Ruby the Hard Way" and I'm essentially done and just putting the finishing touches on my text RPG game they have you make. Mad props to Mattseh for helping me understand key part about passing and receiving variables. But I have a bug that I cannot figure out. Here is the error:

Code:
game.rb:429:  syntax error, unexpected '(', expecting ')'
game.rb:429:  syntax error, unexpected ')', expecting '='

This code is 500 lines, so I'm only to post the relevant part. Below is how I'm running the program to start:

Code:
ROOMS = {
  :death => method(:death),
  :reincarnation => method(:reincarnation(reincarnate, jewel_hallway, jewel_cavern, jewel_stairway)),
  :beginning_room => method(:beginning_room),
  :trap_first => method(:trap_first),
  :hallway_boss => method(:hallway_boss),
  :cavern_room => method(:cavern_room),
  :sanctuary_room => method(:sanctuary_room),
  :priest_room => method(:priest_room),
  :giant_doorway => method(:giant_doorway),
  :money_loop => method(:money_loop),
  :to_the_beast => method(:to_the_beast),
  :dragon_two => method(:dragon_two),
  :dragon_three => method(:dragon_three)
}
    
def runner(map, start)
  next_one = start    
  while true
    room = map[next_one]
    puts "\n--------"
    next_one = room.call()
  end
end
  
reincarnate = false
jewel_hallway = false
jewel_cavern = false
jewel_stairway = false
runner(ROOMS, :reincarnation(reincarnate, jewel_hallway, jewel_cavern, jewel_stairway))

Then it all starts in the "reincarnation" function, which is below:

Code:
def reincarnation(reincarnate, jewel_hallway, jewel_cavern, jewel_stairway)
  if reincarnate == false
    puts "blah"
    return :beginning_room   
  elsif jewel_hallway == true and jewel_cavern == true and jewel_stairway == true
    puts "blah"
    return :giant_doorway    
  else
    puts "blah"
    return :beginning_room
  end
end

I have a feeling that I'm messing up somewhere with passing the variable through the symbol, into the hash, and into the method, and into the function. I'm new so I don't even know if I'm communicating this well. I'm hoping someone can set me straight on this! Haaaalp!

I appreciate it.
 


Or just post it all to https://gist.github.com/ and link us. We can fork it and edit it.

However, I'm not sure what this is:

Code:
:reincarnation(reincarnate, jewel_hallway, jewel_cavern, jewel_stairway)

:reincarnation isn't a method. a :symbol can't be a method. :function("argument1", "argument2") isn't valid Ruby.

I didn't really scrutinize your code, but since you have a `reincarnation` method, looks like you meant to say reincarnation(...) instead of :reincarnation(...).

https://github.com/thehobbster/test/blob/master/game.rb

I'm going to say. I found that to be a royal pain in the ass.

Learning git and pushing to repositories is a necessary skill, so you gained a level doing that. :) But gist is much better for one-off debugging help unless you have a bunch of files.
 
  • Like
Reactions: thehobbster
BTW if you're ever inspecting an array's length in Ruby, you're probably doing too much work.

Code:
puts quips[rand(quips.length())]

can be

Code:
puts quips.sample

Ruby docs are probably my favorite docs of all the programming languages i've played with. if you ever want to look into your options, google "ruby docs <something>" and click the first link.

A good way to get used to Ruby is, whenever you need to do something with a basic datatype like an array, skim the list of methods and look for something that might help you.

Class: Array (Ruby 1.9.3)

Check out Array#sample.
 
Code:
  sanctuary_choice = gets.chomp()  
  if sanctuary_choice == "1"
    puts "You lose all track of time staring lovingly into the eyes of your lord."
    puts "Your eyes glaze over and madness descends upon your soul.  Madness descends"
    puts "upon blore coal.  Madness kepends puffed bomb prole.  Blabber blop roat."
    return :death
  elsif sanctuary_choice == "2"
    puts "You jump up and make it halfway down the aisle before a hoarde of monks descend"
    puts "upon you like lions upon their prey.  Their hoods fall back as they begin gnawing"
    puts "on your flesh!  You can feel the undead virus already coursing through your veins."
    puts "Your body begins hungering for the flesh of the living as your mind goes blank..."
    return :death      
  elsif sanctuary_choice == "3"
    puts "\"Your humility amuses me, child.  Arise before your brethren.\"  You stand and"
    puts "notice on the pulpit a holy book with an \"X\" embossed onto it.  In the middle"
    puts "of this \"X\" is a beautiful, purple colored jewel.  You pretend not to notice."
    return :priest_room      
  else
    puts "Do not disappoint your master!  Choose, lowly dog!"
    return :sanctuary_room
  end

can be

Code:
  # sanctuary_choice = gets.chomp()  
  sanctuary_choice = gets.chomp # don't need () in Ruby
  case sanctuary_choice
  when "1"
    puts %Q[
      You lose all track of time staring lovingly into the eyes of your lord.
      Your eyes glaze over and madness descends upon your soul.  Madness descends
      upon blore coal.  Madness kepends puffed bomb prole.  Blabber blop roat.
    ]
    # return :death
    :death # Ruby implicitly returns the last value expressed in a code path. 
  when "2"
    puts %Q[
      You jump up and make it halfway down the aisle before a hoarde of monks descend
      upon you like lions upon their prey.  Their hoods fall back as they begin gnawing
      on your flesh!  You can feel the undead virus already coursing through your veins.
      Your body begins hungering for the flesh of the living as your mind goes blank...
    ]
    :death      
  when "3"
    ...
    :priest_room      
  else
    puts "Do not disappoint your master!  Choose, lowly dog!"
    :sanctuary_room
  end
 
Ah, I see what you are doing.

Check out this example:

Code:
def hello(msg)
  puts msg
end

method:)hello) returns a Method proc.

To call it, you do method:)hello).call
To pass in params, you do method:)hello).call("whatever")
You're trying to :hello("whatever"). That doesn't work.
 
I like how you changed up the code in your third reply to me. That is much more simple and intuitive. I've just been following some lessons and doing it the way that guy did. I like yours much better.

As far as your last reply, I think I understand, but I'm not sure how to apply that to...
Code:
runner(ROOMS, :reincarnation)
but i think i did it right here...
Code:
ROOMS = {
  :death => method(:death),
  :reincarnation => method(:reincarnation).call(reincarnate, jewel_hallway, jewel_cavern, jewel_stairway),
}

Doing the above reduced my errors down to
Code:
game.rb:429:  undefined local variable or method `reincarnate' for main:Object (NameError)
 
Since reincarnate and jewel_* all try to track "state" -- the state of the game -- I just wrapped all the rooms in a Mapclass and stuck reincarnate and jewel_* in @instance variables.

When a new Map class is initialized before the Runner loop, all the @state variables are set to false. The Runner loop then just calls methods on the class (which return new methods) until the process is killed.

https://github.com/danneu/test/commit/dcf8ca24c0171eac5b07c96bf7aee38022405ff3

example:

Code:
  class Person
    def eat
      #returns what to do next
      :sleep
    end
    def sleep
      :awaken
    end
    def awaken
      :eat
    end
  end

if we have a :symbol of a method name, we can call it on a class with object#send.

person = Person.new
person.send:)eat) is pretty much person.eat

consider:

Code:
person = Person.new
next = person.eat # returns :sleep # to prep the first loop
while true
  next = person.send(next) 
end

iterates through them forever
 
(I can't edit the previous post anymore, but next is a reserved word so that example wouldn't work til you renamed "next" to "do_next").

also, to view the file instead of the diff: https://github.com/danneu/test/blob/dcf8ca24c0171eac5b07c96bf7aee38022405ff3/game.rb

the Map class' `reincarnation` method now doesn't need parameters. it can just inspect the @instance objects for current state which are edited by other methods in the class. Even fundamental object oriented constructs help simplify things. This was an example of loose encapsulation.

Finally, check out http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Literals#The_.25_Notation to see what %Q[...] is and other useful things like how %w[1 2 3 4 5] is shorthand for ["1", "2", "3", "4", "5"]. if you have a lot of gems to require, you can just %w[haml sinatra rack sorcery].map { |gem| require gem }
 
So let me get this straight. The instance variables don't have to be passed to and through the functions. They work for the entire "instance" of the program?

Also, attr_accessor... is that just declaring the variables of the class?

I tried to run the version with the changes you made and it didn't run for me. It had a problem with a gets.chomp() on
Code:
gametest.rb:62: in `beginning_room': private method `chomp' called for nil:NilClass (NoMethodError)

Do you recommend using the % notation? I want to do what the big boys are doing. Do they do the %q{} or the puts "blah"?

By the way, I really appreciate your help with this. I know I'm asking noob questions.
 
So let me get this straight. The instance variables don't have to be passed to and through the functions. They work for the entire "instance" of the program?

Also, attr_accessor... is that just declaring the variables of the class?

I tried to run the version with the changes you made and it didn't run for me. It had a problem with a gets.chomp() on
Code:
gametest.rb:62: in `beginning_room': private method `chomp' called for nil:NilClass (NoMethodError)
Do you recommend using the % notation? I want to do what the big boys are doing. Do they do the %q{} or the puts "blah"?

By the way, I really appreciate your help with this. I know I'm asking noob questions.

I highly recommend this book to learn the intricacies and specifics of all the "magic" in ruby: Amazon.com: Metaprogramming Ruby: Program Like the Ruby Pros (9781934356470): Paolo Perrotta: Books

attr_accesor is the same as writing getter/setter methods. So if you have a class Car:

class Car
@make = "lexus"
end

rather than having to write methods to handle setting and retreiving the make of the car, you can just use attr_accessor and it automatically handles it. So you could then do @instance_of_car_class.make = "Toyota"

There are also attr_reader and attr_writer, which are just the two components of attr_accessor

An instance variable is a variable that exists for the life of an instance of an class (an object). When you create a new instance of a class, like instance_of_car_class = Car.new, there might be a bunch of instance variables in that instance of the Car class that can be used internally in that object (like @make, @model, etc).

Read through this, scope is important in object oriented languages: Ruby Variable Scope - Techotopia

Again, I recommend that book
 
So let me get this straight. The instance variables don't have to be passed to and through the functions. They work for the entire "instance" of the program?

Also, attr_accessor... is that just declaring the variables of the class?

I tried to run the version with the changes you made and it didn't run for me. It had a problem with a gets.chomp() on
Code:
gametest.rb:62: in `beginning_room': private method `chomp' called for nil:NilClass (NoMethodError)

Do you recommend using the % notation? I want to do what the big boys are doing. Do they do the %q{} or the puts "blah"?

By the way, I really appreciate your help with this. I know I'm asking noob questions.

I'm going to echo dchuck because I'm procrastinating this jog I'm about to take. :conehead:

Attr_* Methods

attr_* are convenient short-hand methods.

Code:
class Car
  attr_accessor :driver
  attr_reader :plate_number
  attr_writer :color
end

is the same as

Code:
class Car

  # attr_accessor makes both a getter and setter
  def driver # car.driver
    @driver
  end
  def driver=(value) # car.driver = 'dchuck'
    @driver = value
  end

  # attr_reader only makes a getter
  def plate_number  # car.plate_number
    @plate_number
  end

  # attr_writer only makes a setter
  def color=(value) # car.color = 'blue'
    @color = value
  end
end

It just saves you typing and enhances signal to noise cruft ratio in your object. It's all about what you want your API to look like. Is the @plate_number something you want other objects to be able to set? Does it makes sense for you to ever do dchucks_car.plate_number = HJ8U94?

Not really.

Maybe car.plate_number=(value) shouldn't be part of its API because a car's plate number is set internally, perhaps when it's initialized. Or maybe you create a new car it's assigned from some PlateRegistrar object that queries a website for a new plate number. You'd still want to be able to dchucks_car.plate_number to get the value, but you'd never explicitly assign it.

Code:
class Car
  attr_reader :plate_number
  def initialize
    @plate_number = PlateRegistrar.generate_new_number
  end
end

Instance Variables

You got it. Instance variables are the simplest way an object maintains internal state. They are accessible from all scopes within an object making them work pretty much how you'd expect them to.

Code:
class Person
  attr_reader :name, :sunglasses
  def initialize
    @name = "hobbster"
    @sunglasses = nil
  end
  def coolify!
    @name = "Mr. #{@name.capitalize}"
    @sunglasses = "Ray-Ban 3025s"
  end
end

person = Person.new
person.name #=> hobbster
person.sunglasses #=> nil
person.coolify!
person.name #=> "Mr. Hobbster"
person.sunglasses #=> "Ray-Ban 3025s"

The Big Boys

lol. The more code in the wild you see, the more you'll notice that programmers are lazy.

Code:
food = ["steak", "grapes", "apples", "oranges"] # No thanks!
food = %w[steak grapes apples oranges]
preample = %q[
  We the People of the United States, 
  in Order to form a more perfect Union, 
  establish Justice, insure domestic Tranquility
] 
preample = <<EOS
  We the People of the United States, 
  in Order to form a more perfect Union, 
  establish Justice, insure domestic Tranquility
EOS

Those are common short cuts.

I encourage you to seek out Ruby idioms and emulate what more experienced devs are doing. But I also encourage you to read Ruby's awesome docs to learn new things. Write code and skim the docs for wisdom and better ways of coding.

Ruby is a fun language because it's fun coming up with more clever, more succinct, yet readable ways of simplifying code.

For instance, you may go from:

Code:
def triangle(a, b, c)
  raise TriangleError if a<=0 or b<=0 or c<=0
  raise TriangleError if a+b<=c or b+c<=a or a+c<=b
  return :equilateral if a==b and a==c                 
  return :isosceles if a==b or b==c or a==c
  :scalene                                                                                                       
end

to

Code:
def triangle(a, b, c)
  a, b, c = sides = [a, b, c].sort
  raise TriangleError unless a > 0 and a + b > c
  [:scalene, :isosceles, :equilateral][-sides.uniq.size]
end

Since you're clearly interested in Ruby, check out the resources dchuck mentioned.

I recommend Ruby Koans. Basically, it gives you a bunch of tests that fail and you have to write the Ruby that makes them pass.

I've not read many books, but The Well-Grounded Rubyist is solid.

alright guys can we get a team fistbump?
punch.png
 
punch.png


So I spruced up the game quite a bit and got it running perfectly in Terminal with the code below:

https://gist.github.com/2500159

So now i'm trying to create an application bundle for the Mac OS X and I've managed to do that, but when I run it, I get the following error:

Code:
/Users/*/Desktop/Guardians At The Threshold.app/Contents/Resources/script:98:in `beginning_room': private method `chomp' called for nil:NilClass (NoMethodError)
	from /Users/*/Desktop/Guardians At The Threshold.app/Contents/Resources/script:629:in `send'
	from /Users/*/Desktop/Guardians At The Threshold.app/Contents/Resources/script:629:in `runner'
	from /Users/*/Desktop/Guardians At The Threshold.app/Contents/Resources/script:635

I gots no clue what the heck anymore since I received the awesome help here. I'm finally understanding Class and attr_accessor and all that. But some of this is still shakey for me. Anyone got a clue what's up with this?

Why it would run perfect in Terminal and then not in an application bundle?
 
You're trying to `chomp` the value that `gets` returns (nil). I don't use OS X or know what an .app bundle is, but `gets` isn't hooked up to the right STDIN equipment.

Whatever program you're using to bundle your script, google "<program name> ruby gets nil" or something.

But OS X ships with Ruby. Why not just distribute this as the .rb file and tell them to "ruby programname.rb"?