Forum: Ruby Text Based RPG in Ruby

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
1d77685e8f8375e5ecb267990d6b97f8?d=identicon&s=25 Ruairidh Mchardy (roo82)
on 2008-11-01 19:39
Attachment: main.rb (1 KB)
Hi all,

I'm currently coding a text-based RPG in Ruby which is great fun but I'm
having some difficulty in structuring it.

So far I have a main.rb file containing a class for the basic enemy and
then an extension for the enemy of the lowest level (Grunt). To test I
created an instance of Grunt and assigned it an hp of 100.
 I then create a var for the assignment of damages (random number
between 1 and 100) and finally test it all out by hard-coding an example
battle. This also works.

My problem is that I feel my code is not clean at all and that the
structure feels inefficient. As I will be extending the game in the
future I want to make sure that every aspect of it is neat and easy to
maintain.

Attached is my script, does anyone have any suggestions?

Thanks!
Aaa2b1f12b65d33422e8cdc48d70c0f9?d=identicon&s=25 Stephen Ball (Guest)
on 2008-11-03 14:59
(Received via mailing list)
On Sat, 01 Nov 2008 14:38:08 -0400, Ruairidh Mchardy
<ruairidh82@gmail.com> wrote:

> battle. This also works.
> Attachments:
> http://www.ruby-forum.com/attachment/2882/main.rb
>

Just some quick notes:
- you should have your enemy class track and assign damage, that way you
can have it test the attack parameters against defense and assign
whatever
damage is actually dealt, rather than working out the math in the main
program
-- e.g. Player.attack(Grunto)
- the enemy class should also be responsible for reporting defeat and
damage
-- e.g. Grunto.health # => "Grunto is undamaged", Grunto.health # =>
"Grunty is wounded but can still fight!"
-- e.g. if Grunto.dead? blah blah
- right now your weapon is just a string identifier, you'll want that to
be a class that defines its damage range and other properties

You've got a fair bit of work to do yet. Locations, movement, inventory,
NPCs, conversations, etc. Good luck!

-- Stephen
1d77685e8f8375e5ecb267990d6b97f8?d=identicon&s=25 Ruairidh Mchardy (roo82)
on 2008-11-03 18:28
Argh ok having problems with this. My code has just become very messy
because I can't get my head clear on this one.

I'm trying to structure the enemy classes as per your advice and so have
Grunty laid out in a manner similar to this:

class Grunty < Enemy
 def initialize(hp,attack, defense, weapon)
    super(hp, attack, defense)
    @weapon = "AK47"
    @hp =100
    if Grunto.hp  == 0
  puts "Grunty is dead!"
else
  puts "Grunty is wounded but can still fight!"
end
randy = rand(100)
health = Grunto.hp - randy
puts "Grunty's hp is #{Health}"
end

This throws up no errors and although the hit assignment is poor, it
remains functional.

I then continue to test with an example scenario battle hard coded into
the script:

Grunty = Grunt.new("hp", 300, 300, "Ak47")
 puts  "Grunty is battling a kitten!"
puts "The kitten attacks Grunty with an AK47 and inflicts #{randy}
points of damage!"
puts "Grunty lost #{randy} health!"
puts "Grunty's hp is #{Health} "
end
end

In theory this should work but get the following errors:

/home/roo/dev/RubyRPG/RubyRPG/lib/main.rb:19:in `initialize':
uninitialized constant Enemy::Grunt::Grunto (NameError)
        from /home/roo/dev/RubyRPG/RubyRPG/lib/main.rb:31:in `new'
        from /home/roo/dev/RubyRPG/RubyRPG/lib/main.rb:31
        from :1
        from :1

I'm not too sure why these errors are being thrown up, I assume it's not
liking the way the class has been edited?

Thanks for all the help so far Stephen, it's helped show me the
direction I should be taking!

Kind Regards,

Ruairidh McHardy

Stephen Ball wrote:
> On Sat, 01 Nov 2008 14:38:08 -0400, Ruairidh Mchardy
> <ruairidh82@gmail.com> wrote:
>
>> battle. This also works.
>> Attachments:
>> http://www.ruby-forum.com/attachment/2882/main.rb
>>
>
> Just some quick notes:
> - you should have your enemy class track and assign damage, that way you
> can have it test the attack parameters against defense and assign
> whatever
> damage is actually dealt, rather than working out the math in the main
> program
> -- e.g. Player.attack(Grunto)
> - the enemy class should also be responsible for reporting defeat and
> damage
> -- e.g. Grunto.health # => "Grunto is undamaged", Grunto.health # =>
> "Grunty is wounded but can still fight!"
> -- e.g. if Grunto.dead? blah blah
> - right now your weapon is just a string identifier, you'll want that to
> be a class that defines its damage range and other properties
>
> You've got a fair bit of work to do yet. Locations, movement, inventory,
> NPCs, conversations, etc. Good luck!
>
> -- Stephen
457cf540784a12ba2f30e06565a2c189?d=identicon&s=25 Hugh Sasse (Guest)
on 2008-11-03 18:52
(Received via mailing list)
On Tue, 4 Nov 2008, Ruairidh Mchardy wrote:

>     @hp =100
> This throws up no errors and although the hit assignment is poor, it
> remains functional.

Excuse me blundering into this thread, but you may find:

http://blog.jayfields.com/2008/07/ruby-dwemthys-ar...

and its associated links interesting reading.

        HTH
        Hugh
1d77685e8f8375e5ecb267990d6b97f8?d=identicon&s=25 Ruairidh Mchardy (roo82)
on 2008-11-03 18:59
Hi there Hugh,

I'd previously taken a look at Dwemthy's array (Poignant Guide to Ruby
right) and it is undoubtedly a nice implementation of a text-based RPG
in Ruby. My problem is that I'm extremely stubborn and even lifting a
line of code makes me feel dirty.

I want this program to be 100% written by me. I know that doesn't stop
me learning from others but I often end up writing code that is far too
similar for my liking!

Also on a slightly embarassing note, my ruby skills are not to the level
where I can easily understand snippets such as:

 define_method( :initialize ) do
      self.class.traits.each do |k,v|
        instance_variable_set("@#{k}", v)

or   def self.metaclass; class << self; self; end; end

  def self.traits( *arr )
    return @traits if arr.empty?

    attr_accessor(*arr)

    arr.each do |a|
      metaclass.instance_eval do
        define_method( a ) do |val|
          @traits ||= {}
          @traits[a] = val

And so my code is far less efficient and rather ugly too :(

Thanks for the link though!

Kind Regards,

Ruairidh McHardy

Hugh Sasse wrote:
> On Tue, 4 Nov 2008, Ruairidh Mchardy wrote:
>
>>     @hp =100
>> This throws up no errors and although the hit assignment is poor, it
>> remains functional.
>
> Excuse me blundering into this thread, but you may find:
>
> http://blog.jayfields.com/2008/07/ruby-dwemthys-ar...
>
> and its associated links interesting reading.
>
>         HTH
>         Hugh
457cf540784a12ba2f30e06565a2c189?d=identicon&s=25 Hugh Sasse (Guest)
on 2008-11-03 19:12
(Received via mailing list)
On Tue, 4 Nov 2008, Ruairidh Mchardy wrote:

> Hi there Hugh,
>
> I'd previously taken a look at Dwemthy's array (Poignant Guide to Ruby
> right) and it is undoubtedly a nice implementation of a text-based RPG
> in Ruby. My problem is that I'm extremely stubborn and even lifting a
> line of code makes me feel dirty.

I know what you mean, but it is how progress is made.
>
> I want this program to be 100% written by me. I know that doesn't stop
> me learning from others but I often end up writing code that is far too
> similar for my liking!

As long as you understand it, I'd say that's OK.  But it's good to try
other approaches as well.
>   def self.traits( *arr )
> And so my code is far less efficient and rather ugly too :(
Exactly why I pointed you at the article about how to achieve this with
modules, because I'm in much the same position.
>
> Thanks for the link though!
>
> Kind Regards,
>
> Ruairidh McHardy
>
        Hugh
A61ecce13ed142622f24a5ca3a123922?d=identicon&s=25 Matthew Moss (Guest)
on 2008-11-03 19:15
(Received via mailing list)
>    end
>    randy = rand(100)
>    health = Grunto.hp - randy
>    puts "Grunty's hp is #{Health}"
> end


First, did you mean Grunto.hp or Grunty.hp?

Second, even if you meant Grunty.hp, I do not think that means what
you think that means.

Third, if you meant @hp instead of Grunty.hp, then why even bother
checking if == 0 since you just assigned 100 to it?
1d77685e8f8375e5ecb267990d6b97f8?d=identicon&s=25 Ruairidh Mchardy (roo82)
on 2008-11-03 19:50
Matthew Moss wrote:
>>    end
>>    randy = rand(100)
>>    health = Grunto.hp - randy
>>    puts "Grunty's hp is #{Health}"
>> end
>
>
> First, did you mean Grunto.hp or Grunty.hp?
>
> Second, even if you meant Grunty.hp, I do not think that means what
> you think that means.
>
> Third, if you meant @hp instead of Grunty.hp, then why even bother
> checking if == 0 since you just assigned 100 to it?

Hi,

Just modified my code a little and now have:

Grunty = Grunt.new(100, 300, 300, "Ak47")
randy = rand(100)
Health = Grunt.hp - randy
 puts  "Grunty is battling a kitten!"
puts "The kitten attacks Grunty with an AK47 and inflicts #{randy}
points of damage!"
puts "Grunty lost #{randy} health!"
puts "Grunty's hp is #{Health} "

So Grunty is my monster and I'm checking the starting vars from the
Grunt class, then subtracting hitpoints from a random value. So perhaps
I should do my checks of if Grunt.hp == 0 then blah in the attack class?

Attached is my (albeit messy) code so far!

Hugh,

Very true in regards to learning from others, I just feel that I
couldn't emulate that coding style as I don't understand it
sufficiently. It's very frustrating!

Thanks for the help guys!

Kind Regards,

Ruairidh McHardy
Aaa2b1f12b65d33422e8cdc48d70c0f9?d=identicon&s=25 Stephen Ball (Guest)
on 2008-11-03 19:53
(Received via mailing list)
On Mon, 03 Nov 2008 12:27:21 -0500, Ruairidh Mchardy
<ruairidh82@gmail.com> wrote:

>     @hp =100
> This throws up no errors and although the hit assignment is poor, it
> remains functional.

The check of Grunto's health shouldn't be at initialization (unless
you'll
be potentially initializing enemies that are already dead). And you
certainly shouldn't be printing from the initialization method.

I rather envisioned Enemy having set methods for:
- affecting health
- returning a health string (e.g. your "Grunty is staggered but still
ready for action." kind of statements)

Something like (and this is really rough)

class Enemy
   def initialize(hp)
     @hp = hp
   end

   def hit(damage)
     @hp -= damage
   end

   def status
     if @hp < 50
       return "He looks really rough."
     else
       return "He is in a fighting mood."
     end
   end
end

grunt = Enemy.new(50)
puts grunt.status
# >> He is in a fighting mood.
grunt.hit(70)
puts grunt.status
# >> He looks really rough.

You'll probably want to track max hit points and current hit points, so
you can set the health statements by percentage thresholds (e.g. current
health is less than 25% of max) in addition or instead of explicit point
limits.

Also you should probably just go ahead and create a player class and get
out of the habit of hard coding things straight into your main program
tests. Ideally you'll want to completely avoid any hardcoding (e.g. how
you hardcode "AK47" in your test attack, that should be something like
#{player.weapon}). You also have "AK47" hardcoded in your Grunt class
instead of using the weapon argument.

Here's an idea of how Player and Enemy could interact.

class Player
   def attack
     # this should be random, and based off of the current weapon
     return 75
   end
end

grunt.hit(player.attack)

> end
>
> I'm not too sure why these errors are being thrown up, I assume it's not
> liking the way the class has been edited?

It looks like your syntax error results from declaring a "Grunty" class,
but then calling Grunt.new, but without seeing the entire code base I
can't be sure.

>
> Thanks for all the help so far Stephen, it's helped show me the
> direction I should be taking!

No problem, always ready to help with games. :-)

-- Stephen
289cf19aa581c445915c072bf45c5e25?d=identicon&s=25 Todd Benson (Guest)
on 2008-11-03 20:04
(Received via mailing list)
On Mon, Nov 3, 2008 at 12:51 PM, Stephen Ball <sdball@gmail.com> wrote:
>>  def initialize(hp,attack, defense, weapon)
>> puts "Grunty's hp is #{Health}"
> - affecting health
>  def hit(damage)
> end
> is less than 25% of max) in addition or instead of explicit point limits.
> class Player
>> the script:
>> In theory this should work but get the following errors:
>
> It looks like your syntax error results from declaring a "Grunty" class, but
> then calling Grunt.new, but without seeing the entire code base I can't be
> sure.
>
>>
>> Thanks for all the help so far Stephen, it's helped show me the
>> direction I should be taking!
>
> No problem, always ready to help with games. :-)

Something to think about for the future, also, is to not immediately
classify things as Enemy objects.  You might want to make it more
generic like Player, or even better, Being (to keep it separate from a
Thing that doesn't have hp).

Todd
457cf540784a12ba2f30e06565a2c189?d=identicon&s=25 Hugh Sasse (Guest)
on 2008-11-03 20:16
(Received via mailing list)
On Tue, 4 Nov 2008, Ruairidh Mchardy wrote:

> Hi,
>
> Just modified my code a little and now have:
>
> Grunty = Grunt.new(100, 300, 300, "Ak47")

  Grunty is a constant. (Capital letter). Probably not what you want.

> randy = rand(100)
> Health = Grunt.hp - randy

  ^^ also a constant.

>  puts  "Grunty is battling a kitten!"

   puts  "#{grunty.name} is battling #{article} #{enemy.name}"

> puts "The kitten attacks Grunty with an AK47 and inflicts #{randy}
> points of damage!"
> puts "Grunty lost #{randy} health!"
> puts "Grunty's hp is #{Health} "
>
> So Grunty is my monster and I'm checking the starting vars from the
> Grunt class, then subtracting hitpoints from a random value. So perhaps
> I should do my checks of if Grunt.hp == 0 then blah in the attack class?

..in the attack *method*. Yes.
>
> Attached is my (albeit messy) code so far!
>
Not sure what happened to that, didn't reach the mailing list.
Well, it didn't reach me anyway.

> Hugh,
>
> Very true in regards to learning from others, I just feel that I
> couldn't emulate that coding style as I don't understand it
> sufficiently. It's very frustrating!

Did you look at the stuff that only uses modules, and no metaclasses
at all?  It takes a bit of getting used to, but I think that code is
easier.  Anyway, maybe you don't need to vary your traits that much
if all characters have @hit_points, @charisma, @damage (weapon power),
@inventory = [], @description, @name.

It can get really complicated simulating all this stuff, especially
if you want to take account of where you are:
"You cannot use a two-handed sword in a narrow passage -- at least,
not without upsetting the landlord."

>
> Thanks for the help guys!
>
> Kind Regards,
>
> Ruairidh McHardy
> --
> Posted via http://www.ruby-forum.com/.
>
        Hugh
1d77685e8f8375e5ecb267990d6b97f8?d=identicon&s=25 Ruairidh Mchardy (roo82)
on 2008-11-03 21:37
Attachment: main.rb (1 KB)
Hugh Sasse wrote:
> On Tue, 4 Nov 2008, Ruairidh Mchardy wrote:
>
>> Hi,
>>
>> Just modified my code a little and now have:
>>
>> Grunty = Grunt.new(100, 300, 300, "Ak47")
>
>   Grunty is a constant. (Capital letter). Probably not what you want.
>
>> randy = rand(100)
>> Health = Grunt.hp - randy
>
>   ^^ also a constant.
>
>>  puts  "Grunty is battling a kitten!"
>
>    puts  "#{grunty.name} is battling #{article} #{enemy.name}"
>
>> puts "The kitten attacks Grunty with an AK47 and inflicts #{randy}
>> points of damage!"
>> puts "Grunty lost #{randy} health!"
>> puts "Grunty's hp is #{Health} "
>>
>> So Grunty is my monster and I'm checking the starting vars from the
>> Grunt class, then subtracting hitpoints from a random value. So perhaps
>> I should do my checks of if Grunt.hp == 0 then blah in the attack class?
>
> ..in the attack *method*. Yes.
>>
>> Attached is my (albeit messy) code so far!
>>
> Not sure what happened to that, didn't reach the mailing list.
> Well, it didn't reach me anyway.
>
>> Hugh,
>>
>> Very true in regards to learning from others, I just feel that I
>> couldn't emulate that coding style as I don't understand it
>> sufficiently. It's very frustrating!
>
> Did you look at the stuff that only uses modules, and no metaclasses
> at all?  It takes a bit of getting used to, but I think that code is
> easier.  Anyway, maybe you don't need to vary your traits that much
> if all characters have @hit_points, @charisma, @damage (weapon power),
> @inventory = [], @description, @name.
>
> It can get really complicated simulating all this stuff, especially
> if you want to take account of where you are:
> "You cannot use a two-handed sword in a narrow passage -- at least,
> not without upsetting the landlord."
>
>>
>> Thanks for the help guys!
>>
>> Kind Regards,
>>
>> Ruairidh McHardy
>> --
>> Posted via http://www.ruby-forum.com/.
>>
>         Hugh

Wow didn't expect quite so many replies! It seems I have a lot to learn
but it's highly enjoyable (after coffee). I realize that my structuring
is definitely rather odd...

I think my problem is that I haven't fully learnt the grammar of Ruby
and am probably suffering from a lack of planning in my game design. I
hadn't really taken into account all the work it requires!

I think I'll be recoding a lot of this project after seeing the replies
as my current code (now attached, forgot last time!) is far too messy.

Thanks guys!

Kind Regards,

Ruairidh Wynne-McHardy
This topic is locked and can not be replied to.